summaryrefslogtreecommitdiffstats
path: root/chrome/common/child_process_host.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/common/child_process_host.cc')
-rw-r--r--chrome/common/child_process_host.cc190
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());
+}