blob: b6e1094798a9b5a9cdcff270a7465c4509b77ceb [file] [log] [blame]
// Copyright (c) 2012 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 "content/browser/browser_child_process_host_impl.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/stl_util.h"
#include "base/string_util.h"
#include "content/browser/histogram_message_filter.h"
#include "content/browser/profiler_message_filter.h"
#include "content/browser/renderer_host/resource_message_filter.h"
#include "content/browser/trace_message_filter.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/plugin_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browser_child_process_host_delegate.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#if defined(OS_MACOSX)
#include "content/browser/mach_broker_mac.h"
#endif
namespace content {
namespace {
static base::LazyInstance<BrowserChildProcessHostImpl::BrowserChildProcessList>
g_child_process_list = LAZY_INSTANCE_INITIALIZER;
// Helper functions since the child process related notifications happen on the
// UI thread.
void ChildNotificationHelper(int notification_type,
const ChildProcessData& data) {
NotificationService::current()->Notify(
notification_type, NotificationService::AllSources(),
Details<const ChildProcessData>(&data));
}
} // namespace
BrowserChildProcessHost* BrowserChildProcessHost::Create(
ProcessType type,
BrowserChildProcessHostDelegate* delegate) {
return new BrowserChildProcessHostImpl(type, delegate);
}
#if defined(OS_MACOSX)
base::ProcessMetrics::PortProvider* BrowserChildProcessHost::GetPortProvider() {
return MachBroker::GetInstance();
}
#endif
BrowserChildProcessHostImpl::BrowserChildProcessList*
BrowserChildProcessHostImpl::GetIterator() {
return g_child_process_list.Pointer();
}
BrowserChildProcessHostImpl::BrowserChildProcessHostImpl(
ProcessType type,
BrowserChildProcessHostDelegate* delegate)
: data_(type),
delegate_(delegate) {
data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
child_process_host_.reset(ChildProcessHost::Create(this));
child_process_host_->AddFilter(new TraceMessageFilter);
child_process_host_->AddFilter(new ProfilerMessageFilter(type));
child_process_host_->AddFilter(new HistogramMessageFilter());
g_child_process_list.Get().push_back(this);
GetContentClient()->browser()->BrowserChildProcessHostCreated(this);
}
BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() {
g_child_process_list.Get().remove(this);
}
// static
void BrowserChildProcessHostImpl::TerminateAll() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Make a copy since the BrowserChildProcessHost dtor mutates the original
// list.
BrowserChildProcessList copy = g_child_process_list.Get();
for (BrowserChildProcessList::iterator it = copy.begin();
it != copy.end(); ++it) {
delete (*it)->delegate(); // ~*HostDelegate deletes *HostImpl.
}
}
void BrowserChildProcessHostImpl::Launch(
#if defined(OS_WIN)
const FilePath& exposed_dir,
#elif defined(OS_POSIX)
bool use_zygote,
const base::EnvironmentVector& environ,
#endif
CommandLine* cmd_line) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
GetContentClient()->browser()->AppendExtraCommandLineSwitches(
cmd_line, data_.id);
const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
static const char* kForwardSwitches[] = {
#if defined(OS_POSIX)
switches::kChildCleanExit,
#endif
switches::kDisableLogging,
switches::kEnableDCHECK,
switches::kEnableLogging,
switches::kLoggingLevel,
switches::kV,
switches::kVModule,
};
cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches,
arraysize(kForwardSwitches));
child_process_.reset(new ChildProcessLauncher(
#if defined(OS_WIN)
exposed_dir,
#elif defined(OS_POSIX)
use_zygote,
environ,
child_process_host_->TakeClientFileDescriptor(),
#endif
cmd_line,
data_.id,
this));
}
const ChildProcessData& BrowserChildProcessHostImpl::GetData() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return data_;
}
ChildProcessHost* BrowserChildProcessHostImpl::GetHost() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return child_process_host_.get();
}
base::ProcessHandle BrowserChildProcessHostImpl::GetHandle() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(child_process_.get())
<< "Requesting a child process handle before launching.";
DCHECK(child_process_->GetHandle())
<< "Requesting a child process handle before launch has completed OK.";
return child_process_->GetHandle();
}
void BrowserChildProcessHostImpl::SetName(const string16& name) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
data_.name = name;
}
void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
data_.handle = handle;
}
void BrowserChildProcessHostImpl::ForceShutdown() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
g_child_process_list.Get().remove(this);
child_process_host_->ForceShutdown();
}
void BrowserChildProcessHostImpl::SetTerminateChildOnShutdown(
bool terminate_on_shutdown) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown);
}
void BrowserChildProcessHostImpl::Notify(int type) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&ChildNotificationHelper, type, data_));
}
base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus(
int* exit_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!child_process_.get()) // If the delegate doesn't use Launch() helper.
return base::GetTerminationStatus(data_.handle, exit_code);
return child_process_->GetChildTerminationStatus(exit_code);
}
bool BrowserChildProcessHostImpl::OnMessageReceived(
const IPC::Message& message) {
return delegate_->OnMessageReceived(message);
}
void BrowserChildProcessHostImpl::OnChannelConnected(int32 peer_pid) {
Notify(NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED);
delegate_->OnChannelConnected(peer_pid);
}
void BrowserChildProcessHostImpl::OnChannelError() {
delegate_->OnChannelError();
}
bool BrowserChildProcessHostImpl::CanShutdown() {
return delegate_->CanShutdown();
}
void BrowserChildProcessHostImpl::OnChildDisconnected() {
DCHECK(data_.handle != base::kNullProcessHandle);
int exit_code;
base::TerminationStatus status = GetTerminationStatus(&exit_code);
switch (status) {
case base::TERMINATION_STATUS_PROCESS_CRASHED:
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: {
delegate_->OnProcessCrashed(exit_code);
// Report that this child process crashed.
Notify(NOTIFICATION_CHILD_PROCESS_CRASHED);
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed",
data_.type,
PROCESS_TYPE_MAX);
break;
}
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
delegate_->OnProcessCrashed(exit_code);
// Report that this child process was killed.
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed",
data_.type,
PROCESS_TYPE_MAX);
break;
}
case base::TERMINATION_STATUS_STILL_RUNNING: {
UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive",
data_.type,
PROCESS_TYPE_MAX);
}
default:
break;
}
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected",
data_.type,
PROCESS_TYPE_MAX);
// Notify in the main loop of the disconnection.
Notify(NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED);
delete delegate_; // Will delete us
}
bool BrowserChildProcessHostImpl::Send(IPC::Message* message) {
return child_process_host_->Send(message);
}
void BrowserChildProcessHostImpl::OnProcessLaunched() {
if (!child_process_->GetHandle()) {
delete delegate_; // Will delete us
return;
}
data_.handle = child_process_->GetHandle();
delegate_->OnProcessLaunched();
}
} // namespace content