|  | // 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/ppapi_plugin/ppapi_thread.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/cpu.h" | 
|  | #include "base/debug/alias.h" | 
|  | #include "base/debug/crash_logging.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/discardable_memory_allocator.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/metrics/sparse_histogram.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/child/browser_font_resource_trusted.h" | 
|  | #include "content/child/child_discardable_shared_memory_manager.h" | 
|  | #include "content/child/child_process.h" | 
|  | #include "content/common/child_process_messages.h" | 
|  | #include "content/ppapi_plugin/broker_process_dispatcher.h" | 
|  | #include "content/ppapi_plugin/plugin_process_dispatcher.h" | 
|  | #include "content/ppapi_plugin/ppapi_blink_platform_impl.h" | 
|  | #include "content/public/common/content_client.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "content/public/common/pepper_plugin_info.h" | 
|  | #include "content/public/common/sandbox_init.h" | 
|  | #include "ipc/ipc_channel_handle.h" | 
|  | #include "ipc/ipc_platform_file.h" | 
|  | #include "ipc/ipc_sync_channel.h" | 
|  | #include "ipc/ipc_sync_message_filter.h" | 
|  | #include "ppapi/c/dev/ppp_network_state_dev.h" | 
|  | #include "ppapi/c/pp_errors.h" | 
|  | #include "ppapi/c/ppp.h" | 
|  | #include "ppapi/proxy/interface_list.h" | 
|  | #include "ppapi/proxy/plugin_globals.h" | 
|  | #include "ppapi/proxy/plugin_message_filter.h" | 
|  | #include "ppapi/proxy/ppapi_messages.h" | 
|  | #include "ppapi/proxy/resource_reply_thread_registrar.h" | 
|  | #include "third_party/WebKit/public/web/WebKit.h" | 
|  | #include "ui/base/ui_base_switches.h" | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include "base/win/win_util.h" | 
|  | #include "base/win/windows_version.h" | 
|  | #include "content/child/font_warmup_win.h" | 
|  | #include "sandbox/win/src/sandbox.h" | 
|  | #elif defined(OS_MACOSX) | 
|  | #include "content/common/sandbox_init_mac.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | const char kWidevineCdmAdapterFileName[] = "widevinecdmadapter.dll"; | 
|  |  | 
|  | extern sandbox::TargetServices* g_target_services; | 
|  |  | 
|  | // Used by EnumSystemLocales for warming up. | 
|  | static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) { | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CALLBACK EnumLocalesProcEx( | 
|  | LPWSTR lpLocaleString, | 
|  | DWORD dwFlags, | 
|  | LPARAM lParam) { | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | // Warm up language subsystems before the sandbox is turned on. | 
|  | static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) { | 
|  | ::GetUserDefaultLangID(); | 
|  | ::GetUserDefaultLCID(); | 
|  |  | 
|  | if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) { | 
|  | if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | 
|  | typedef BOOL (WINAPI *PfnEnumSystemLocalesEx) | 
|  | (LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID); | 
|  |  | 
|  | HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll"); | 
|  | PfnEnumSystemLocalesEx enum_sys_locales_ex = | 
|  | reinterpret_cast<PfnEnumSystemLocalesEx> | 
|  | (GetProcAddress(handle_kern32, "EnumSystemLocalesEx")); | 
|  |  | 
|  | enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0); | 
|  | } else { | 
|  | EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | typedef int32_t (*InitializeBrokerFunc) | 
|  | (PP_ConnectInstance_Func* connect_instance_func); | 
|  |  | 
|  | PpapiThread::PpapiThread(const base::CommandLine& command_line, bool is_broker) | 
|  | : is_broker_(is_broker), | 
|  | plugin_globals_(GetIOTaskRunner()), | 
|  | connect_instance_func_(NULL), | 
|  | local_pp_module_(base::RandInt(0, std::numeric_limits<PP_Module>::max())), | 
|  | next_plugin_dispatcher_id_(1) { | 
|  | plugin_globals_.SetPluginProxyDelegate(this); | 
|  | plugin_globals_.set_command_line( | 
|  | command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs)); | 
|  |  | 
|  | blink_platform_impl_.reset(new PpapiBlinkPlatformImpl); | 
|  | blink::Platform::initialize(blink_platform_impl_.get()); | 
|  |  | 
|  | if (!is_broker_) { | 
|  | scoped_refptr<ppapi::proxy::PluginMessageFilter> plugin_filter( | 
|  | new ppapi::proxy::PluginMessageFilter( | 
|  | NULL, plugin_globals_.resource_reply_thread_registrar())); | 
|  | channel()->AddFilter(plugin_filter.get()); | 
|  | plugin_globals_.RegisterResourceMessageFilters(plugin_filter.get()); | 
|  | } | 
|  |  | 
|  | // In single process, browser main loop set up the discardable memory | 
|  | // allocator. | 
|  | if (!command_line.HasSwitch(switches::kSingleProcess)) { | 
|  | base::DiscardableMemoryAllocator::SetInstance( | 
|  | ChildThreadImpl::discardable_shared_memory_manager()); | 
|  | } | 
|  | } | 
|  |  | 
|  | PpapiThread::~PpapiThread() { | 
|  | } | 
|  |  | 
|  | void PpapiThread::Shutdown() { | 
|  | ChildThreadImpl::Shutdown(); | 
|  |  | 
|  | ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate(); | 
|  | if (plugin_entry_points_.shutdown_module) | 
|  | plugin_entry_points_.shutdown_module(); | 
|  | blink_platform_impl_->Shutdown(); | 
|  | } | 
|  |  | 
|  | bool PpapiThread::Send(IPC::Message* msg) { | 
|  | // Allow access from multiple threads. | 
|  | if (message_loop()->task_runner()->BelongsToCurrentThread()) | 
|  | return ChildThreadImpl::Send(msg); | 
|  |  | 
|  | return sync_message_filter()->Send(msg); | 
|  | } | 
|  |  | 
|  | // Note that this function is called only for messages from the channel to the | 
|  | // browser process. Messages from the renderer process are sent via a different | 
|  | // channel that ends up at Dispatcher::OnMessageReceived. | 
|  | bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) { | 
|  | bool handled = true; | 
|  | IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg) | 
|  | IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin) | 
|  | IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel) | 
|  | IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState) | 
|  | IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash) | 
|  | IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang) | 
|  | IPC_MESSAGE_UNHANDLED(handled = false) | 
|  | IPC_END_MESSAGE_MAP() | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnChannelConnected(int32_t peer_pid) { | 
|  | ChildThreadImpl::OnChannelConnected(peer_pid); | 
|  | #if defined(OS_WIN) | 
|  | if (is_broker_) | 
|  | peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid)); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | base::SingleThreadTaskRunner* PpapiThread::GetIPCTaskRunner() { | 
|  | return ChildProcess::current()->io_task_runner(); | 
|  | } | 
|  |  | 
|  | base::WaitableEvent* PpapiThread::GetShutdownEvent() { | 
|  | return ChildProcess::current()->GetShutDownEvent(); | 
|  | } | 
|  |  | 
|  | IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote( | 
|  | base::PlatformFile handle, | 
|  | base::ProcessId peer_pid, | 
|  | bool should_close_source) { | 
|  | return IPC::GetPlatformFileForTransit(handle, should_close_source); | 
|  | } | 
|  |  | 
|  | base::SharedMemoryHandle PpapiThread::ShareSharedMemoryHandleWithRemote( | 
|  | const base::SharedMemoryHandle& handle, | 
|  | base::ProcessId remote_pid) { | 
|  | DCHECK(remote_pid != base::kNullProcessId); | 
|  | return base::SharedMemory::DuplicateHandle(handle); | 
|  | } | 
|  |  | 
|  | std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() { | 
|  | return &globally_seen_instance_ids_; | 
|  | } | 
|  |  | 
|  | IPC::Sender* PpapiThread::GetBrowserSender() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | std::string PpapiThread::GetUILanguage() { | 
|  | base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | 
|  | return command_line->GetSwitchValueASCII(switches::kLang); | 
|  | } | 
|  |  | 
|  | void PpapiThread::PreCacheFontForFlash(const void* logfontw) { | 
|  | #if defined(OS_WIN) | 
|  | ChildThreadImpl::PreCacheFont(*static_cast<const LOGFONTW*>(logfontw)); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void PpapiThread::SetActiveURL(const std::string& url) { | 
|  | GetContentClient()->SetActiveURL(GURL(url)); | 
|  | } | 
|  |  | 
|  | PP_Resource PpapiThread::CreateBrowserFont( | 
|  | ppapi::proxy::Connection connection, | 
|  | PP_Instance instance, | 
|  | const PP_BrowserFont_Trusted_Description& desc, | 
|  | const ppapi::Preferences& prefs) { | 
|  | if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc)) | 
|  | return 0; | 
|  | return (new BrowserFontResource_Trusted( | 
|  | connection, instance, desc, prefs))->GetReference(); | 
|  | } | 
|  |  | 
|  | uint32_t PpapiThread::Register( | 
|  | ppapi::proxy::PluginDispatcher* plugin_dispatcher) { | 
|  | if (!plugin_dispatcher || | 
|  | plugin_dispatchers_.size() >= std::numeric_limits<uint32_t>::max()) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | uint32_t id = 0; | 
|  | do { | 
|  | // Although it is unlikely, make sure that we won't cause any trouble when | 
|  | // the counter overflows. | 
|  | id = next_plugin_dispatcher_id_++; | 
|  | } while (id == 0 || | 
|  | plugin_dispatchers_.find(id) != plugin_dispatchers_.end()); | 
|  | plugin_dispatchers_[id] = plugin_dispatcher; | 
|  | return id; | 
|  | } | 
|  |  | 
|  | void PpapiThread::Unregister(uint32_t plugin_dispatcher_id) { | 
|  | plugin_dispatchers_.erase(plugin_dispatcher_id); | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnLoadPlugin(const base::FilePath& path, | 
|  | const ppapi::PpapiPermissions& permissions) { | 
|  | // In case of crashes, the crash dump doesn't indicate which plugin | 
|  | // it came from. | 
|  | base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII()); | 
|  |  | 
|  | SavePluginName(path); | 
|  |  | 
|  | // This must be set before calling into the plugin so it can get the | 
|  | // interfaces it has permission for. | 
|  | ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions); | 
|  | permissions_ = permissions; | 
|  |  | 
|  | // Trusted Pepper plugins may be "internal", i.e. built-in to the browser | 
|  | // binary.  If we're being asked to load such a plugin (e.g. the Chromoting | 
|  | // client) then fetch the entry points from the embedder, rather than a DLL. | 
|  | std::vector<PepperPluginInfo> plugins; | 
|  | GetContentClient()->AddPepperPlugins(&plugins); | 
|  | for (size_t i = 0; i < plugins.size(); ++i) { | 
|  | if (plugins[i].is_internal && plugins[i].path == path) { | 
|  | // An internal plugin is being loaded, so fetch the entry points. | 
|  | plugin_entry_points_ = plugins[i].internal_entry_points; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If the plugin isn't internal then load it from |path|. | 
|  | base::ScopedNativeLibrary library; | 
|  | if (plugin_entry_points_.initialize_module == NULL) { | 
|  | // Load the plugin from the specified library. | 
|  | base::NativeLibraryLoadError error; | 
|  | base::TimeDelta load_time; | 
|  | { | 
|  | TRACE_EVENT1("ppapi", "PpapiThread::LoadPlugin", "path", | 
|  | path.MaybeAsASCII()); | 
|  |  | 
|  | base::TimeTicks start = base::TimeTicks::Now(); | 
|  | library.Reset(base::LoadNativeLibrary(path, &error)); | 
|  | load_time = base::TimeTicks::Now() - start; | 
|  | } | 
|  |  | 
|  | if (!library.is_valid()) { | 
|  | LOG(ERROR) << "Failed to load Pepper module from " << path.value() | 
|  | << " (error: " << error.ToString() << ")"; | 
|  | if (!base::PathExists(path)) { | 
|  | ReportLoadResult(path, FILE_MISSING); | 
|  | return; | 
|  | } | 
|  | ReportLoadResult(path, LOAD_FAILED); | 
|  | // Report detailed reason for load failure. | 
|  | ReportLoadErrorCode(path, error); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Only report load time for success loads. | 
|  | ReportLoadTime(path, load_time); | 
|  |  | 
|  | // Get the GetInterface function (required). | 
|  | plugin_entry_points_.get_interface = | 
|  | reinterpret_cast<PP_GetInterface_Func>( | 
|  | library.GetFunctionPointer("PPP_GetInterface")); | 
|  | if (!plugin_entry_points_.get_interface) { | 
|  | LOG(WARNING) << "No PPP_GetInterface in plugin library"; | 
|  | ReportLoadResult(path, ENTRY_POINT_MISSING); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The ShutdownModule/ShutdownBroker function is optional. | 
|  | plugin_entry_points_.shutdown_module = | 
|  | is_broker_ ? | 
|  | reinterpret_cast<PP_ShutdownModule_Func>( | 
|  | library.GetFunctionPointer("PPP_ShutdownBroker")) : | 
|  | reinterpret_cast<PP_ShutdownModule_Func>( | 
|  | library.GetFunctionPointer("PPP_ShutdownModule")); | 
|  |  | 
|  | if (!is_broker_) { | 
|  | // Get the InitializeModule function (required for non-broker code). | 
|  | plugin_entry_points_.initialize_module = | 
|  | reinterpret_cast<PP_InitializeModule_Func>( | 
|  | library.GetFunctionPointer("PPP_InitializeModule")); | 
|  | if (!plugin_entry_points_.initialize_module) { | 
|  | LOG(WARNING) << "No PPP_InitializeModule in plugin library"; | 
|  | ReportLoadResult(path, ENTRY_POINT_MISSING); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | // If code subsequently tries to exit using abort(), force a crash (since | 
|  | // otherwise these would be silent terminations and fly under the radar). | 
|  | base::win::SetAbortBehaviorForCrashReporting(); | 
|  |  | 
|  | // Once we lower the token the sandbox is locked down and no new modules | 
|  | // can be loaded. TODO(cpu): consider changing to the loading style of | 
|  | // regular plugins. | 
|  | if (g_target_services) { | 
|  | // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+. | 
|  | if (permissions.HasPermission(ppapi::PERMISSION_FLASH) || | 
|  | path.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName) { | 
|  | if (base::win::OSInfo::GetInstance()->version() >= | 
|  | base::win::VERSION_VISTA) { | 
|  | LoadLibraryA("dxva2.dll"); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) { | 
|  | if (base::win::OSInfo::GetInstance()->version() >= | 
|  | base::win::VERSION_WIN7) { | 
|  | base::CPU cpu; | 
|  | if (cpu.vendor_name() == "AuthenticAMD") { | 
|  | // The AMD crypto acceleration is only AMD Bulldozer and above. | 
|  | #if defined(_WIN64) | 
|  | LoadLibraryA("amdhcp64.dll"); | 
|  | #else | 
|  | LoadLibraryA("amdhcp32.dll"); | 
|  | #endif | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Cause advapi32 to load before the sandbox is turned on. | 
|  | unsigned int dummy_rand; | 
|  | rand_s(&dummy_rand); | 
|  |  | 
|  | WarmupWindowsLocales(permissions); | 
|  |  | 
|  | if (!base::win::IsUser32AndGdi32Available() && | 
|  | permissions.HasPermission(ppapi::PERMISSION_FLASH)) { | 
|  | PatchGdiFontEnumeration(path); | 
|  | } | 
|  |  | 
|  | g_target_services->LowerToken(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (is_broker_) { | 
|  | // Get the InitializeBroker function (required). | 
|  | InitializeBrokerFunc init_broker = | 
|  | reinterpret_cast<InitializeBrokerFunc>( | 
|  | library.GetFunctionPointer("PPP_InitializeBroker")); | 
|  | if (!init_broker) { | 
|  | LOG(WARNING) << "No PPP_InitializeBroker in plugin library"; | 
|  | ReportLoadResult(path, ENTRY_POINT_MISSING); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int32_t init_error = init_broker(&connect_instance_func_); | 
|  | if (init_error != PP_OK) { | 
|  | LOG(WARNING) << "InitBroker failed with error " << init_error; | 
|  | ReportLoadResult(path, INIT_FAILED); | 
|  | return; | 
|  | } | 
|  | if (!connect_instance_func_) { | 
|  | LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func"; | 
|  | ReportLoadResult(path, INIT_FAILED); | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | #if defined(OS_MACOSX) | 
|  | // We need to do this after getting |PPP_GetInterface()| (or presumably | 
|  | // doing something nontrivial with the library), else the sandbox | 
|  | // intercedes. | 
|  | CHECK(InitializeSandbox()); | 
|  | #endif | 
|  |  | 
|  | int32_t init_error = plugin_entry_points_.initialize_module( | 
|  | local_pp_module_, | 
|  | &ppapi::proxy::PluginDispatcher::GetBrowserInterface); | 
|  | if (init_error != PP_OK) { | 
|  | LOG(WARNING) << "InitModule failed with error " << init_error; | 
|  | ReportLoadResult(path, INIT_FAILED); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Initialization succeeded, so keep the plugin DLL loaded. | 
|  | library_.Reset(library.Release()); | 
|  |  | 
|  | ReportLoadResult(path, LOAD_SUCCESS); | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid, | 
|  | int renderer_child_id, | 
|  | bool incognito) { | 
|  | IPC::ChannelHandle channel_handle; | 
|  |  | 
|  | if (!plugin_entry_points_.get_interface ||  // Plugin couldn't be loaded. | 
|  | !SetupChannel(renderer_pid, renderer_child_id, incognito, | 
|  | &channel_handle)) { | 
|  | Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle())); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Send(new PpapiHostMsg_ChannelCreated(channel_handle)); | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnSetNetworkState(bool online) { | 
|  | // Note the browser-process side shouldn't send us these messages in the | 
|  | // first unless the plugin has dev permissions, so we don't need to check | 
|  | // again here. We don't want random plugins depending on this dev interface. | 
|  | if (!plugin_entry_points_.get_interface) | 
|  | return; | 
|  | const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>( | 
|  | plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE)); | 
|  | if (ns) | 
|  | ns->SetOnLine(PP_FromBool(online)); | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnCrash() { | 
|  | // Intentionally crash upon the request of the browser. | 
|  | // | 
|  | // Linker's ICF feature may merge this function with other functions with the | 
|  | // same definition and it may confuse the crash report processing system. | 
|  | static int static_variable_to_make_this_function_unique = 0; | 
|  | base::debug::Alias(&static_variable_to_make_this_function_unique); | 
|  |  | 
|  | volatile int* null_pointer = nullptr; | 
|  | *null_pointer = 0; | 
|  | } | 
|  |  | 
|  | void PpapiThread::OnHang() { | 
|  | // Intentionally hang upon the request of the browser. | 
|  | for (;;) | 
|  | base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); | 
|  | } | 
|  |  | 
|  | bool PpapiThread::SetupChannel(base::ProcessId renderer_pid, | 
|  | int renderer_child_id, | 
|  | bool incognito, | 
|  | IPC::ChannelHandle* handle) { | 
|  | DCHECK(is_broker_ == (connect_instance_func_ != NULL)); | 
|  | IPC::ChannelHandle plugin_handle; | 
|  | plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID( | 
|  | base::StringPrintf( | 
|  | "%d.r%d", base::GetCurrentProcId(), renderer_child_id)); | 
|  |  | 
|  | ppapi::proxy::ProxyChannel* dispatcher = NULL; | 
|  | bool init_result = false; | 
|  | if (is_broker_) { | 
|  | bool peer_is_browser = renderer_pid == base::kNullProcessId; | 
|  | BrokerProcessDispatcher* broker_dispatcher = | 
|  | new BrokerProcessDispatcher(plugin_entry_points_.get_interface, | 
|  | connect_instance_func_, peer_is_browser); | 
|  | init_result = broker_dispatcher->InitBrokerWithChannel(this, | 
|  | renderer_pid, | 
|  | plugin_handle, | 
|  | false); | 
|  | dispatcher = broker_dispatcher; | 
|  | } else { | 
|  | DCHECK_NE(base::kNullProcessId, renderer_pid); | 
|  | PluginProcessDispatcher* plugin_dispatcher = | 
|  | new PluginProcessDispatcher(plugin_entry_points_.get_interface, | 
|  | permissions_, | 
|  | incognito); | 
|  | init_result = plugin_dispatcher->InitPluginWithChannel(this, | 
|  | renderer_pid, | 
|  | plugin_handle, | 
|  | false); | 
|  | dispatcher = plugin_dispatcher; | 
|  | } | 
|  |  | 
|  | if (!init_result) { | 
|  | delete dispatcher; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | handle->name = plugin_handle.name; | 
|  | #if defined(OS_POSIX) | 
|  | // On POSIX, transfer ownership of the renderer-side (client) FD. | 
|  | // This ensures this process will be notified when it is closed even if a | 
|  | // connection is not established. | 
|  | handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD()); | 
|  | if (handle->socket.fd == -1) | 
|  | return false; | 
|  | #endif | 
|  |  | 
|  | // From here, the dispatcher will manage its own lifetime according to the | 
|  | // lifetime of the attached channel. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void PpapiThread::SavePluginName(const base::FilePath& path) { | 
|  | ppapi::proxy::PluginGlobals::Get()->set_plugin_name( | 
|  | path.BaseName().AsUTF8Unsafe()); | 
|  | } | 
|  |  | 
|  | static std::string GetHistogramName(bool is_broker, | 
|  | const std::string& metric_name, | 
|  | const base::FilePath& path) { | 
|  | return std::string("Plugin.Ppapi") + (is_broker ? "Broker" : "Plugin") + | 
|  | metric_name + "_" + path.BaseName().MaybeAsASCII(); | 
|  | } | 
|  |  | 
|  | void PpapiThread::ReportLoadResult(const base::FilePath& path, | 
|  | LoadResult result) { | 
|  | DCHECK_LT(result, LOAD_RESULT_MAX); | 
|  |  | 
|  | // Note: This leaks memory, which is expected behavior. | 
|  | base::HistogramBase* histogram = | 
|  | base::LinearHistogram::FactoryGet( | 
|  | GetHistogramName(is_broker_, "LoadResult", path), | 
|  | 1, | 
|  | LOAD_RESULT_MAX, | 
|  | LOAD_RESULT_MAX + 1, | 
|  | base::HistogramBase::kUmaTargetedHistogramFlag); | 
|  |  | 
|  | histogram->Add(result); | 
|  | } | 
|  |  | 
|  | void PpapiThread::ReportLoadErrorCode( | 
|  | const base::FilePath& path, | 
|  | const base::NativeLibraryLoadError& error) { | 
|  | // Only report load error code on Windows because that's the only platform that | 
|  | // has a numerical error value. | 
|  | #if defined(OS_WIN) | 
|  | // For sparse histograms, we can use the macro, as it does not incorporate a | 
|  | // static. | 
|  | UMA_HISTOGRAM_SPARSE_SLOWLY( | 
|  | GetHistogramName(is_broker_, "LoadErrorCode", path), error.code); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void PpapiThread::ReportLoadTime(const base::FilePath& path, | 
|  | const base::TimeDelta load_time) { | 
|  | // Note: This leaks memory, which is expected behavior. | 
|  | base::HistogramBase* histogram = | 
|  | base::Histogram::FactoryTimeGet( | 
|  | GetHistogramName(is_broker_, "LoadTime", path), | 
|  | base::TimeDelta::FromMilliseconds(1), | 
|  | base::TimeDelta::FromSeconds(10), | 
|  | 50, | 
|  | base::HistogramBase::kUmaTargetedHistogramFlag); | 
|  |  | 
|  | histogram->AddTime(load_time); | 
|  | } | 
|  |  | 
|  | }  // namespace content |