diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/render_process_host.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_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.cc | 778 |
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()); +} |