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/npapi/plugin_channel_host.h" 6 7 #include "base/metrics/histogram.h" 8 #include "base/time/time.h" 9 #include "content/child/child_process.h" 10 #include "content/child/npapi/npobject_base.h" 11 #include "content/child/plugin_messages.h" 12 13 #if defined(OS_POSIX) 14 #include "ipc/ipc_channel_posix.h" 15 #endif 16 17 #include "third_party/WebKit/public/web/WebBindings.h" 18 19 // TODO(shess): Debugging for http://crbug.com/97285 20 // 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/crash_logging.h" 31 #include "base/debug/stack_trace.h" 32 #include "base/strings/sys_string_conversions.h" 33 34 namespace { 35 36 // Breakpad key for the RemoveRoute() backtrace. 37 const char* kRemoveRouteTraceKey = "remove_route_bt"; 38 39 // Breakpad key for the OnChannelError() backtrace. 40 const char* kChannelErrorTraceKey = "channel_error_bt"; 41 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; 47 48 } // namespace 49 #endif 50 51 namespace content { 52 53 #if defined(OS_MACOSX) 54 // static 55 bool* PluginChannelHost::GetRemoveTrackingFlag() { 56 return &remove_tracking; 57 } 58 #endif 59 60 // static 61 PluginChannelHost* PluginChannelHost::GetPluginChannelHost( 62 const IPC::ChannelHandle& channel_handle, 63 base::MessageLoopProxy* ipc_message_loop) { 64 PluginChannelHost* result = 65 static_cast<PluginChannelHost*>(NPChannelBase::GetChannel( 66 channel_handle, 67 IPC::Channel::MODE_CLIENT, 68 ClassFactory, 69 ipc_message_loop, 70 true, 71 ChildProcess::current()->GetShutDownEvent())); 72 return result; 73 } 74 75 PluginChannelHost::PluginChannelHost() : expecting_shutdown_(false) { 76 } 77 78 PluginChannelHost::~PluginChannelHost() { 79 } 80 81 bool PluginChannelHost::Init(base::MessageLoopProxy* ipc_message_loop, 82 bool create_pipe_now, 83 base::WaitableEvent* shutdown_event) { 84 return NPChannelBase::Init(ipc_message_loop, create_pipe_now, shutdown_event); 85 } 86 87 int PluginChannelHost::GenerateRouteID() { 88 int route_id = MSG_ROUTING_NONE; 89 Send(new PluginMsg_GenerateRouteID(&route_id)); 90 91 return route_id; 92 } 93 94 void PluginChannelHost::AddRoute(int route_id, 95 IPC::Listener* listener, 96 NPObjectBase* npobject) { 97 NPChannelBase::AddRoute(route_id, listener, npobject); 98 99 if (!npobject) 100 proxies_[route_id] = listener; 101 } 102 103 void PluginChannelHost::RemoveRoute(int route_id) { 104 #if defined(OS_MACOSX) 105 if (remove_tracking) { 106 base::debug::StackTrace trace; 107 size_t count = 0; 108 const void* const* addresses = trace.Addresses(&count); 109 base::debug::SetCrashKeyFromAddresses( 110 kRemoveRouteTraceKey, addresses, count); 111 } 112 #endif 113 114 proxies_.erase(route_id); 115 NPChannelBase::RemoveRoute(route_id); 116 } 117 118 bool PluginChannelHost::OnControlMessageReceived(const IPC::Message& message) { 119 bool handled = true; 120 IPC_BEGIN_MESSAGE_MAP(PluginChannelHost, message) 121 IPC_MESSAGE_HANDLER(PluginHostMsg_SetException, OnSetException) 122 IPC_MESSAGE_HANDLER(PluginHostMsg_PluginShuttingDown, OnPluginShuttingDown) 123 IPC_MESSAGE_UNHANDLED(handled = false) 124 IPC_END_MESSAGE_MAP() 125 DCHECK(handled); 126 return handled; 127 } 128 129 void PluginChannelHost::OnSetException(const std::string& message) { 130 blink::WebBindings::setException(NULL, message.c_str()); 131 } 132 133 void PluginChannelHost::OnPluginShuttingDown() { 134 expecting_shutdown_ = true; 135 } 136 137 bool PluginChannelHost::Send(IPC::Message* msg) { 138 if (msg->is_sync()) { 139 base::TimeTicks start_time(base::TimeTicks::Now()); 140 bool result = NPChannelBase::Send(msg); 141 UMA_HISTOGRAM_TIMES("Plugin.SyncMessageTime", 142 base::TimeTicks::Now() - start_time); 143 return result; 144 } 145 return NPChannelBase::Send(msg); 146 } 147 148 void PluginChannelHost::OnChannelError() { 149 #if defined(OS_MACOSX) 150 if (remove_tracking) { 151 base::debug::StackTrace trace; 152 size_t count = 0; 153 const void* const* addresses = trace.Addresses(&count); 154 base::debug::SetCrashKeyFromAddresses( 155 kChannelErrorTraceKey, addresses, count); 156 } 157 #endif 158 159 NPChannelBase::OnChannelError(); 160 161 for (ProxyMap::iterator iter = proxies_.begin(); 162 iter != proxies_.end(); iter++) { 163 iter->second->OnChannelError(); 164 } 165 166 proxies_.clear(); 167 } 168 169 } // namespace content 170