| // 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 |