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"
9 #include "content/common/child_process.h"
10 #include "content/common/npobject_base.h"
11 #include "content/common/plugin_messages.h"
14 #include "ipc/ipc_channel_posix.h"
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
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"
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;
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
{
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_
;
67 virtual ~IsListeningFilter() {}
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.
81 // Always process message reply to prevent renderer from hanging on sync
83 if (message
.is_reply() || message
.is_reply_error()) {
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
);
98 bool IsListeningFilter::is_listening_
= true;
101 bool PluginChannelHost::IsListening() {
102 return IsListeningFilter::is_listening_
;
106 void PluginChannelHost::SetListening(bool flag
) {
107 IsListeningFilter::is_listening_
= flag
;
110 #if defined(OS_MACOSX)
112 bool* PluginChannelHost::GetRemoveTrackingFlag() {
113 return &remove_tracking
;
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(
124 IPC::Channel::MODE_CLIENT
,
128 ChildProcess::current()->GetShutDownEvent()));
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
) {
142 NPChannelBase::Init(ipc_message_loop
, create_pipe_now
, shutdown_event
);
144 is_listening_filter_
= new IsListeningFilter
;
145 channel_
->AddFilter(is_listening_filter_
);
150 int PluginChannelHost::GenerateRouteID() {
151 int route_id
= MSG_ROUTING_NONE
;
152 Send(new PluginMsg_GenerateRouteID(&route_id
));
157 void PluginChannelHost::AddRoute(int route_id
,
158 IPC::Listener
* listener
,
159 NPObjectBase
* npobject
) {
160 NPChannelBase::AddRoute(route_id
, listener
, 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
;
171 const void* const* addresses
= trace
.Addresses(&count
);
172 base::mac::SetCrashKeyFromAddresses(
173 base::SysUTF8ToNSString(kRemoveRouteTraceKey
), addresses
, count
);
177 proxies_
.erase(route_id
);
178 NPChannelBase::RemoveRoute(route_id
);
181 bool PluginChannelHost::OnControlMessageReceived(const IPC::Message
& message
) {
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()
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
);
208 return NPChannelBase::Send(msg
);
211 void PluginChannelHost::OnChannelError() {
212 #if defined(OS_MACOSX)
213 if (remove_tracking
) {
214 base::debug::StackTrace trace
;
216 const void* const* addresses
= trace
.Addresses(&count
);
217 base::mac::SetCrashKeyFromAddresses(
218 base::SysUTF8ToNSString(kChannelErrorTraceKey
), addresses
, count
);
222 NPChannelBase::OnChannelError();
224 for (ProxyMap::iterator iter
= proxies_
.begin();
225 iter
!= proxies_
.end(); iter
++) {
226 iter
->second
->OnChannelError();
232 } // namespace content