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 SyncMessageStatusReceiver).
30class PepperHungPluginFilter
31    : public ppapi::proxy::HostDispatcher::SyncMessageStatusReceiver {
32 public:
33  // The |view_routing_id| is the ID of the render_view so that this class can
34  // send messages to the browser via that view's route. The |plugin_child_id|
35  // is the ID in the browser process of the pepper plugin process host. We use
36  // this to identify the proper plugin process to terminate.
37  PepperHungPluginFilter(const base::FilePath& plugin_path,
38                         int view_routing_id,
39                         int plugin_child_id);
40
41  // SyncMessageStatusReceiver implementation.
42  virtual void BeginBlockOnSyncMessage() OVERRIDE;
43  virtual void EndBlockOnSyncMessage() OVERRIDE;
44
45  // MessageFilter implementation.
46  virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
47  virtual void OnFilterRemoved() OVERRIDE;
48  virtual void OnChannelError() OVERRIDE;
49  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
50
51 protected:
52  virtual ~PepperHungPluginFilter();
53
54 private:
55  // Makes sure that the hung timer is scheduled.
56  void EnsureTimerScheduled();
57
58  // Checks whether the plugin could have transitioned from hung to unhung and
59  // notifies the browser if so.
60  void MayHaveBecomeUnhung();
61
62  // Calculate the point at which the plugin could next be considered hung.
63  base::TimeTicks GetHungTime() const;
64
65  // Checks if the plugin is considered hung based on whether it has been
66  // blocked for long enough.
67  bool IsHung() const;
68
69  // Timer handler that checks for a hang after a timeout.
70  void OnHangTimer();
71
72  // Sends the hung/unhung message to the browser process.
73  void SendHungMessage(bool is_hung);
74
75  base::Lock lock_;
76
77  base::FilePath plugin_path_;
78  int view_routing_id_;
79  int plugin_child_id_;
80
81  // Used to post messages to the renderer <-> browser message channel from
82  // other threads without having to worry about the lifetime of that object.
83  // We don't actually use the "sync" feature of this filter.
84  scoped_refptr<IPC::SyncMessageFilter> filter_;
85
86  scoped_refptr<base::MessageLoopProxy> io_loop_;
87
88  // The time when we start being blocked on a sync message. If there are
89  // nested ones, this is the time of the outermost one.
90  //
91  // This will be is_null() if we've never blocked.
92  base::TimeTicks began_blocking_time_;
93
94  // Time that the last message was received from the plugin.
95  //
96  // This will be is_null() if we've never received any messages.
97  base::TimeTicks last_message_received_;
98
99  // Number of nested sync messages that we're blocked on.
100  int pending_sync_message_count_;
101
102  // True when we've sent the "plugin is hung" message to the browser. We track
103  // this so we know to look for it becoming unhung and send the corresponding
104  // message to the browser.
105  bool hung_plugin_showing_;
106
107  bool timer_task_pending_;
108
109  DISALLOW_COPY_AND_ASSIGN(PepperHungPluginFilter);
110};
111
112}  // namespace content
113
114#endif  // CONTENT_RENDERER_PEPPER_PEPPER_HUNG_PLUGIN_FILTER_H_
115