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