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 34namespace { 35 36// Breakpad key for the RemoveRoute() backtrace. 37const char* kRemoveRouteTraceKey = "remove_route_bt"; 38 39// Breakpad key for the OnChannelError() backtrace. 40const 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. 46bool remove_tracking = false; 47 48} // namespace 49#endif 50 51namespace content { 52 53#if defined(OS_MACOSX) 54// static 55bool* PluginChannelHost::GetRemoveTrackingFlag() { 56 return &remove_tracking; 57} 58#endif 59 60// static 61PluginChannelHost* 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 75PluginChannelHost::PluginChannelHost() : expecting_shutdown_(false) { 76} 77 78PluginChannelHost::~PluginChannelHost() { 79} 80 81bool 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 87int PluginChannelHost::GenerateRouteID() { 88 int route_id = MSG_ROUTING_NONE; 89 Send(new PluginMsg_GenerateRouteID(&route_id)); 90 91 return route_id; 92} 93 94void 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 103void 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 118bool 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 129void PluginChannelHost::OnSetException(const std::string& message) { 130 blink::WebBindings::setException(NULL, message.c_str()); 131} 132 133void PluginChannelHost::OnPluginShuttingDown() { 134 expecting_shutdown_ = true; 135} 136 137bool 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 148void 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