diff options
Diffstat (limited to 'chrome/common/child_process_host.cc')
-rw-r--r-- | chrome/common/child_process_host.cc | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/chrome/common/child_process_host.cc b/chrome/common/child_process_host.cc new file mode 100644 index 0000000..bcd0c3d --- /dev/null +++ b/chrome/common/child_process_host.cc @@ -0,0 +1,190 @@ +// 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" + +#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<int>(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()); +} |