1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/pepper/pepper_hung_plugin_filter.h" 6 7 #include "base/bind.h" 8 #include "content/child/child_process.h" 9 #include "content/common/frame_messages.h" 10 #include "content/renderer/render_thread_impl.h" 11 12 namespace content { 13 14 namespace { 15 16 // We'll consider the plugin hung after not hearing anything for this long. 17 const int kHungThresholdSec = 10; 18 19 // If we ever are blocked for this long, we'll consider the plugin hung, even 20 // if we continue to get messages (which is why the above hung threshold never 21 // kicked in). Maybe the plugin is spamming us with events and never unblocking 22 // and never processing our sync message. 23 const int kBlockedHardThresholdSec = kHungThresholdSec * 1.5; 24 25 } // namespace 26 27 PepperHungPluginFilter::PepperHungPluginFilter( 28 const base::FilePath& plugin_path, 29 int frame_routing_id, 30 int plugin_child_id) 31 : plugin_path_(plugin_path), 32 frame_routing_id_(frame_routing_id), 33 plugin_child_id_(plugin_child_id), 34 filter_(RenderThread::Get()->GetSyncMessageFilter()), 35 io_loop_(ChildProcess::current()->io_message_loop_proxy()), 36 pending_sync_message_count_(0), 37 hung_plugin_showing_(false), 38 timer_task_pending_(false) { 39 } 40 41 void PepperHungPluginFilter::BeginBlockOnSyncMessage() { 42 base::AutoLock lock(lock_); 43 last_message_received_ = base::TimeTicks::Now(); 44 if (pending_sync_message_count_ == 0) 45 began_blocking_time_ = last_message_received_; 46 pending_sync_message_count_++; 47 48 EnsureTimerScheduled(); 49 } 50 51 void PepperHungPluginFilter::EndBlockOnSyncMessage() { 52 base::AutoLock lock(lock_); 53 pending_sync_message_count_--; 54 DCHECK(pending_sync_message_count_ >= 0); 55 56 MayHaveBecomeUnhung(); 57 } 58 59 void PepperHungPluginFilter::OnFilterAdded(IPC::Channel* channel) { 60 } 61 62 void PepperHungPluginFilter::OnFilterRemoved() { 63 base::AutoLock lock(lock_); 64 MayHaveBecomeUnhung(); 65 } 66 67 void PepperHungPluginFilter::OnChannelError() { 68 base::AutoLock lock(lock_); 69 MayHaveBecomeUnhung(); 70 } 71 72 bool PepperHungPluginFilter::OnMessageReceived(const IPC::Message& message) { 73 // Just track incoming message times but don't handle any messages. 74 base::AutoLock lock(lock_); 75 last_message_received_ = base::TimeTicks::Now(); 76 MayHaveBecomeUnhung(); 77 return false; 78 } 79 80 PepperHungPluginFilter::~PepperHungPluginFilter() {} 81 82 void PepperHungPluginFilter::EnsureTimerScheduled() { 83 lock_.AssertAcquired(); 84 if (timer_task_pending_) 85 return; 86 87 timer_task_pending_ = true; 88 io_loop_->PostDelayedTask(FROM_HERE, 89 base::Bind(&PepperHungPluginFilter::OnHangTimer, this), 90 base::TimeDelta::FromSeconds(kHungThresholdSec)); 91 } 92 93 void PepperHungPluginFilter::MayHaveBecomeUnhung() { 94 lock_.AssertAcquired(); 95 if (!hung_plugin_showing_ || IsHung()) 96 return; 97 98 SendHungMessage(false); 99 hung_plugin_showing_ = false; 100 } 101 102 base::TimeTicks PepperHungPluginFilter::GetHungTime() const { 103 lock_.AssertAcquired(); 104 105 DCHECK(pending_sync_message_count_); 106 DCHECK(!began_blocking_time_.is_null()); 107 DCHECK(!last_message_received_.is_null()); 108 109 // Always considered hung at the hard threshold. 110 base::TimeTicks hard_time = began_blocking_time_ + 111 base::TimeDelta::FromSeconds(kBlockedHardThresholdSec); 112 113 // Hung after a soft threshold from last message of any sort. 114 base::TimeTicks soft_time = last_message_received_ + 115 base::TimeDelta::FromSeconds(kHungThresholdSec); 116 117 return std::min(soft_time, hard_time); 118 } 119 120 bool PepperHungPluginFilter::IsHung() const { 121 lock_.AssertAcquired(); 122 123 if (!pending_sync_message_count_) 124 return false; // Not blocked on a sync message. 125 126 return base::TimeTicks::Now() > GetHungTime(); 127 } 128 129 void PepperHungPluginFilter::OnHangTimer() { 130 base::AutoLock lock(lock_); 131 timer_task_pending_ = false; 132 133 if (!pending_sync_message_count_) 134 return; // Not blocked any longer. 135 136 base::TimeDelta delay = GetHungTime() - base::TimeTicks::Now(); 137 if (delay > base::TimeDelta()) { 138 // Got a timer message while we're waiting on a sync message. We need 139 // to schedule another timer message because the latest sync message 140 // would not have scheduled one (we only have one out-standing timer at 141 // a time). 142 timer_task_pending_ = true; 143 io_loop_->PostDelayedTask(FROM_HERE, 144 base::Bind(&PepperHungPluginFilter::OnHangTimer, this), 145 delay); 146 return; 147 } 148 149 hung_plugin_showing_ = true; 150 SendHungMessage(true); 151 } 152 153 void PepperHungPluginFilter::SendHungMessage(bool is_hung) { 154 filter_->Send(new FrameHostMsg_PepperPluginHung( 155 frame_routing_id_, plugin_child_id_, plugin_path_, is_hung)); 156 } 157 158 } // namespace content 159