// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/npapi/plugin_channel_host.h" #include "base/metrics/histogram.h" #include "base/time/time.h" #include "content/child/child_process.h" #include "content/child/npapi/npobject_base.h" #include "content/child/plugin_messages.h" #if defined(OS_POSIX) #include "ipc/ipc_channel_posix.h" #endif #include "third_party/WebKit/public/web/WebBindings.h" // TODO(shess): Debugging for http://crbug.com/97285 // // The hypothesis at #55 requires that RemoveRoute() be called between // sending ViewHostMsg_OpenChannelToPlugin to the browser, and calling // GetPluginChannelHost() on the result. This code detects that case // and stores the backtrace of the RemoveRoute() in a breakpad key. // The specific RemoveRoute() is not tracked (there could be multiple, // and which is the one can't be known until the open completes), but // the backtrace from any such nested call should be sufficient to // drive a repro. #if defined(OS_MACOSX) #include "base/debug/crash_logging.h" #include "base/debug/stack_trace.h" #include "base/strings/sys_string_conversions.h" namespace { // Breakpad key for the RemoveRoute() backtrace. const char* kRemoveRouteTraceKey = "remove_route_bt"; // Breakpad key for the OnChannelError() backtrace. const char* kChannelErrorTraceKey = "channel_error_bt"; // GetRemoveTrackingFlag() exposes this so that // WebPluginDelegateProxy::Initialize() can do scoped set/reset. When // true, RemoveRoute() knows WBDP::Initialize() is on the stack, and // records the backtrace. bool remove_tracking = false; } // namespace #endif namespace content { #if defined(OS_MACOSX) // static bool* PluginChannelHost::GetRemoveTrackingFlag() { return &remove_tracking; } #endif // static PluginChannelHost* PluginChannelHost::GetPluginChannelHost( const IPC::ChannelHandle& channel_handle, base::SingleThreadTaskRunner* ipc_task_runner) { PluginChannelHost* result = static_cast(NPChannelBase::GetChannel( channel_handle, IPC::Channel::MODE_CLIENT, ClassFactory, ipc_task_runner, true, ChildProcess::current()->GetShutDownEvent())); return result; } PluginChannelHost::PluginChannelHost() : expecting_shutdown_(false) { } PluginChannelHost::~PluginChannelHost() { } bool PluginChannelHost::Init(base::SingleThreadTaskRunner* ipc_task_runner, bool create_pipe_now, base::WaitableEvent* shutdown_event) { return NPChannelBase::Init(ipc_task_runner, create_pipe_now, shutdown_event); } int PluginChannelHost::GenerateRouteID() { int route_id = MSG_ROUTING_NONE; Send(new PluginMsg_GenerateRouteID(&route_id)); return route_id; } void PluginChannelHost::AddRoute(int route_id, IPC::Listener* listener, NPObjectBase* npobject) { NPChannelBase::AddRoute(route_id, listener, npobject); if (!npobject) proxies_[route_id] = listener; } void PluginChannelHost::RemoveRoute(int route_id) { #if defined(OS_MACOSX) if (remove_tracking) { base::debug::StackTrace trace; size_t count = 0; const void* const* addresses = trace.Addresses(&count); base::debug::SetCrashKeyFromAddresses( kRemoveRouteTraceKey, addresses, count); } #endif proxies_.erase(route_id); NPChannelBase::RemoveRoute(route_id); } bool PluginChannelHost::OnControlMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PluginChannelHost, message) IPC_MESSAGE_HANDLER(PluginHostMsg_SetException, OnSetException) IPC_MESSAGE_HANDLER(PluginHostMsg_PluginShuttingDown, OnPluginShuttingDown) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() DCHECK(handled); return handled; } void PluginChannelHost::OnSetException(const std::string& message) { blink::WebBindings::setException(NULL, message.c_str()); } void PluginChannelHost::OnPluginShuttingDown() { expecting_shutdown_ = true; } bool PluginChannelHost::Send(IPC::Message* msg) { if (msg->is_sync()) { base::TimeTicks start_time(base::TimeTicks::Now()); bool result = NPChannelBase::Send(msg); UMA_HISTOGRAM_TIMES("Plugin.SyncMessageTime", base::TimeTicks::Now() - start_time); return result; } return NPChannelBase::Send(msg); } void PluginChannelHost::OnChannelError() { #if defined(OS_MACOSX) if (remove_tracking) { base::debug::StackTrace trace; size_t count = 0; const void* const* addresses = trace.Addresses(&count); base::debug::SetCrashKeyFromAddresses( kChannelErrorTraceKey, addresses, count); } #endif NPChannelBase::OnChannelError(); for (ProxyMap::iterator iter = proxies_.begin(); iter != proxies_.end(); iter++) { iter->second->OnChannelError(); } proxies_.clear(); } } // namespace content