// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/pepper/pepper_hung_plugin_filter.h" #include "base/bind.h" #include "content/child/child_process.h" #include "content/common/frame_messages.h" #include "content/renderer/render_thread_impl.h" namespace content { namespace { // We'll consider the plugin hung after not hearing anything for this long. const int kHungThresholdSec = 10; // If we ever are blocked for this long, we'll consider the plugin hung, even // if we continue to get messages (which is why the above hung threshold never // kicked in). Maybe the plugin is spamming us with events and never unblocking // and never processing our sync message. const int kBlockedHardThresholdSec = kHungThresholdSec * 1.5; } // namespace PepperHungPluginFilter::PepperHungPluginFilter( const base::FilePath& plugin_path, int frame_routing_id, int plugin_child_id) : plugin_path_(plugin_path), frame_routing_id_(frame_routing_id), plugin_child_id_(plugin_child_id), filter_(RenderThread::Get()->GetSyncMessageFilter()), io_task_runner_(ChildProcess::current()->io_task_runner()), pending_sync_message_count_(0), hung_plugin_showing_(false), timer_task_pending_(false) { } void PepperHungPluginFilter::BeginBlockOnSyncMessage() { base::AutoLock lock(lock_); last_message_received_ = base::TimeTicks::Now(); if (pending_sync_message_count_ == 0) began_blocking_time_ = last_message_received_; pending_sync_message_count_++; EnsureTimerScheduled(); } void PepperHungPluginFilter::EndBlockOnSyncMessage() { base::AutoLock lock(lock_); pending_sync_message_count_--; DCHECK(pending_sync_message_count_ >= 0); MayHaveBecomeUnhung(); } void PepperHungPluginFilter::OnFilterRemoved() { base::AutoLock lock(lock_); MayHaveBecomeUnhung(); } void PepperHungPluginFilter::OnChannelError() { base::AutoLock lock(lock_); MayHaveBecomeUnhung(); } bool PepperHungPluginFilter::OnMessageReceived(const IPC::Message& message) { // Just track incoming message times but don't handle any messages. base::AutoLock lock(lock_); last_message_received_ = base::TimeTicks::Now(); MayHaveBecomeUnhung(); return false; } PepperHungPluginFilter::~PepperHungPluginFilter() {} void PepperHungPluginFilter::EnsureTimerScheduled() { lock_.AssertAcquired(); if (timer_task_pending_) return; timer_task_pending_ = true; io_task_runner_->PostDelayedTask( FROM_HERE, base::BindOnce(&PepperHungPluginFilter::OnHangTimer, this), base::TimeDelta::FromSeconds(kHungThresholdSec)); } void PepperHungPluginFilter::MayHaveBecomeUnhung() { lock_.AssertAcquired(); if (!hung_plugin_showing_ || IsHung()) return; SendHungMessage(false); hung_plugin_showing_ = false; } base::TimeTicks PepperHungPluginFilter::GetHungTime() const { lock_.AssertAcquired(); DCHECK(pending_sync_message_count_); DCHECK(!began_blocking_time_.is_null()); DCHECK(!last_message_received_.is_null()); // Always considered hung at the hard threshold. base::TimeTicks hard_time = began_blocking_time_ + base::TimeDelta::FromSeconds(kBlockedHardThresholdSec); // Hung after a soft threshold from last message of any sort. base::TimeTicks soft_time = last_message_received_ + base::TimeDelta::FromSeconds(kHungThresholdSec); return std::min(soft_time, hard_time); } bool PepperHungPluginFilter::IsHung() const { lock_.AssertAcquired(); if (!pending_sync_message_count_) return false; // Not blocked on a sync message. return base::TimeTicks::Now() > GetHungTime(); } void PepperHungPluginFilter::OnHangTimer() { base::AutoLock lock(lock_); timer_task_pending_ = false; if (!pending_sync_message_count_) return; // Not blocked any longer. base::TimeDelta delay = GetHungTime() - base::TimeTicks::Now(); if (delay > base::TimeDelta()) { // Got a timer message while we're waiting on a sync message. We need // to schedule another timer message because the latest sync message // would not have scheduled one (we only have one out-standing timer at // a time). timer_task_pending_ = true; io_task_runner_->PostDelayedTask( FROM_HERE, base::BindOnce(&PepperHungPluginFilter::OnHangTimer, this), delay); return; } hung_plugin_showing_ = true; SendHungMessage(true); } void PepperHungPluginFilter::SendHungMessage(bool is_hung) { filter_->Send(new FrameHostMsg_PepperPluginHung( frame_routing_id_, plugin_child_id_, plugin_path_, is_hung)); } } // namespace content