1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/browser_child_process_host_impl.h"
6
7#include "base/base_switches.h"
8#include "base/bind.h"
9#include "base/command_line.h"
10#include "base/files/file_path.h"
11#include "base/lazy_instance.h"
12#include "base/logging.h"
13#include "base/metrics/histogram.h"
14#include "base/path_service.h"
15#include "base/stl_util.h"
16#include "base/strings/string_util.h"
17#include "base/synchronization/waitable_event.h"
18#include "content/browser/histogram_message_filter.h"
19#include "content/browser/loader/resource_message_filter.h"
20#include "content/browser/profiler_message_filter.h"
21#include "content/browser/tracing/trace_message_filter.h"
22#include "content/common/child_process_host_impl.h"
23#include "content/public/browser/browser_child_process_host_delegate.h"
24#include "content/public/browser/browser_child_process_observer.h"
25#include "content/public/browser/browser_thread.h"
26#include "content/public/browser/child_process_data.h"
27#include "content/public/browser/content_browser_client.h"
28#include "content/public/common/content_switches.h"
29#include "content/public/common/process_type.h"
30#include "content/public/common/result_codes.h"
31
32#if defined(OS_MACOSX)
33#include "content/browser/mach_broker_mac.h"
34#endif
35
36namespace content {
37namespace {
38
39static base::LazyInstance<BrowserChildProcessHostImpl::BrowserChildProcessList>
40    g_child_process_list = LAZY_INSTANCE_INITIALIZER;
41
42base::LazyInstance<ObserverList<BrowserChildProcessObserver> >
43    g_observers = LAZY_INSTANCE_INITIALIZER;
44
45void NotifyProcessHostConnected(const ChildProcessData& data) {
46  FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
47                    BrowserChildProcessHostConnected(data));
48}
49
50void NotifyProcessHostDisconnected(const ChildProcessData& data) {
51  FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
52                    BrowserChildProcessHostDisconnected(data));
53}
54
55void NotifyProcessCrashed(const ChildProcessData& data) {
56  FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
57                    BrowserChildProcessCrashed(data));
58}
59
60}  // namespace
61
62BrowserChildProcessHost* BrowserChildProcessHost::Create(
63    int process_type,
64    BrowserChildProcessHostDelegate* delegate) {
65  return new BrowserChildProcessHostImpl(process_type, delegate);
66}
67
68#if defined(OS_MACOSX)
69base::ProcessMetrics::PortProvider* BrowserChildProcessHost::GetPortProvider() {
70  return MachBroker::GetInstance();
71}
72#endif
73
74// static
75BrowserChildProcessHostImpl::BrowserChildProcessList*
76    BrowserChildProcessHostImpl::GetIterator() {
77  return g_child_process_list.Pointer();
78}
79
80// static
81void BrowserChildProcessHostImpl::AddObserver(
82    BrowserChildProcessObserver* observer) {
83  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
84  g_observers.Get().AddObserver(observer);
85}
86
87// static
88void BrowserChildProcessHostImpl::RemoveObserver(
89    BrowserChildProcessObserver* observer) {
90  // TODO(phajdan.jr): Check thread after fixing http://crbug.com/167126.
91  g_observers.Get().RemoveObserver(observer);
92}
93
94BrowserChildProcessHostImpl::BrowserChildProcessHostImpl(
95    int process_type,
96    BrowserChildProcessHostDelegate* delegate)
97    : data_(process_type),
98      delegate_(delegate),
99      power_monitor_message_broadcaster_(this) {
100  data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
101
102  child_process_host_.reset(ChildProcessHost::Create(this));
103  AddFilter(new TraceMessageFilter);
104  AddFilter(new ProfilerMessageFilter(process_type));
105  AddFilter(new HistogramMessageFilter);
106
107  g_child_process_list.Get().push_back(this);
108  GetContentClient()->browser()->BrowserChildProcessHostCreated(this);
109
110  power_monitor_message_broadcaster_.Init();
111}
112
113BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() {
114  g_child_process_list.Get().remove(this);
115
116#if defined(OS_WIN)
117  DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent());
118#endif
119}
120
121// static
122void BrowserChildProcessHostImpl::TerminateAll() {
123  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
124  // Make a copy since the BrowserChildProcessHost dtor mutates the original
125  // list.
126  BrowserChildProcessList copy = g_child_process_list.Get();
127  for (BrowserChildProcessList::iterator it = copy.begin();
128       it != copy.end(); ++it) {
129    delete (*it)->delegate();  // ~*HostDelegate deletes *HostImpl.
130  }
131}
132
133void BrowserChildProcessHostImpl::Launch(
134    SandboxedProcessLauncherDelegate* delegate,
135    base::CommandLine* cmd_line) {
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
137
138  GetContentClient()->browser()->AppendExtraCommandLineSwitches(
139      cmd_line, data_.id);
140
141  const base::CommandLine& browser_command_line =
142      *base::CommandLine::ForCurrentProcess();
143  static const char* kForwardSwitches[] = {
144    switches::kDisableLogging,
145    switches::kEnableLogging,
146    switches::kIPCConnectionTimeout,
147    switches::kLoggingLevel,
148    switches::kTraceToConsole,
149    switches::kV,
150    switches::kVModule,
151  };
152  cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches,
153                             arraysize(kForwardSwitches));
154
155  child_process_.reset(new ChildProcessLauncher(
156      delegate,
157      cmd_line,
158      data_.id,
159      this));
160}
161
162const ChildProcessData& BrowserChildProcessHostImpl::GetData() const {
163  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
164  return data_;
165}
166
167ChildProcessHost* BrowserChildProcessHostImpl::GetHost() const {
168  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
169  return child_process_host_.get();
170}
171
172base::ProcessHandle BrowserChildProcessHostImpl::GetHandle() const {
173  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
174  DCHECK(child_process_.get())
175      << "Requesting a child process handle before launching.";
176  DCHECK(child_process_->GetHandle())
177      << "Requesting a child process handle before launch has completed OK.";
178  return child_process_->GetHandle();
179}
180
181void BrowserChildProcessHostImpl::SetName(const base::string16& name) {
182  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
183  data_.name = name;
184}
185
186void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) {
187  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
188  data_.handle = handle;
189}
190
191void BrowserChildProcessHostImpl::ForceShutdown() {
192  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
193  g_child_process_list.Get().remove(this);
194  child_process_host_->ForceShutdown();
195}
196
197void BrowserChildProcessHostImpl::SetBackgrounded(bool backgrounded) {
198  child_process_->SetProcessBackgrounded(backgrounded);
199}
200
201void BrowserChildProcessHostImpl::SetTerminateChildOnShutdown(
202    bool terminate_on_shutdown) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204  child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown);
205}
206
207void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) {
208  child_process_host_->AddFilter(filter->GetFilter());
209}
210
211void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated(
212    const ChildProcessData& data) {
213  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214  FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
215                    BrowserChildProcessInstanceCreated(data));
216}
217
218void BrowserChildProcessHostImpl::HistogramBadMessageTerminated(
219    int process_type) {
220  UMA_HISTOGRAM_ENUMERATION("ChildProcess.BadMessgeTerminated", process_type,
221                            PROCESS_TYPE_MAX);
222}
223
224base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus(
225    bool known_dead, int* exit_code) {
226  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227  if (!child_process_)  // If the delegate doesn't use Launch() helper.
228    return base::GetTerminationStatus(data_.handle, exit_code);
229  return child_process_->GetChildTerminationStatus(known_dead,
230                                                   exit_code);
231}
232
233bool BrowserChildProcessHostImpl::OnMessageReceived(
234    const IPC::Message& message) {
235  return delegate_->OnMessageReceived(message);
236}
237
238void BrowserChildProcessHostImpl::OnChannelConnected(int32 peer_pid) {
239#if defined(OS_WIN)
240  // From this point onward, the exit of the child process is detected by an
241  // error on the IPC channel.
242  DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent());
243  early_exit_watcher_.StopWatching();
244#endif
245
246  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
247  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
248                          base::Bind(&NotifyProcessHostConnected, data_));
249
250  delegate_->OnChannelConnected(peer_pid);
251}
252
253void BrowserChildProcessHostImpl::OnChannelError() {
254  delegate_->OnChannelError();
255}
256
257void BrowserChildProcessHostImpl::OnBadMessageReceived(
258    const IPC::Message& message) {
259  HistogramBadMessageTerminated(data_.process_type);
260  if (CommandLine::ForCurrentProcess()->HasSwitch(
261          switches::kDisableKillAfterBadIPC)) {
262    return;
263  }
264  base::KillProcess(GetHandle(), RESULT_CODE_KILLED_BAD_MESSAGE, false);
265}
266
267bool BrowserChildProcessHostImpl::CanShutdown() {
268  return delegate_->CanShutdown();
269}
270
271void BrowserChildProcessHostImpl::OnChildDisconnected() {
272  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273  if (child_process_.get() || data_.handle) {
274    DCHECK(data_.handle != base::kNullProcessHandle);
275    int exit_code;
276    base::TerminationStatus status = GetTerminationStatus(
277        true /* known_dead */, &exit_code);
278    switch (status) {
279      case base::TERMINATION_STATUS_PROCESS_CRASHED:
280      case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: {
281        delegate_->OnProcessCrashed(exit_code);
282        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
283                                base::Bind(&NotifyProcessCrashed, data_));
284        UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed2",
285                                  data_.process_type,
286                                  PROCESS_TYPE_MAX);
287        break;
288      }
289      case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
290        delegate_->OnProcessCrashed(exit_code);
291        // Report that this child process was killed.
292        UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2",
293                                  data_.process_type,
294                                  PROCESS_TYPE_MAX);
295        break;
296      }
297      case base::TERMINATION_STATUS_STILL_RUNNING: {
298        UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive2",
299                                  data_.process_type,
300                                  PROCESS_TYPE_MAX);
301      }
302      default:
303        break;
304    }
305    UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected2",
306                              data_.process_type,
307                              PROCESS_TYPE_MAX);
308  }
309  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
310                          base::Bind(&NotifyProcessHostDisconnected, data_));
311  delete delegate_;  // Will delete us
312}
313
314bool BrowserChildProcessHostImpl::Send(IPC::Message* message) {
315  return child_process_host_->Send(message);
316}
317
318void BrowserChildProcessHostImpl::OnProcessLaunchFailed() {
319  delegate_->OnProcessLaunchFailed();
320  delete delegate_;  // Will delete us
321}
322
323void BrowserChildProcessHostImpl::OnProcessLaunched() {
324  base::ProcessHandle handle = child_process_->GetHandle();
325  if (!handle) {
326    delete delegate_;  // Will delete us
327    return;
328  }
329
330#if defined(OS_WIN)
331  // Start a WaitableEventWatcher that will invoke OnProcessExitedEarly if the
332  // child process exits. This watcher is stopped once the IPC channel is
333  // connected and the exit of the child process is detecter by an error on the
334  // IPC channel thereafter.
335  DCHECK(!early_exit_watcher_.GetWatchedEvent());
336  early_exit_watcher_.StartWatching(
337      new base::WaitableEvent(handle),
338      base::Bind(&BrowserChildProcessHostImpl::OnProcessExitedEarly,
339                 base::Unretained(this)));
340#endif
341
342  data_.handle = handle;
343  delegate_->OnProcessLaunched();
344}
345
346#if defined(OS_WIN)
347
348void BrowserChildProcessHostImpl::DeleteProcessWaitableEvent(
349    base::WaitableEvent* event) {
350  if (!event)
351    return;
352
353  // The WaitableEvent does not own the process handle so ensure it does not
354  // close it.
355  event->Release();
356
357  delete event;
358}
359
360void BrowserChildProcessHostImpl::OnProcessExitedEarly(
361    base::WaitableEvent* event) {
362  DeleteProcessWaitableEvent(event);
363  OnChildDisconnected();
364}
365
366#endif
367
368}  // namespace content
369