[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / content / renderer / plugin_channel_host.cc
blob5d251b66b7bd6f35394fe6bf07e95742863e32b9
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.
5 #include "content/renderer/plugin_channel_host.h"
7 #include "base/metrics/histogram.h"
8 #include "base/time.h"
9 #include "content/common/child_process.h"
10 #include "content/common/npobject_base.h"
11 #include "content/common/plugin_messages.h"
13 #if defined(OS_POSIX)
14 #include "ipc/ipc_channel_posix.h"
15 #endif
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
19 // TODO(shess): Debugging for http://crbug.com/97285
21 // The hypothesis at #55 requires that RemoveRoute() be called between
22 // sending ViewHostMsg_OpenChannelToPlugin to the browser, and calling
23 // GetPluginChannelHost() on the result. This code detects that case
24 // and stores the backtrace of the RemoveRoute() in a breakpad key.
25 // The specific RemoveRoute() is not tracked (there could be multiple,
26 // and which is the one can't be known until the open completes), but
27 // the backtrace from any such nested call should be sufficient to
28 // drive a repro.
29 #if defined(OS_MACOSX)
30 #include "base/debug/stack_trace.h"
31 #include "base/mac/crash_logging.h"
32 #include "base/sys_string_conversions.h"
34 namespace {
36 // Breakpad key for the RemoveRoute() backtrace.
37 const char* kRemoveRouteTraceKey = "remove_route_bt";
39 // Breakpad key for the OnChannelError() backtrace.
40 const char* kChannelErrorTraceKey = "channel_error_bt";
42 // GetRemoveTrackingFlag() exposes this so that
43 // WebPluginDelegateProxy::Initialize() can do scoped set/reset. When
44 // true, RemoveRoute() knows WBDP::Initialize() is on the stack, and
45 // records the backtrace.
46 bool remove_tracking = false;
48 } // namespace
49 #endif
51 namespace content {
53 // A simple MessageFilter that will ignore all messages and respond to sync
54 // messages with an error when is_listening_ is false.
55 class IsListeningFilter : public IPC::ChannelProxy::MessageFilter {
56 public:
57 IsListeningFilter() : channel_(NULL) {}
59 // MessageFilter overrides
60 virtual void OnFilterRemoved() {}
61 virtual void OnFilterAdded(IPC::Channel* channel) { channel_ = channel; }
62 virtual bool OnMessageReceived(const IPC::Message& message);
64 static bool is_listening_;
66 protected:
67 virtual ~IsListeningFilter() {}
69 private:
70 IPC::Channel* channel_;
72 DISALLOW_COPY_AND_ASSIGN(IsListeningFilter);
75 bool IsListeningFilter::OnMessageReceived(const IPC::Message& message) {
76 if (IsListeningFilter::is_listening_) {
77 // Proceed with normal operation.
78 return false;
81 // Always process message reply to prevent renderer from hanging on sync
82 // messages.
83 if (message.is_reply() || message.is_reply_error()) {
84 return false;
87 // Reply to synchronous messages with an error (so they don't block while
88 // we're not listening).
89 if (message.is_sync()) {
90 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
91 reply->set_reply_error();
92 channel_->Send(reply);
94 return true;
97 // static
98 bool IsListeningFilter::is_listening_ = true;
100 // static
101 bool PluginChannelHost::IsListening() {
102 return IsListeningFilter::is_listening_;
105 // static
106 void PluginChannelHost::SetListening(bool flag) {
107 IsListeningFilter::is_listening_ = flag;
110 #if defined(OS_MACOSX)
111 // static
112 bool* PluginChannelHost::GetRemoveTrackingFlag() {
113 return &remove_tracking;
115 #endif
117 // static
118 PluginChannelHost* PluginChannelHost::GetPluginChannelHost(
119 const IPC::ChannelHandle& channel_handle,
120 base::MessageLoopProxy* ipc_message_loop) {
121 PluginChannelHost* result =
122 static_cast<PluginChannelHost*>(NPChannelBase::GetChannel(
123 channel_handle,
124 IPC::Channel::MODE_CLIENT,
125 ClassFactory,
126 ipc_message_loop,
127 true,
128 ChildProcess::current()->GetShutDownEvent()));
129 return result;
132 PluginChannelHost::PluginChannelHost() : expecting_shutdown_(false) {
135 PluginChannelHost::~PluginChannelHost() {
138 bool PluginChannelHost::Init(base::MessageLoopProxy* ipc_message_loop,
139 bool create_pipe_now,
140 base::WaitableEvent* shutdown_event) {
141 bool ret =
142 NPChannelBase::Init(ipc_message_loop, create_pipe_now, shutdown_event);
143 if (ret) {
144 is_listening_filter_ = new IsListeningFilter;
145 channel_->AddFilter(is_listening_filter_);
147 return ret;
150 int PluginChannelHost::GenerateRouteID() {
151 int route_id = MSG_ROUTING_NONE;
152 Send(new PluginMsg_GenerateRouteID(&route_id));
154 return route_id;
157 void PluginChannelHost::AddRoute(int route_id,
158 IPC::Listener* listener,
159 NPObjectBase* npobject) {
160 NPChannelBase::AddRoute(route_id, listener, npobject);
162 if (!npobject)
163 proxies_[route_id] = listener;
166 void PluginChannelHost::RemoveRoute(int route_id) {
167 #if defined(OS_MACOSX)
168 if (remove_tracking) {
169 base::debug::StackTrace trace;
170 size_t count = 0;
171 const void* const* addresses = trace.Addresses(&count);
172 base::mac::SetCrashKeyFromAddresses(
173 base::SysUTF8ToNSString(kRemoveRouteTraceKey), addresses, count);
175 #endif
177 proxies_.erase(route_id);
178 NPChannelBase::RemoveRoute(route_id);
181 bool PluginChannelHost::OnControlMessageReceived(const IPC::Message& message) {
182 bool handled = true;
183 IPC_BEGIN_MESSAGE_MAP(PluginChannelHost, message)
184 IPC_MESSAGE_HANDLER(PluginHostMsg_SetException, OnSetException)
185 IPC_MESSAGE_HANDLER(PluginHostMsg_PluginShuttingDown, OnPluginShuttingDown)
186 IPC_MESSAGE_UNHANDLED(handled = false)
187 IPC_END_MESSAGE_MAP()
188 DCHECK(handled);
189 return handled;
192 void PluginChannelHost::OnSetException(const std::string& message) {
193 WebKit::WebBindings::setException(NULL, message.c_str());
196 void PluginChannelHost::OnPluginShuttingDown() {
197 expecting_shutdown_ = true;
200 bool PluginChannelHost::Send(IPC::Message* msg) {
201 if (msg->is_sync()) {
202 base::TimeTicks start_time(base::TimeTicks::Now());
203 bool result = NPChannelBase::Send(msg);
204 UMA_HISTOGRAM_TIMES("Plugin.SyncMessageTime",
205 base::TimeTicks::Now() - start_time);
206 return result;
208 return NPChannelBase::Send(msg);
211 void PluginChannelHost::OnChannelError() {
212 #if defined(OS_MACOSX)
213 if (remove_tracking) {
214 base::debug::StackTrace trace;
215 size_t count = 0;
216 const void* const* addresses = trace.Addresses(&count);
217 base::mac::SetCrashKeyFromAddresses(
218 base::SysUTF8ToNSString(kChannelErrorTraceKey), addresses, count);
220 #endif
222 NPChannelBase::OnChannelError();
224 for (ProxyMap::iterator iter = proxies_.begin();
225 iter != proxies_.end(); iter++) {
226 iter->second->OnChannelError();
229 proxies_.clear();
232 } // namespace content