Index: chrome/browser/renderer_host/browser_render_process_host.cc |
=================================================================== |
--- chrome/browser/renderer_host/browser_render_process_host.cc (revision 32255) |
+++ chrome/browser/renderer_host/browser_render_process_host.cc (working copy) |
@@ -20,6 +20,7 @@ |
#include "base/field_trial.h" |
#include "base/logging.h" |
#include "base/process_util.h" |
+#include "base/stl_util-inl.h" |
#include "base/string_util.h" |
#include "base/thread.h" |
#include "chrome/browser/browser_process.h" |
@@ -47,10 +48,8 @@ |
#include "chrome/common/chrome_switches.h" |
#include "chrome/common/child_process_info.h" |
#include "chrome/common/child_process_host.h" |
-#include "chrome/common/chrome_descriptors.h" |
#include "chrome/common/logging_chrome.h" |
#include "chrome/common/notification_service.h" |
-#include "chrome/common/process_watcher.h" |
#include "chrome/common/render_messages.h" |
#include "chrome/common/result_codes.h" |
#include "chrome/renderer/render_process.h" |
@@ -63,12 +62,6 @@ |
#if defined(OS_WIN) |
#include "app/win_util.h" |
-#include "chrome/browser/sandbox_policy.h" |
-#elif defined(OS_LINUX) |
-#include "base/singleton.h" |
-#include "chrome/browser/crash_handler_host_linux.h" |
-#include "chrome/browser/zygote_host_linux.h" |
-#include "chrome/browser/renderer_host/render_sandbox_host_linux.h" |
#endif |
using WebKit::WebCache; |
@@ -201,7 +194,6 @@ |
ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_( |
base::TimeDelta::FromSeconds(5), |
this, &BrowserRenderProcessHost::ClearTransportDIBCache)), |
- zygote_child_(false), |
extension_process_(false) { |
widget_helper_ = new RenderWidgetHelper(); |
@@ -239,21 +231,15 @@ |
// We may have some unsent messages at this point, but that's OK. |
channel_.reset(); |
+ while (!queued_messages_.empty()) { |
+ delete queued_messages_.front(); |
+ queued_messages_.pop(); |
+ } |
// Destroy the AudioRendererHost properly. |
if (audio_renderer_host_.get()) |
audio_renderer_host_->Destroy(); |
- if (process_.handle() && !run_renderer_in_process()) { |
- if (zygote_child_) { |
-#if defined(OS_LINUX) |
- Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle()); |
-#endif |
- } else { |
- ProcessWatcher::EnsureProcessTerminated(process_.handle()); |
- } |
- } |
- |
ClearTransportDIBCache(); |
NotificationService::current()->Notify( |
@@ -313,16 +299,6 @@ |
// be doing. |
channel_->set_sync_messages_with_no_timeout_allowed(false); |
- // Build command line for renderer, we have to quote the executable name to |
- // deal with spaces. |
- CommandLine cmd_line(renderer_path); |
- cmd_line.AppendSwitchWithValue(switches::kProcessChannelID, |
- ASCIIToWide(channel_id)); |
- if (is_extensions_process) |
- cmd_line.AppendSwitch(switches::kEnableDatabases); |
- bool has_cmd_prefix; |
- AppendRendererCommandLine(&cmd_line, &has_cmd_prefix); |
- |
if (run_renderer_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 |
@@ -342,49 +318,25 @@ |
options.message_loop_type = MessageLoop::TYPE_DEFAULT; |
#endif |
in_process_renderer_->StartWithOptions(options); |
+ |
+ OnProcessLaunched(); // Fake a callback that the process is ready. |
} else { |
- base::TimeTicks begin_launch_time = base::TimeTicks::Now(); |
+ // Build command line for renderer, we have to quote the executable name to |
+ // deal with spaces. |
+ scoped_ptr<CommandLine> cmd_line(new CommandLine(renderer_path)); |
+ cmd_line->AppendSwitchWithValue(switches::kProcessChannelID, |
+ ASCIIToWide(channel_id)); |
+ if (is_extensions_process) |
+ cmd_line->AppendSwitch(switches::kEnableDatabases); |
+ AppendRendererCommandLine(cmd_line.get()); |
- // Actually spawn the child process. |
- base::ProcessHandle process = ExecuteRenderer(&cmd_line, has_cmd_prefix); |
- if (!process) { |
- channel_.reset(); |
- return false; |
- } |
- process_.set_handle(process); |
- fast_shutdown_started_ = false; |
+ // Spawn the child process asynchronously to avoid blocking the UI thread. |
+ child_process_.reset(new ChildProcessLauncher( |
+ cmd_line.release(), channel_.get(), this)); |
- // Log the launch time, separating out the first one (which will likely be |
- // slower due to the rest of the browser initializing at the same time). |
- static bool done_first_launch = false; |
- if (done_first_launch) { |
- UMA_HISTOGRAM_TIMES("MPArch.RendererLaunchSubsequent", |
- base::TimeTicks::Now() - begin_launch_time); |
- } else { |
- UMA_HISTOGRAM_TIMES("MPArch.RendererLaunchFirst", |
- base::TimeTicks::Now() - begin_launch_time); |
- done_first_launch = true; |
- } |
+ fast_shutdown_started_ = false; |
} |
- // Now that the process is created, set its backgrounding accordingly. |
- SetBackgrounded(backgrounded_); |
- |
- InitVisitedLinks(); |
- InitUserScripts(); |
- InitExtensions(); |
-#if defined(SPELLCHECKER_IN_RENDERER) |
- // We don't want to initialize the spellchecker unless SpellCheckHost has been |
- // created. In InitSpellChecker(), we know if GetSpellCheckHost() is NULL |
- // then the spellchecker has been turned off, but here, we don't know if |
- // it's been turned off or just not loaded yet. |
- if (profile()->GetSpellCheckHost()) |
- InitSpellChecker(); |
-#endif |
- |
- if (max_page_id_ != -1) |
- Send(new ViewMsg_SetNextPageID(max_page_id_ + 1)); |
- |
return true; |
} |
@@ -404,11 +356,16 @@ |
bool BrowserRenderProcessHost::WaitForPaintMsg(int render_widget_id, |
const base::TimeDelta& max_delay, |
IPC::Message* msg) { |
+ // The post task to this thread with the process id could be in queue, and we |
+ // don't want to dispatch a message before then since it will need the handle. |
+ if (child_process_.get() && child_process_->IsStarting()) |
+ return false; |
+ |
return widget_helper_->WaitForPaintMsg(render_widget_id, max_delay, msg); |
} |
void BrowserRenderProcessHost::ReceivedBadMessage(uint16 msg_type) { |
- BadMessageTerminateProcess(msg_type, process_.handle()); |
+ BadMessageTerminateProcess(msg_type, GetHandle()); |
} |
void BrowserRenderProcessHost::ViewCreated() { |
@@ -446,6 +403,21 @@ |
} |
} |
+void BrowserRenderProcessHost::SendVisitedLinkTable( |
+ base::SharedMemory* table_memory) { |
+ // Check if the process is still starting and we don't have a handle for it |
+ // yet, in which case this will happen later when InitVisitedLinks is called. |
+ if (!run_renderer_in_process() && |
+ (!child_process_.get() || child_process_->IsStarting())) { |
+ return; |
+ } |
+ |
+ base::SharedMemoryHandle handle_for_process; |
+ table_memory->ShareToProcess(GetHandle(), &handle_for_process); |
+ if (base::SharedMemory::IsHandleValid(handle_for_process)) |
+ Send(new ViewMsg_VisitedLink_NewTable(handle_for_process)); |
+} |
+ |
void BrowserRenderProcessHost::AddVisitedLinks( |
const VisitedLinkCommon::Fingerprints& links) { |
visited_link_updater_->AddLinks(links); |
@@ -464,8 +436,7 @@ |
} |
void BrowserRenderProcessHost::AppendRendererCommandLine( |
- CommandLine* command_line, |
- bool* has_cmd_prefix) const { |
+ CommandLine* command_line) const { |
if (logging::DialogsAreSuppressed()) |
command_line->AppendSwitch(switches::kNoErrorDialogs); |
@@ -497,16 +468,12 @@ |
// A command prefix is something prepended to the command line of the spawned |
// process. It is supported only on POSIX systems. |
#if defined(OS_POSIX) |
- *has_cmd_prefix = |
- browser_command_line.HasSwitch(switches::kRendererCmdPrefix); |
- if (*has_cmd_prefix) { |
+ if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) { |
// launch the renderer child with some prefix (usually "gdb --args") |
const std::wstring prefix = |
browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix); |
command_line->PrependWrapper(prefix); |
} |
-#else |
- *has_cmd_prefix = false; |
#endif // defined(OS_POSIX) |
ChildProcessHost::SetCrashReporterCommandLine(command_line); |
@@ -598,88 +565,26 @@ |
} |
} |
-#if defined(OS_WIN) |
+base::ProcessHandle BrowserRenderProcessHost::GetHandle() { |
+ // child_process_ is null either because we're in single process mode, we have |
+ // done fast termination, or the process has crashed. |
+ if (run_renderer_in_process() || !child_process_.get()) |
+ return base::Process::Current().handle(); |
-base::ProcessHandle BrowserRenderProcessHost::ExecuteRenderer( |
- CommandLine* cmd_line, |
- bool has_cmd_prefix) { |
- return sandbox::StartProcess(cmd_line); |
-} |
- |
-#elif defined(OS_POSIX) |
- |
-base::ProcessHandle BrowserRenderProcessHost::ExecuteRenderer( |
- CommandLine* cmd_line, |
- bool has_cmd_prefix) { |
-#if defined(OS_LINUX) |
- // On Linux, normally spawn processes with zygotes. We can't do this when |
- // we're spawning child processes through an external program (i.e. there is a |
- // command prefix) like GDB so fall through to the POSIX case then. |
- if (!has_cmd_prefix) { |
- base::GlobalDescriptors::Mapping mapping; |
- const int ipcfd = channel_->GetClientFileDescriptor(); |
- mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd)); |
- const int crash_signal_fd = |
- Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket(); |
- if (crash_signal_fd >= 0) { |
- mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal, |
- crash_signal_fd)); |
- } |
- zygote_child_ = true; |
- return Singleton<ZygoteHost>()->ForkRenderer(cmd_line->argv(), mapping); |
+ if (child_process_->IsStarting()) { |
+ NOTREACHED() << "BrowserRenderProcessHost::GetHandle() called early!"; |
+ return base::kNullProcessHandle; |
} |
-#endif // defined(OS_LINUX) |
- // NOTE: This code is duplicated with plugin_process_host.cc, but |
- // there's not a good place to de-duplicate it. |
- base::file_handle_mapping_vector fds_to_map; |
- const int ipcfd = channel_->GetClientFileDescriptor(); |
- fds_to_map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3)); |
- |
-#if defined(OS_LINUX) |
- // On Linux, we need to add some extra file descriptors for crash handling and |
- // the sandbox. |
- const int crash_signal_fd = |
- Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket(); |
- if (crash_signal_fd >= 0) { |
- fds_to_map.push_back(std::make_pair(crash_signal_fd, |
- kCrashDumpSignal + 3)); |
- } |
- const int sandbox_fd = |
- Singleton<RenderSandboxHostLinux>()->GetRendererSocket(); |
- fds_to_map.push_back(std::make_pair(sandbox_fd, kSandboxIPCChannel + 3)); |
-#endif // defined(OS_LINUX) |
- |
- // Actually launch the app. |
- zygote_child_ = false; |
- base::ProcessHandle process_handle; |
- if (!base::LaunchApp(cmd_line->argv(), fds_to_map, false, &process_handle)) |
- return 0; |
- return process_handle; |
+ return child_process_->GetHandle(); |
} |
-#endif // defined(OS_POSIX) |
- |
-base::ProcessHandle BrowserRenderProcessHost::GetHandle() { |
- if (run_renderer_in_process()) |
- return base::Process::Current().handle(); |
- |
- return process_.handle(); |
-} |
- |
void BrowserRenderProcessHost::InitVisitedLinks() { |
VisitedLinkMaster* visitedlink_master = profile()->GetVisitedLinkMaster(); |
- if (!visitedlink_master) { |
+ if (!visitedlink_master) |
return; |
- } |
- base::SharedMemoryHandle handle_for_process; |
- bool r = visitedlink_master->ShareToProcess(GetHandle(), &handle_for_process); |
- DCHECK(r); |
- |
- if (base::SharedMemory::IsHandleValid(handle_for_process)) { |
- Send(new ViewMsg_VisitedLink_NewTable(handle_for_process)); |
- } |
+ SendVisitedLinkTable(visitedlink_master->shared_memory()); |
} |
void BrowserRenderProcessHost::InitUserScripts() { |
@@ -705,6 +610,11 @@ |
void BrowserRenderProcessHost::SendUserScriptsUpdate( |
base::SharedMemory *shared_memory) { |
+ // Process is being started asynchronously. We'll end up calling |
+ // InitUserScripts when it's created which will call this again. |
+ if (child_process_.get() && child_process_->IsStarting()) |
+ return; |
+ |
base::SharedMemoryHandle handle_for_process; |
if (!shared_memory->ShareToProcess(GetHandle(), &handle_for_process)) { |
// This can legitimately fail if the renderer asserts at startup. |
@@ -717,11 +627,12 @@ |
} |
bool BrowserRenderProcessHost::FastShutdownIfPossible() { |
- if (!process_.handle()) |
- return false; // Render process is probably crashed. |
if (run_renderer_in_process()) |
return false; // Single process mode can't do fast shutdown. |
+ if (!child_process_.get() || child_process_->IsStarting() || !GetHandle()) |
+ return false; // Render process hasn't started or is probably crashed. |
+ |
// Test if there's an unload listener. |
// 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 |
@@ -748,23 +659,7 @@ |
iter.Advance(); |
} |
- // Otherwise, we're allowed to just terminate the process. Using exit code 0 |
- // means that UMA won't treat this as a renderer crash. |
- process_.Terminate(ResultCodes::NORMAL_EXIT); |
- // On POSIX, we must additionally reap the child. |
-#if defined(OS_POSIX) |
- if (zygote_child_) { |
-#if defined(OS_LINUX) |
- // If the renderer was created via a zygote, we have to proxy the reaping |
- // through the zygote process. |
- Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle()); |
-#endif // defined(OS_LINUX) |
- } else { |
- ProcessWatcher::EnsureProcessGetsReaped(process_.handle()); |
- } |
-#endif // defined(OS_POSIX) |
- process_.Close(); |
- |
+ child_process_.reset(); |
fast_shutdown_started_ = true; |
return true; |
} |
@@ -831,11 +726,8 @@ |
} |
void BrowserRenderProcessHost::ClearTransportDIBCache() { |
- for (std::map<TransportDIB::Id, TransportDIB*>::iterator |
- i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { |
- delete i->second; |
- } |
- |
+ STLDeleteContainerPairSecondPointers( |
+ cached_dibs_.begin(), cached_dibs_.end()); |
cached_dibs_.clear(); |
} |
@@ -844,6 +736,12 @@ |
delete msg; |
return false; |
} |
+ |
+ if (child_process_.get() && child_process_->IsStarting()) { |
+ queued_messages_.push(msg); |
+ return true; |
+ } |
+ |
return channel_->Send(msg); |
} |
@@ -895,49 +793,8 @@ |
} |
void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) { |
- // process_ is not NULL if we created the renderer process |
- if (!process_.handle()) { |
- if (fast_shutdown_started_) { |
- // We terminated the process, but the ChannelConnected task was still |
- // in the queue. We can safely ignore it. |
- return; |
- } else if (base::GetCurrentProcId() == 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(base::GetCurrentProcessHandle()); |
- } else { |
-#if defined(OS_WIN) |
- // 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)); |
-#else |
- NOTREACHED(); |
-#endif |
- DCHECK(process_.handle()); |
- } |
- } 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. |
- if (peer_pid != process_.pid()) { |
- // This check is invalid on Linux for two reasons: |
- // a) If we are running the renderer in a wrapper (with |
- // --renderer-cmd-prefix) then the we'll see the PID of the wrapper |
- // process, not the renderer itself. |
- // b) If we are using the SUID sandbox with CLONE_NEWPID, then the |
- // renderer will be in a new PID namespace and will believe that |
- // it's PID is 2 or 3. |
- // Additionally, this check isn't a security problem on Linux since we |
- // don't use the PID as reported by the renderer. |
-#if !defined(OS_LINUX) |
- CHECK(peer_pid == process_.pid()) << peer_pid << " " << process_.pid(); |
-#endif |
- } |
- mark_child_process_activity_time(); |
- } |
- |
#if defined(IPC_MESSAGE_LOG_ENABLED) |
- bool enabled = IPC::Logging::current()->Enabled(); |
- Send(new ViewMsg_SetIPCLoggingEnabled(enabled)); |
+ Send(new ViewMsg_SetIPCLoggingEnabled(IPC::Logging::current()->Enabled())); |
#endif |
} |
@@ -945,7 +802,7 @@ |
void BrowserRenderProcessHost::BadMessageTerminateProcess( |
uint16 msg_type, base::ProcessHandle process) { |
LOG(ERROR) << "bad message " << msg_type << " terminating renderer."; |
- if (BrowserRenderProcessHost::run_renderer_in_process()) { |
+ if (run_renderer_in_process()) { |
// In single process mode it is better if we don't suicide but just crash. |
CHECK(false); |
} |
@@ -962,42 +819,17 @@ |
if (!channel_.get()) |
return; |
- bool child_exited; |
- bool did_crash; |
- if (!process_.handle()) { |
- // The process has been terminated (likely FastShutdownIfPossible). |
- did_crash = false; |
- child_exited = true; |
- } else if (zygote_child_) { |
-#if defined(OS_LINUX) |
- did_crash = Singleton<ZygoteHost>()->DidProcessCrash( |
- process_.handle(), &child_exited); |
-#else |
- NOTREACHED(); |
- did_crash = true; |
-#endif |
- } else { |
- did_crash = base::DidProcessCrash(&child_exited, process_.handle()); |
- } |
+ // NULL in single process mode or if fast termination happened. |
+ bool did_crash = |
+ child_process_.get() ? child_process_->DidProcessCrash() : false; |
NotificationService::current()->Notify( |
NotificationType::RENDERER_PROCESS_CLOSED, |
Source<RenderProcessHost>(this), |
Details<bool>(&did_crash)); |
- // POSIX: If the process crashed, then the kernel closed the socket for it |
- // and so the child has already died by the time we get here. Since |
- // DidProcessCrash called waitpid with WNOHANG, it'll reap the process. |
- // However, if DidProcessCrash didn't reap the child, we'll need to in |
- // ~BrowserRenderProcessHost via ProcessWatcher. So we can't close the handle |
- // here. |
- // |
- // This is moot on Windows where |child_exited| will always be true. |
- if (child_exited) |
- process_.Close(); |
- |
WebCacheManager::GetInstance()->Remove(id()); |
- |
+ child_process_.reset(); |
channel_.reset(); |
IDMap<IPC::Channel::Listener>::iterator iter(&listeners_); |
@@ -1035,32 +867,27 @@ |
} |
void BrowserRenderProcessHost::SetBackgrounded(bool backgrounded) { |
- // If the process_ is NULL, the process hasn't been created yet. |
- if (process_.handle()) { |
- bool should_set_backgrounded = true; |
- |
-#if defined(OS_WIN) |
- // The cbstext.dll loads as a global GetMessage hook in the browser process |
- // and intercepts/unintercepts the kernel32 API SetPriorityClass in a |
- // background thread. If the UI thread invokes this API just when it is |
- // intercepted the stack is messed up on return from the interceptor |
- // which causes random crashes in the browser process. Our hack for now |
- // is to not invoke the SetPriorityClass API if the dll is loaded. |
- should_set_backgrounded = (GetModuleHandle(L"cbstext.dll") == NULL); |
-#endif // OS_WIN |
- |
- if (should_set_backgrounded) { |
- process_.SetProcessBackgrounded(backgrounded); |
- } |
- } |
- |
// 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; |
+ if (!child_process_.get() || child_process_->IsStarting()) |
+ return; |
+ |
+#if defined(OS_WIN) |
+ // The cbstext.dll loads as a global GetMessage hook in the browser process |
+ // and intercepts/unintercepts the kernel32 API SetPriorityClass in a |
+ // background thread. If the UI thread invokes this API just when it is |
+ // intercepted the stack is messed up on return from the interceptor |
+ // which causes random crashes in the browser process. Our hack for now |
+ // is to not invoke the SetPriorityClass API if the dll is loaded. |
+ if (GetModuleHandle(L"cbstext.dll")) |
+ return; |
+#endif // OS_WIN |
+ |
+ child_process_->SetProcessBackgrounded(backgrounded); |
} |
-// NotificationObserver implementation. |
void BrowserRenderProcessHost::Observe(NotificationType type, |
const NotificationSource& source, |
const NotificationDetails& details) { |
@@ -1103,6 +930,35 @@ |
} |
} |
+void BrowserRenderProcessHost::OnProcessLaunched() { |
+ // Now that the process is created, set its backgrounding accordingly. |
+ SetBackgrounded(backgrounded_); |
+ |
+ InitVisitedLinks(); |
+ InitUserScripts(); |
+ InitExtensions(); |
+#if defined(SPELLCHECKER_IN_RENDERER) |
+ // We don't want to initialize the spellchecker unless SpellCheckHost has been |
+ // created. In InitSpellChecker(), we know if GetSpellCheckHost() is NULL |
+ // then the spellchecker has been turned off, but here, we don't know if |
+ // it's been turned off or just not loaded yet. |
+ if (profile()->GetSpellCheckHost()) |
+ InitSpellChecker(); |
+#endif |
+ |
+ if (max_page_id_ != -1) |
+ Send(new ViewMsg_SetNextPageID(max_page_id_ + 1)); |
+ |
+ while (!queued_messages_.empty()) { |
+ Send(queued_messages_.front()); |
+ queued_messages_.pop(); |
+ } |
+ |
+ NotificationService::current()->Notify( |
+ NotificationType::RENDERER_PROCESS_CREATED, |
+ Source<RenderProcessHost>(this), NotificationService::NoDetails()); |
+} |
+ |
void BrowserRenderProcessHost::OnExtensionAddListener( |
const std::string& event_name) { |
if (profile()->GetExtensionMessageService()) { |