Home | History | Annotate | Download | only in pepper
      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