// Copyright (c) 2010 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 "chrome/common/child_process_host.h" #include "base/command_line.h" #include "base/histogram.h" #include "base/path_service.h" #include "chrome/common/child_process_info.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/plugin_messages.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_message.h" #if defined(OS_LINUX) #include "base/linux_util.h" #endif // OS_LINUX ChildProcessHost::ChildProcessHost() : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), opening_channel_(false) { } ChildProcessHost::~ChildProcessHost() { } // static FilePath ChildProcessHost::GetChildPath(bool allow_self) { FilePath child_path; child_path = CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kBrowserSubprocessPath); if (!child_path.empty()) return child_path; #if defined(OS_MACOSX) // On the Mac, the child executable lives at a predefined location within // the app bundle's versioned directory. return chrome::GetVersionedDirectory(). Append(chrome::kHelperProcessExecutablePath); #endif #if defined(OS_LINUX) // Use /proc/self/exe rather than our known binary path so updates // can't swap out the binary from underneath us. if (allow_self) return FilePath("/proc/self/exe"); #endif // On most platforms, the child executable is the same as the current // executable. PathService::Get(base::FILE_EXE, &child_path); return child_path; } #if defined(OS_WIN) // static void ChildProcessHost::PreCacheFont(LOGFONT font) { // If a child process is running in a sandbox, GetTextMetrics() // can sometimes fail. If a font has not been loaded // previously, GetTextMetrics() will try to load the font // from the font file. However, the sandboxed process does // not have permissions to access any font files and // the call fails. So we make the browser pre-load the // font for us by using a dummy call to GetTextMetrics of // the same font. // Maintain a circular queue for the fonts and DCs to be cached. // font_index maintains next available location in the queue. static const int kFontCacheSize = 32; static HFONT fonts[kFontCacheSize] = {0}; static HDC hdcs[kFontCacheSize] = {0}; static size_t font_index = 0; UMA_HISTOGRAM_COUNTS_100("Memory.CachedFontAndDC", fonts[kFontCacheSize-1] ? kFontCacheSize : static_cast(font_index)); HDC hdc = GetDC(NULL); HFONT font_handle = CreateFontIndirect(&font); DCHECK(NULL != font_handle); HGDIOBJ old_font = SelectObject(hdc, font_handle); DCHECK(NULL != old_font); TEXTMETRIC tm; BOOL ret = GetTextMetrics(hdc, &tm); DCHECK(ret); if (fonts[font_index] || hdcs[font_index]) { // We already have too many fonts, we will delete one and take it's place. DeleteObject(fonts[font_index]); ReleaseDC(NULL, hdcs[font_index]); } fonts[font_index] = font_handle; hdcs[font_index] = hdc; font_index = (font_index + 1) % kFontCacheSize; } #endif // OS_WIN bool ChildProcessHost::CreateChannel() { channel_id_ = ChildProcessInfo::GenerateRandomChannelID(this); channel_.reset(new IPC::Channel( channel_id_, IPC::Channel::MODE_SERVER, &listener_)); if (!channel_->Connect()) return false; // Make sure these messages get sent first. #if defined(IPC_MESSAGE_LOG_ENABLED) bool enabled = IPC::Logging::current()->Enabled(); SendOnChannel(new PluginProcessMsg_SetIPCLoggingEnabled(enabled)); #endif SendOnChannel(new PluginProcessMsg_AskBeforeShutdown()); opening_channel_ = true; return true; } void ChildProcessHost::InstanceCreated() { Notify(NotificationType::CHILD_INSTANCE_CREATED); } bool ChildProcessHost::SendOnChannel(IPC::Message* msg) { if (!channel_.get()) { delete msg; return false; } return channel_->Send(msg); } void ChildProcessHost::OnChildDied() { delete this; } ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) : host_(host) { } void ChildProcessHost::ListenerHook::OnMessageReceived( const IPC::Message& msg) { #ifdef IPC_MESSAGE_LOG_ENABLED IPC::Logging* logger = IPC::Logging::current(); if (msg.type() == IPC_LOGGING_ID) { logger->OnReceivedLoggingMessage(msg); return; } if (logger->Enabled()) logger->OnPreDispatchMessage(msg); #endif bool handled = host_->InterceptMessageFromChild(msg); if (!handled) { if (msg.type() == PluginProcessHostMsg_ShutdownRequest::ID) { if (host_->CanShutdown()) host_->SendOnChannel(new PluginProcessMsg_Shutdown()); } else { host_->OnMessageReceived(msg); } } #ifdef IPC_MESSAGE_LOG_ENABLED if (logger->Enabled()) logger->OnPostDispatchMessage(msg, host_->channel_id_); #endif } void ChildProcessHost::ListenerHook::OnChannelConnected(int32 peer_pid) { host_->opening_channel_ = false; host_->OnChannelConnected(peer_pid); // Notify in the main loop of the connection. host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED); } void ChildProcessHost::ListenerHook::OnChannelError() { host_->opening_channel_ = false; host_->OnChannelError(); // This will delete host_, which will also destroy this! host_->OnChildDied(); } void ChildProcessHost::ForceShutdown() { SendOnChannel(new PluginProcessMsg_Shutdown()); }