summaryrefslogtreecommitdiffstats
path: root/chrome/browser/render_process_host.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/render_process_host.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/render_process_host.cc')
-rw-r--r--chrome/browser/render_process_host.cc778
1 files changed, 778 insertions, 0 deletions
diff --git a/chrome/browser/render_process_host.cc b/chrome/browser/render_process_host.cc
new file mode 100644
index 0000000..a434320
--- /dev/null
+++ b/chrome/browser/render_process_host.cc
@@ -0,0 +1,778 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Represents the browser side of the browser <--> renderer communication
+// channel. There will be one RenderProcessHost per renderer process.
+
+#include "chrome/browser/render_process_host.h"
+
+#include <windows.h>
+#include <wininet.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/debug_util.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/win_util.h"
+#include "chrome/app/result_codes.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/cache_manager_host.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/plugin_service.h"
+#include "chrome/browser/render_widget_helper.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/renderer_security_policy.h"
+#include "chrome/browser/resource_message_filter.h"
+#include "chrome/browser/sandbox_policy.h"
+#include "chrome/browser/visitedlink_master.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/debug_flags.h"
+#include "chrome/common/env_util.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/win_util.h"
+#include "chrome/renderer/render_process.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_util.h"
+#include "sandbox/src/sandbox.h"
+
+#include "SkBitmap.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// Defines the maximum number of renderer processes according to the amount
+// of installed memory as reported by the OS. The table values are calculated
+// by assuming that you want the renderers to use half of the installed ram
+// and assuming that each tab uses ~25MB.
+const int kMaxRenderersByRamTier[] = {
+ 4, // less than 256MB
+ 8, // 256MB
+ 12, // 512MB
+ 16, // 768MB
+ chrome::kMaxRendererProcessCount, // 1GB or more.
+};
+
+unsigned int GetMaxRendererProcessCount() {
+ static unsigned int max_count = 0;
+ if (!max_count) {
+ int memory_tier = env_util::GetPhysicalMemoryMB() / 256;
+ if (memory_tier >= arraysize(kMaxRenderersByRamTier))
+ max_count = chrome::kMaxRendererProcessCount;
+ else
+ max_count = kMaxRenderersByRamTier[memory_tier];
+ }
+ return max_count;
+}
+
+// ----------------------------------------------------------------------------
+
+class RendererMainThread : public Thread {
+ public:
+ explicit RendererMainThread(const std::wstring& channel_id)
+ : Thread("Chrome_InProcRendererThread"),
+ channel_id_(channel_id) {
+ }
+
+ protected:
+ virtual void Init() {
+ CoInitialize(NULL);
+
+ bool rv = RenderProcess::GlobalInit(channel_id_);
+ DCHECK(rv);
+ // It's a little lame to manually set this flag. But the single process
+ // RendererThread will receive the WM_QUIT. We don't need to assert on
+ // this thread, so just force the flag manually.
+ // If we want to avoid this, we could create the InProcRendererThread
+ // directly with _beginthreadex() rather than using the Thread class.
+ Thread::SetThreadWasQuitProperly(true);
+ }
+
+ virtual void CleanUp() {
+ RenderProcess::GlobalCleanup();
+
+ CoUninitialize();
+ }
+
+ private:
+ std::wstring channel_id_;
+};
+
+// Used for a View_ID where the renderer has not been attached yet
+const int32 kInvalidViewID = -1;
+
+// the global list of all renderer processes
+IDMap<RenderProcessHost> all_hosts;
+
+// Get the path to the renderer executable, which is the same as the
+// current executable.
+bool GetRendererPath(std::wstring* cmd_line) {
+ return PathService::Get(base::FILE_EXE, cmd_line);
+}
+
+const wchar_t* const kDesktopName = L"ChromeRendererDesktop";
+
+} // namespace
+
+//------------------------------------------------------------------------------
+
+bool RenderProcessHost::run_renderer_in_process_ = false;
+
+// static
+void RenderProcessHost::RegisterPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(prefs::kStartRenderersManually, false);
+}
+
+// static
+RenderProcessHost* RenderProcessHost::FromID(int render_process_id) {
+ return all_hosts.Lookup(render_process_id);
+}
+
+RenderProcessHost::RenderProcessHost(Profile* profile)
+ : profile_(profile),
+ max_page_id_(-1),
+ visible_widgets_(0),
+ backgrounded_(true),
+ notified_termination_(false) {
+ host_id_ = all_hosts.Add(this);
+ DCHECK(host_id_ >= 0); // We use a negative host_id_ in destruction.
+ widget_helper_ = new RenderWidgetHelper(host_id_);
+
+ CacheManagerHost::GetInstance()->Add(host_id_);
+ RendererSecurityPolicy::GetInstance()->Add(host_id_);
+
+ PrefService* prefs = profile->GetPrefs();
+ prefs->AddPrefObserver(prefs::kBlockPopups, this);
+ widget_helper_->set_block_popups(
+ profile->GetPrefs()->GetBoolean(prefs::kBlockPopups));
+
+ // Note: When we create the RenderProcessHost, it's technically backgrounded,
+ // because it has no visible listeners. But the process doesn't
+ // actually exist yet, so we'll Background it later, after creation.
+}
+
+RenderProcessHost::~RenderProcessHost() {
+ // Some tests hold RenderProcessHost in a scoped_ptr, so we must call
+ // Unregister here as well as in response to Release().
+ Unregister();
+
+ // We may have some unsent messages at this point, but that's OK.
+ channel_.reset();
+
+ if (process_.handle() && !run_renderer_in_process_) {
+ MessageLoop::current()->WatchObject(process_.handle(), NULL);
+ ProcessWatcher::EnsureProcessTerminated(process_.handle());
+ }
+
+ profile_->GetPrefs()->RemovePrefObserver(prefs::kBlockPopups, this);
+}
+
+void RenderProcessHost::Unregister() {
+ if (host_id_ >= 0) {
+ CacheManagerHost::GetInstance()->Remove(host_id_);
+ RendererSecurityPolicy::GetInstance()->Remove(host_id_);
+ all_hosts.Remove(host_id_);
+ host_id_ = -1;
+ }
+}
+
+bool RenderProcessHost::Init() {
+ // calling Init() more than once does nothing, this makes it more convenient
+ // for the view host which may not be sure in some cases
+ if (channel_.get())
+ return true;
+
+ // run the IPC channel on the shared IO thread.
+ Thread* io_thread = g_browser_process->io_thread();
+
+ scoped_refptr<ResourceMessageFilter> resource_message_filter =
+ new ResourceMessageFilter(g_browser_process->resource_dispatcher_host(),
+ PluginService::GetInstance(),
+ g_browser_process->print_job_manager(),
+ host_id_,
+ profile_,
+ widget_helper_,
+ profile_->GetSpellChecker());
+
+ CommandLine browser_command_line;
+
+ // setup IPC channel
+ std::wstring channel_id = GenerateRandomChannelID(this);
+ channel_.reset(
+ new IPC::ChannelProxy(channel_id, IPC::Channel::MODE_SERVER, this,
+ resource_message_filter,
+ io_thread->message_loop()));
+
+ // build command line for renderer, we have to quote the executable name to
+ // deal with spaces
+ std::wstring renderer_path =
+ browser_command_line.GetSwitchValue(switches::kRendererPath);
+ if (renderer_path.empty())
+ if (!GetRendererPath(&renderer_path))
+ return false;
+ std::wstring cmd_line;
+ cmd_line = L"\"" + renderer_path + L"\"";
+ if (logging::DialogsAreSuppressed())
+ CommandLine::AppendSwitch(&cmd_line, switches::kNoErrorDialogs);
+
+ // propagate the following switches to the renderer command line
+ // (along with any associated values) if present in the browser command line
+ static const wchar_t* const switch_names[] = {
+ switches::kRendererAssertTest,
+ switches::kRendererCrashTest,
+ switches::kRendererStartupDialog,
+ switches::kNoSandbox,
+ switches::kTestSandbox,
+ switches::kInProcessPlugins,
+ switches::kDomAutomationController,
+ switches::kJavaScriptFlags,
+ switches::kRecordMode,
+ switches::kPlaybackMode,
+ switches::kDisableBreakpad,
+ switches::kFullMemoryCrashReport,
+ switches::kEnableLogging,
+ switches::kDumpHistogramsOnExit,
+ switches::kDisableLogging,
+ switches::kDebugPrint,
+ switches::kDnsPrefetchDisable,
+ switches::kAllowAllActiveX,
+ switches::kMemoryProfiling,
+ switches::kEnableWatchdog,
+ switches::kMessageLoopStrategy,
+ switches::kMessageLoopHistogrammer,
+ switches::kEnableDCHECK,
+ switches::kSilentDumpOnDCHECK,
+ switches::kDisablePopupBlocking,
+ switches::kUseLowFragHeapCrt
+ };
+
+ for (int i = 0; i < arraysize(switch_names); ++i) {
+ if (browser_command_line.HasSwitch(switch_names[i])) {
+ CommandLine::AppendSwitchWithValue(
+ &cmd_line, switch_names[i],
+ browser_command_line.GetSwitchValue(switch_names[i]));
+ }
+ }
+
+ // Pass on the browser locale.
+ const std::wstring locale = g_browser_process->GetApplicationLocale();
+ CommandLine::AppendSwitchWithValue(&cmd_line, switches::kLang, locale);
+
+ bool in_sandbox = !browser_command_line.HasSwitch(switches::kNoSandbox);
+ if (browser_command_line.HasSwitch(switches::kInProcessPlugins)) {
+ // In process plugins won't work if the sandbox is enabled.
+ in_sandbox = false;
+ }
+
+ bool child_needs_help =
+ DebugFlags::ProcessDebugFlags(&cmd_line,
+ DebugFlags::RENDERER,
+ in_sandbox);
+ CommandLine::AppendSwitchWithValue(&cmd_line,
+ switches::kProcessType,
+ switches::kRendererProcess);
+
+ CommandLine::AppendSwitchWithValue(&cmd_line,
+ switches::kProcessChannelID,
+ channel_id);
+
+ const std::wstring& profile_path =
+ browser_command_line.GetSwitchValue(switches::kUserDataDir);
+ if (!profile_path.empty())
+ CommandLine::AppendSwitchWithValue(&cmd_line, switches::kUserDataDir,
+ profile_path);
+
+ bool run_in_process = RenderProcessHost::run_renderer_in_process();
+ if (run_in_process) {
+ // Crank up a thread and run the initialization there. With the
+ // way that messages flow between the browser and renderer, this
+ // thread is required to prevent a deadlock in single-process mode.
+ // When using multiple processes, the primordial thread in the
+ // renderer process has a message loop which is used for sending
+ // messages asynchronously to the io thread in the browser process.
+ // If we don't create this thread, then the RenderThread is both
+ // responsible for rendering and also for communicating IO.
+ // This can lead to deadlocks where the RenderThread is waiting
+ // for the IO to complete, while the browsermain is trying to
+ // pass an event to the RenderThread.
+ //
+ // TODO: We should consider how to better cleanup threads on
+ // exit.
+ Thread *renderThread = new RendererMainThread(channel_id);
+ renderThread->Start();
+ } else {
+ if (g_browser_process->local_state() &&
+ g_browser_process->local_state()->GetBoolean(
+ prefs::kStartRenderersManually)) {
+ std::wstring message =
+ L"Please start a renderer process using:\n" + cmd_line;
+
+ // We don't know the owner window for RenderProcessHost and therefore we
+ // pass a NULL HWND argument.
+ win_util::MessageBox(NULL,
+ message,
+ switches::kBrowserStartRenderersManually,
+ MB_OK);
+ } else {
+ if (in_sandbox) {
+ // spawn the child process in the sandbox
+ sandbox::BrokerServices* broker_service =
+ g_browser_process->broker_services();
+
+ sandbox::ResultCode result;
+ PROCESS_INFORMATION target = {0};
+ sandbox::TargetPolicy* policy = broker_service->CreatePolicy();
+ policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0);
+
+ sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED;
+ if (win_util::GetWinVersion() > win_util::WINVERSION_XP) {
+ // On 2003/Vista the initial token has to be restricted if the main
+ // token is restricted.
+ initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ }
+
+ policy->SetTokenLevel(initial_token, sandbox::USER_LOCKDOWN);
+ policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+
+ HDESK desktop = CreateDesktop(kDesktopName, NULL, NULL, 0,
+ DESKTOP_CREATEWINDOW, NULL);
+ if (desktop) {
+ policy->SetDesktop(kDesktopName);
+ } else {
+ DLOG(WARNING) << "Failed to apply desktop security to the renderer";
+ }
+
+ if (!AddGenericPolicy(policy)) {
+ NOTREACHED();
+ return false;
+ }
+
+ result = broker_service->SpawnTarget(renderer_path.c_str(),
+ cmd_line.c_str(),
+ policy, &target);
+ policy->Release();
+
+ if (desktop)
+ CloseDesktop(desktop);
+
+ if (sandbox::SBOX_ALL_OK != result)
+ return false;
+
+ bool on_sandbox_desktop = (desktop != NULL);
+ NotificationService::current()->Notify(
+ NOTIFY_RENDERER_PROCESS_IN_SBOX, Source<RenderProcessHost>(this),
+ Details<bool>(&on_sandbox_desktop));
+
+ ResumeThread(target.hThread);
+ CloseHandle(target.hThread);
+ process_.set_handle(target.hProcess);
+
+ // Help the process a little. It can't start the debugger by itself if
+ // the process is in a sandbox.
+ if (child_needs_help)
+ DebugUtil::SpawnDebuggerOnProcess(target.dwProcessId);
+ } else {
+ // spawn child process
+ HANDLE process;
+ if (!process_util::LaunchApp(cmd_line, false, false, &process))
+ return false;
+ process_.set_handle(process);
+ }
+
+ MessageLoop::current()->WatchObject(process_.handle(), this);
+ }
+ }
+
+ // Now that the process is created, set it's backgrounding accordingly.
+ SetBackgrounded(backgrounded_);
+
+ VisitedLinkMaster* visitedlink_master = profile_->GetVisitedLinkMaster();
+ if (visitedlink_master) {
+ std::wstring history_table_name = visitedlink_master->GetSharedMemoryName();
+ SharedMemoryHandle handle_for_process = NULL;
+ HANDLE target_process = process_.handle();
+ if (!target_process) {
+ // Target process can be null if it's started with the --single-process
+ // flag.
+ target_process = GetCurrentProcess();
+ }
+
+ visitedlink_master->ShareToProcess(target_process, &handle_for_process);
+ DCHECK(handle_for_process);
+
+ channel_->Send(new ViewMsg_VisitedLink_NewTable(handle_for_process));
+ }
+
+ if (max_page_id_ != -1)
+ channel_->Send(new ViewMsg_SetNextPageID(max_page_id_ + 1));
+
+ return true;
+}
+
+void RenderProcessHost::Attach(IPC::Channel::Listener* listener,
+ int routing_id) {
+ listeners_.AddWithID(listener, routing_id);
+}
+
+void RenderProcessHost::Release(int listener_id) {
+ DCHECK(listeners_.Lookup(listener_id) != NULL);
+ listeners_.Remove(listener_id);
+
+ // make sure that all associated resource requests are stopped.
+ widget_helper_->CancelResourceRequests(listener_id);
+
+ // when no other owners of this object, we can delete ourselves
+ if (listeners_.IsEmpty()) {
+ if (!notified_termination_) {
+ bool close_expected = true;
+ NotificationService::current()->Notify(NOTIFY_RENDERER_PROCESS_TERMINATED,
+ Source<RenderProcessHost>(this),
+ Details<bool>(&close_expected));
+ notified_termination_ = true;
+ }
+ Unregister();
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ }
+}
+
+void RenderProcessHost::ReportExpectingClose(int32 listener_id) {
+ listeners_expecting_close_.insert(listener_id);
+}
+
+bool RenderProcessHost::FastShutdownIfPossible() {
+ HANDLE proc = process();
+ if (!proc)
+ return false;
+ // If we're in single process mode, do nothing.
+ if (RenderProcessHost::run_renderer_in_process())
+ return false;
+
+ // Test if there's an unload listener
+ RenderProcessHost::listeners_iterator iter;
+ // NOTE: This is a bit dangerous. We know that for now, listeners are
+ // always RenderWidgetHosts. But in theory, they don't have to be.
+ for (iter = listeners_begin(); iter != listeners_end(); ++iter) {
+ RenderWidgetHost* widget = static_cast<RenderWidgetHost*>(iter->second);
+ DCHECK(widget);
+ if (!widget || !widget->IsRenderView())
+ continue;
+ RenderViewHost* rvh = static_cast<RenderViewHost*>(widget);
+ if (rvh->HasUnloadListener()) {
+ // NOTE: It's possible that an onunload listener may be installed
+ // while we're shutting down, so there's a small race here. Given that
+ // the window is small, it's unlikely that the web page has much
+ // state that will be lost by not calling its unload handlers properly.
+ return false;
+ }
+ }
+ // Otherwise, call TerminateProcess. Using exit code 0 means that UMA won't
+ // treat this as a renderer crash.
+ ::TerminateProcess(proc, 0);
+ return true;
+}
+
+// Static. This function can be called from the IO Thread or from the UI thread.
+void RenderProcessHost::BadMessageTerminateProcess(uint16 msg_type,
+ HANDLE process) {
+ LOG(ERROR) << "bad message " << msg_type << " terminating renderer.";
+ if (RenderProcessHost::run_renderer_in_process()) {
+ // In single process mode it is better if we don't suicide but just crash.
+ CHECK(false);
+ }
+ NOTREACHED();
+ ::TerminateProcess(process, ResultCodes::KILLED_BAD_MESSAGE);
+}
+
+void RenderProcessHost::UpdateMaxPageID(int32 page_id) {
+ if (page_id > max_page_id_)
+ max_page_id_ = page_id;
+}
+
+void RenderProcessHost::CrossSiteClosePageACK(int new_render_process_host_id,
+ int new_request_id) {
+ widget_helper_->CrossSiteClosePageACK(new_render_process_host_id,
+ new_request_id);
+}
+
+void RenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ // dispatch control messages
+ bool msg_is_ok = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(RenderProcessHost, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PageContents, OnPageContents)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdatedCacheStats,
+ OnUpdatedCacheStats)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP_EX()
+
+ if (!msg_is_ok) {
+ // The message had a handler, but its de-serialization failed.
+ // We consider this a capital crime. Kill the renderer if we have one.
+ ReceivedBadMessage(msg.type());
+ }
+ return;
+ }
+
+ // dispatch incoming messages to the appropriate TabContents
+ IPC::Channel::Listener* listener = listeners_.Lookup(msg.routing_id());
+ if (!listener) {
+ DLOG(WARNING) << "Listener " << msg.routing_id() << " not found for "
+ << msg.type();
+ return;
+ }
+ listener->OnMessageReceived(msg);
+}
+
+void RenderProcessHost::OnChannelConnected(int32 peer_pid) {
+ // process_ is not NULL if we created the renderer process
+ if (!process_.handle()) {
+ if (GetCurrentProcessId() == peer_pid) {
+ // We are in single-process mode. In theory we should have access to
+ // ourself but it may happen that we don't.
+ process_.set_handle(GetCurrentProcess());
+ } else {
+ // Request MAXIMUM_ALLOWED to match the access a handle
+ // returned by CreateProcess() has to the process object.
+ process_.set_handle(OpenProcess(MAXIMUM_ALLOWED, FALSE, peer_pid));
+ DCHECK(process_.handle());
+ MessageLoop::current()->WatchObject(process_.handle(), this);
+ }
+ } else {
+ // Need to verify that the peer_pid is actually the process we know, if
+ // it is not, we need to panic now. See bug 1002150.
+ CHECK(peer_pid == process_.pid());
+ }
+}
+
+// indicates the renderer process has exited
+void RenderProcessHost::OnObjectSignaled(HANDLE object) {
+ DCHECK(process_.handle());
+ DCHECK(channel_.get());
+ DCHECK_EQ(object, process_.handle());
+
+ MessageLoop::current()->WatchObject(object, NULL);
+
+ bool clean_shutdown = !process_util::DidProcessCrash(object);
+
+ process_.Close();
+
+ channel_.reset();
+
+ if (!notified_termination_) {
+ // If |close_expected| is false, it means the renderer process went away
+ // before the web views expected it; count it as a crash.
+ NotificationService::current()->Notify(NOTIFY_RENDERER_PROCESS_TERMINATED,
+ Source<RenderProcessHost>(this),
+ Details<bool>(&clean_shutdown));
+ notified_termination_ = true;
+ }
+
+ // This process should detach all the listeners, causing the object to be
+ // deleted. We therefore need a stack copy of the web view list to avoid
+ // crashing when checking for the termination condition the last time.
+ IDMap<IPC::Channel::Listener> local_listeners(listeners_);
+ for (IDMap<IPC::Channel::Listener>::const_iterator i = local_listeners.begin();
+ i != local_listeners.end(); ++i) {
+ i->second->OnMessageReceived(ViewHostMsg_RendererGone(i->first));
+ }
+ // at this point, this object should be deleted
+}
+
+// Used to send responses to resource requests
+bool RenderProcessHost::Send(IPC::Message* msg) {
+ if (!channel_.get()) {
+ delete msg;
+ return false;
+ }
+ return channel_->Send(msg);
+}
+
+void RenderProcessHost::OnPageContents(const GURL& url,
+ int32 page_id,
+ const std::wstring& contents) {
+ Profile* p = profile();
+ if (!p || p->IsOffTheRecord())
+ return;
+
+ HistoryService* hs = p->GetHistoryService(Profile::IMPLICIT_ACCESS);
+ if (hs)
+ hs->SetPageContents(url, contents);
+}
+
+void RenderProcessHost::OnUpdatedCacheStats(
+ const CacheManager::UsageStats& stats) {
+ CacheManagerHost::GetInstance()->ObserveStats(host_id(), stats);
+}
+
+// static
+RenderProcessHost::iterator RenderProcessHost::begin() {
+ return all_hosts.begin();
+}
+
+// static
+RenderProcessHost::iterator RenderProcessHost::end() {
+ return all_hosts.end();
+}
+
+// static
+size_t RenderProcessHost::size() {
+ return all_hosts.size();
+}
+
+// Returns true if the given host is suitable for launching a new view
+// associated with the given profile.
+// TODO(jabdelmalek): do we want to avoid processes with hung renderers
+// or with a large memory consumption?
+static bool IsSuitableHost(Profile* profile, RenderProcessHost* host) {
+ return host->profile() == profile;
+}
+
+// static
+RenderProcessHost* RenderProcessHost::GetExistingProcessHost(Profile* profile) {
+ // First figure out which existing renderers we can use
+ std::vector<RenderProcessHost*> suitable_renderers;
+ suitable_renderers.reserve(all_hosts.size());
+
+ for (IDMap<RenderProcessHost>::const_iterator iter = all_hosts.begin();
+ iter != all_hosts.end(); ++iter) {
+ if (IsSuitableHost(profile, iter->second))
+ suitable_renderers.push_back(iter->second);
+ }
+
+ // Now pick a random suitable renderer, if we have any
+ if (!suitable_renderers.empty()) {
+ int suitable_count = static_cast<int>(suitable_renderers.size());
+ int random_index = rand_util::RandInt(0, suitable_count - 1);
+ return suitable_renderers[random_index];
+ }
+
+ return NULL;
+}
+
+void RenderProcessHost::SetBackgrounded(bool backgrounded) {
+ // If the process_ is NULL, the process hasn't been created yet.
+ if (process_.handle()) {
+ process_.SetProcessBackgrounded(backgrounded);
+
+ // Now tune the memory footprint of the renderer.
+ // If the OS needs to page, we'd rather it page idle renderers.
+ BrowserProcess::MemoryModel model = g_browser_process->memory_model();
+ if (model < BrowserProcess::HIGH_MEMORY_MODEL) {
+ if (backgrounded) {
+ if (model == BrowserProcess::LOW_MEMORY_MODEL)
+ process_.EmptyWorkingSet();
+ else if (model == BrowserProcess::MEDIUM_MEMORY_MODEL)
+ process_.ReduceWorkingSet();
+ } else {
+ if (model == BrowserProcess::MEDIUM_MEMORY_MODEL)
+ process_.UnReduceWorkingSet();
+ }
+ }
+ }
+
+ // Note: we always set the backgrounded_ value. If the process is NULL
+ // (and hence hasn't been created yet), we will set the process priority
+ // later when we create the process.
+ backgrounded_ = backgrounded;
+}
+
+void RenderProcessHost::WidgetRestored() {
+ // Verify we were properly backgrounded.
+ DCHECK(backgrounded_ == (visible_widgets_ == 0));
+ visible_widgets_++;
+ SetBackgrounded(false);
+}
+
+void RenderProcessHost::WidgetHidden() {
+ // On startup, the browser will call Hide
+ if (backgrounded_)
+ return;
+
+ DCHECK(backgrounded_ == (visible_widgets_ == 0));
+ visible_widgets_--;
+ DCHECK(visible_widgets_ >= 0);
+ if (visible_widgets_ == 0) {
+ DCHECK(!backgrounded_);
+ SetBackgrounded(true);
+ }
+}
+
+// NotificationObserver implementation.
+void RenderProcessHost::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_PREF_CHANGED: {
+ std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
+ DCHECK(Source<PrefService>(source).ptr() == profile()->GetPrefs());
+ if (*pref_name_in == prefs::kBlockPopups) {
+ widget_helper_->set_block_popups(
+ profile()->GetPrefs()->GetBoolean(prefs::kBlockPopups));
+ } else {
+ NOTREACHED() << "unexpected pref change notification" << *pref_name_in;
+ }
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+// static
+bool RenderProcessHost::ShouldTryToUseExistingProcessHost() {
+ unsigned int renderer_process_count =
+ static_cast<unsigned int>(all_hosts.size());
+
+ // NOTE: Sometimes it's necessary to create more render processes than
+ // GetMaxRendererProcessCount(), for instance when we want to create
+ // a renderer process for a profile that has no existing renderers.
+ // This is OK in moderation, since the GetMaxRendererProcessCount()
+ // is conservative.
+
+ return run_renderer_in_process() ||
+ (renderer_process_count >= GetMaxRendererProcessCount());
+}