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#ifndef CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
6#define CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
7
8#include "base/files/file_path.h"
9#include "base/memory/ref_counted.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/synchronization/lock.h"
12#include "ipc/ipc_channel_proxy.h"
13#include "ipc/ipc_sync_message_filter.h"
14#include "ppapi/proxy/host_dispatcher.h"
15
16namespace content {
17
18// This class monitors a renderer <-> pepper plugin channel on the I/O thread
19// of the renderer for a hung plugin.
20//
21// If the plugin is not responding to sync messages, it will notify the browser
22// process and give the user the option to kill the hung plugin.
23//
24// Note that this class must be threadsafe since it will get the begin/end
25// block notifications on the main thread, but the filter is run on the I/O
26// thread. This is important since when we're blocked on a sync message to a
27// hung plugin, the main thread is frozen.
28//
29// NOTE: This class is refcounted (via IPC::MessageFilter).
30class PepperHungPluginFilter
31    : public ppapi::proxy::HostDispatcher::SyncMessageStatusObserver,
32      public IPC::MessageFilter {
33 public:
34  // The |frame_routing_id| is the ID of the render_frame so that this class can
35  // send messages to the browser via that frame's route. The |plugin_child_id|
36  // is the ID in the browser process of the pepper plugin process host. We use
37  // this to identify the proper plugin process to terminate.
38  PepperHungPluginFilter(const base::FilePath& plugin_path,
39                         int frame_routing_id,
40                         int plugin_child_id);
41
42  // SyncMessageStatusReceiver implementation.
43  virtual void BeginBlockOnSyncMessage() OVERRIDE;
44  virtual void EndBlockOnSyncMessage() OVERRIDE;
45
46  // MessageFilter implementation.
47  virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE;
48  virtual void OnFilterRemoved() OVERRIDE;
49  virtual void OnChannelError() OVERRIDE;
50  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
51
52 protected:
53  virtual ~PepperHungPluginFilter();
54
55 private:
56  // Makes sure that the hung timer is scheduled.
57  void EnsureTimerScheduled();
58
59  // Checks whether the plugin could have transitioned from hung to unhung and
60  // notifies the browser if so.
61  void MayHaveBecomeUnhung();
62
63  // Calculate the point at which the plugin could next be considered hung.
64  base::TimeTicks GetHungTime() const;
65
66  // Checks if the plugin is considered hung based on whether it has been
67  // blocked for long enough.
68  bool IsHung() const;
69
70  // Timer handler that checks for a hang after a timeout.
71  void OnHangTimer();
72
73  // Sends the hung/unhung message to the browser process.
74  void SendHungMessage(bool is_hung);
75
76  base::Lock lock_;
77
78  base::FilePath plugin_path_;
79  int frame_routing_id_;
80  int plugin_child_id_;
81
82  // Used to post messages to the renderer <-> browser message channel from
83  // other threads without having to worry about the lifetime of that object.
84  // We don't actually use the "sync" feature of this filter.
85  scoped_refptr<IPC::SyncMessageFilter> filter_;
86
87  scoped_refptr<base::MessageLoopProxy> io_loop_;
88
89  // The time when we start being blocked on a sync message. If there are
90  // nested ones, this is the time of the outermost one.
91  //
92  // This will be is_null() if we've never blocked.
93  base::TimeTicks began_blocking_time_;
94
95  // Time that the last message was received from the plugin.
96  //
97  // This will be is_null() if we've never received any messages.
98  base::TimeTicks last_message_received_;
99
100  // Number of nested sync messages that we're blocked on.
101  int pending_sync_message_count_;
102
103  // True when we've sent the "plugin is hung" message to the browser. We track
104  // this so we know to look for it becoming unhung and send the corresponding
105  // message to the browser.
106  bool hung_plugin_showing_;
107
108  bool timer_task_pending_;
109
110  DISALLOW_COPY_AND_ASSIGN(PepperHungPluginFilter);
111};
112
113}  // namespace content
114
115#endif  // CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
116