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 void PepperHungPluginFilter::BeginBlockOnSyncMessage() { 41 base::AutoLock lock(lock_); 42 last_message_received_ = base::TimeTicks::Now(); 43 if (pending_sync_message_count_ == 0) 44 began_blocking_time_ = last_message_received_; 45 pending_sync_message_count_++; 46 47 EnsureTimerScheduled(); 48 } 49 50 void PepperHungPluginFilter::EndBlockOnSyncMessage() { 51 base::AutoLock lock(lock_); 52 pending_sync_message_count_--; 53 DCHECK(pending_sync_message_count_ >= 0); 54 55 MayHaveBecomeUnhung(); 56 } 57 58 void PepperHungPluginFilter::OnFilterAdded(IPC::Sender* sender) {} 59 60 void PepperHungPluginFilter::OnFilterRemoved() { 61 base::AutoLock lock(lock_); 62 MayHaveBecomeUnhung(); 63 } 64 65 void PepperHungPluginFilter::OnChannelError() { 66 base::AutoLock lock(lock_); 67 MayHaveBecomeUnhung(); 68 } 69 70 bool PepperHungPluginFilter::OnMessageReceived(const IPC::Message& message) { 71 // Just track incoming message times but don't handle any messages. 72 base::AutoLock lock(lock_); 73 last_message_received_ = base::TimeTicks::Now(); 74 MayHaveBecomeUnhung(); 75 return false; 76 } 77 78 PepperHungPluginFilter::~PepperHungPluginFilter() {} 79 80 void PepperHungPluginFilter::EnsureTimerScheduled() { 81 lock_.AssertAcquired(); 82 if (timer_task_pending_) 83 return; 84 85 timer_task_pending_ = true; 86 io_loop_->PostDelayedTask( 87 FROM_HERE, 88 base::Bind(&PepperHungPluginFilter::OnHangTimer, this), 89 base::TimeDelta::FromSeconds(kHungThresholdSec)); 90 } 91 92 void PepperHungPluginFilter::MayHaveBecomeUnhung() { 93 lock_.AssertAcquired(); 94 if (!hung_plugin_showing_ || IsHung()) 95 return; 96 97 SendHungMessage(false); 98 hung_plugin_showing_ = false; 99 } 100 101 base::TimeTicks PepperHungPluginFilter::GetHungTime() const { 102 lock_.AssertAcquired(); 103 104 DCHECK(pending_sync_message_count_); 105 DCHECK(!began_blocking_time_.is_null()); 106 DCHECK(!last_message_received_.is_null()); 107 108 // Always considered hung at the hard threshold. 109 base::TimeTicks hard_time = 110 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 = 115 last_message_received_ + 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( 144 FROM_HERE, 145 base::Bind(&PepperHungPluginFilter::OnHangTimer, this), 146 delay); 147 return; 148 } 149 150 hung_plugin_showing_ = true; 151 SendHungMessage(true); 152 } 153 154 void PepperHungPluginFilter::SendHungMessage(bool is_hung) { 155 filter_->Send(new FrameHostMsg_PepperPluginHung( 156 frame_routing_id_, plugin_child_id_, plugin_path_, is_hung)); 157 } 158 159 } // namespace content 160