[content shell] hook up testRunner.dumpEditingCallbacks
[chromium-blink-merge.git] / content / common / child_thread.cc
blob8b1b000eb088433784789796d4147d55afeac3cb
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.
5 #include "content/common/child_thread.h"
7 #include "base/allocator/allocator_extension.h"
8 #include "base/command_line.h"
9 #include "base/message_loop.h"
10 #include "base/process.h"
11 #include "base/process_util.h"
12 #include "base/string_util.h"
13 #include "base/tracked_objects.h"
14 #include "content/common/child_histogram_message_filter.h"
15 #include "content/common/child_process.h"
16 #include "content/common/child_process_messages.h"
17 #include "content/common/fileapi/file_system_dispatcher.h"
18 #include "content/common/quota_dispatcher.h"
19 #include "content/common/resource_dispatcher.h"
20 #include "content/common/socket_stream_dispatcher.h"
21 #include "content/components/tracing/child_trace_message_filter.h"
22 #include "content/public/common/content_switches.h"
23 #include "ipc/ipc_logging.h"
24 #include "ipc/ipc_switches.h"
25 #include "ipc/ipc_sync_channel.h"
26 #include "ipc/ipc_sync_message_filter.h"
27 #include "webkit/glue/webkit_glue.h"
29 #if defined(OS_WIN)
30 #include "content/common/handle_enumerator_win.h"
31 #endif
33 using tracked_objects::ThreadData;
35 namespace content {
36 namespace {
38 // How long to wait for a connection to the browser process before giving up.
39 const int kConnectionTimeoutS = 15;
41 // This isn't needed on Windows because there the sandbox's job object
42 // terminates child processes automatically. For unsandboxed processes (i.e.
43 // plugins), PluginThread has EnsureTerminateMessageFilter.
44 #if defined(OS_POSIX)
46 class SuicideOnChannelErrorFilter : public IPC::ChannelProxy::MessageFilter {
47 public:
48 // IPC::ChannelProxy::MessageFilter
49 virtual void OnChannelError() OVERRIDE {
50 // For renderer/worker processes:
51 // On POSIX, at least, one can install an unload handler which loops
52 // forever and leave behind a renderer process which eats 100% CPU forever.
54 // This is because the terminate signals (ViewMsg_ShouldClose and the error
55 // from the IPC channel) are routed to the main message loop but never
56 // processed (because that message loop is stuck in V8).
58 // One could make the browser SIGKILL the renderers, but that leaves open a
59 // large window where a browser failure (or a user, manually terminating
60 // the browser because "it's stuck") will leave behind a process eating all
61 // the CPU.
63 // So, we install a filter on the channel so that we can process this event
64 // here and kill the process.
66 // We want to kill this process after giving it 30 seconds to run the exit
67 // handlers. SIGALRM has a default disposition of terminating the
68 // application.
69 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kChildCleanExit))
70 alarm(30);
71 else
72 _exit(0);
75 protected:
76 virtual ~SuicideOnChannelErrorFilter() {}
79 #endif // OS(POSIX)
81 } // namespace
83 ChildThread::ChildThread()
84 : ALLOW_THIS_IN_INITIALIZER_LIST(channel_connected_factory_(this)) {
85 channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
86 switches::kProcessChannelID);
87 Init();
90 ChildThread::ChildThread(const std::string& channel_name)
91 : channel_name_(channel_name),
92 ALLOW_THIS_IN_INITIALIZER_LIST(channel_connected_factory_(this)) {
93 Init();
96 void ChildThread::Init() {
97 on_channel_error_called_ = false;
98 message_loop_ = MessageLoop::current();
99 channel_.reset(new IPC::SyncChannel(channel_name_,
100 IPC::Channel::MODE_CLIENT, this,
101 ChildProcess::current()->io_message_loop_proxy(), true,
102 ChildProcess::current()->GetShutDownEvent()));
103 #ifdef IPC_MESSAGE_LOG_ENABLED
104 IPC::Logging::GetInstance()->SetIPCSender(this);
105 #endif
107 resource_dispatcher_.reset(new ResourceDispatcher(this));
108 socket_stream_dispatcher_.reset(new SocketStreamDispatcher());
109 file_system_dispatcher_.reset(new FileSystemDispatcher());
110 quota_dispatcher_.reset(new QuotaDispatcher());
112 sync_message_filter_ =
113 new IPC::SyncMessageFilter(ChildProcess::current()->GetShutDownEvent());
114 histogram_message_filter_ = new ChildHistogramMessageFilter();
116 channel_->AddFilter(histogram_message_filter_.get());
117 channel_->AddFilter(sync_message_filter_.get());
118 channel_->AddFilter(new ChildTraceMessageFilter(
119 ChildProcess::current()->io_message_loop_proxy()));
121 #if defined(OS_POSIX)
122 // Check that --process-type is specified so we don't do this in unit tests
123 // and single-process mode.
124 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType))
125 channel_->AddFilter(new SuicideOnChannelErrorFilter());
126 #endif
128 MessageLoop::current()->PostDelayedTask(
129 FROM_HERE,
130 base::Bind(&ChildThread::EnsureConnected,
131 channel_connected_factory_.GetWeakPtr()),
132 base::TimeDelta::FromSeconds(kConnectionTimeoutS));
135 ChildThread::~ChildThread() {
136 #ifdef IPC_MESSAGE_LOG_ENABLED
137 IPC::Logging::GetInstance()->SetIPCSender(NULL);
138 #endif
140 channel_->RemoveFilter(histogram_message_filter_.get());
141 channel_->RemoveFilter(sync_message_filter_.get());
143 // The ChannelProxy object caches a pointer to the IPC thread, so need to
144 // reset it as it's not guaranteed to outlive this object.
145 // NOTE: this also has the side-effect of not closing the main IPC channel to
146 // the browser process. This is needed because this is the signal that the
147 // browser uses to know that this process has died, so we need it to be alive
148 // until this process is shut down, and the OS closes the handle
149 // automatically. We used to watch the object handle on Windows to do this,
150 // but it wasn't possible to do so on POSIX.
151 channel_->ClearIPCTaskRunner();
154 void ChildThread::OnChannelConnected(int32 peer_pid) {
155 channel_connected_factory_.InvalidateWeakPtrs();
158 void ChildThread::OnChannelError() {
159 set_on_channel_error_called(true);
160 MessageLoop::current()->Quit();
163 bool ChildThread::Send(IPC::Message* msg) {
164 DCHECK(MessageLoop::current() == message_loop());
165 if (!channel_.get()) {
166 delete msg;
167 return false;
170 return channel_->Send(msg);
173 void ChildThread::AddRoute(int32 routing_id, IPC::Listener* listener) {
174 DCHECK(MessageLoop::current() == message_loop());
176 router_.AddRoute(routing_id, listener);
179 void ChildThread::RemoveRoute(int32 routing_id) {
180 DCHECK(MessageLoop::current() == message_loop());
182 router_.RemoveRoute(routing_id);
185 IPC::Listener* ChildThread::ResolveRoute(int32 routing_id) {
186 DCHECK(MessageLoop::current() == message_loop());
188 return router_.ResolveRoute(routing_id);
191 webkit_glue::ResourceLoaderBridge* ChildThread::CreateBridge(
192 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) {
193 return resource_dispatcher()->CreateBridge(request_info);
196 base::SharedMemory* ChildThread::AllocateSharedMemory(
197 size_t buf_size) {
198 scoped_ptr<base::SharedMemory> shared_buf;
199 #if defined(OS_WIN)
200 shared_buf.reset(new base::SharedMemory);
201 if (!shared_buf->CreateAndMapAnonymous(buf_size)) {
202 NOTREACHED();
203 return NULL;
205 #else
206 // On POSIX, we need to ask the browser to create the shared memory for us,
207 // since this is blocked by the sandbox.
208 base::SharedMemoryHandle shared_mem_handle;
209 if (Send(new ChildProcessHostMsg_SyncAllocateSharedMemory(
210 buf_size, &shared_mem_handle))) {
211 if (base::SharedMemory::IsHandleValid(shared_mem_handle)) {
212 shared_buf.reset(new base::SharedMemory(shared_mem_handle, false));
213 if (!shared_buf->Map(buf_size)) {
214 NOTREACHED() << "Map failed";
215 return NULL;
217 } else {
218 NOTREACHED() << "Browser failed to allocate shared memory";
219 return NULL;
221 } else {
222 NOTREACHED() << "Browser allocation request message failed";
223 return NULL;
225 #endif
226 return shared_buf.release();
229 ResourceDispatcher* ChildThread::resource_dispatcher() {
230 return resource_dispatcher_.get();
233 IPC::SyncMessageFilter* ChildThread::sync_message_filter() {
234 return sync_message_filter_;
237 MessageLoop* ChildThread::message_loop() {
238 return message_loop_;
241 bool ChildThread::OnMessageReceived(const IPC::Message& msg) {
242 // Resource responses are sent to the resource dispatcher.
243 if (resource_dispatcher_->OnMessageReceived(msg))
244 return true;
245 if (socket_stream_dispatcher_->OnMessageReceived(msg))
246 return true;
247 if (file_system_dispatcher_->OnMessageReceived(msg))
248 return true;
249 if (quota_dispatcher_->OnMessageReceived(msg))
250 return true;
252 bool handled = true;
253 IPC_BEGIN_MESSAGE_MAP(ChildThread, msg)
254 IPC_MESSAGE_HANDLER(ChildProcessMsg_Shutdown, OnShutdown)
255 #if defined(IPC_MESSAGE_LOG_ENABLED)
256 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetIPCLoggingEnabled,
257 OnSetIPCLoggingEnabled)
258 #endif
259 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetProfilerStatus,
260 OnSetProfilerStatus)
261 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetChildProfilerData,
262 OnGetChildProfilerData)
263 IPC_MESSAGE_HANDLER(ChildProcessMsg_DumpHandles, OnDumpHandles)
264 #if defined(USE_TCMALLOC)
265 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetTcmallocStats, OnGetTcmallocStats)
266 #endif
267 IPC_MESSAGE_UNHANDLED(handled = false)
268 IPC_END_MESSAGE_MAP()
270 if (handled)
271 return true;
273 if (msg.routing_id() == MSG_ROUTING_CONTROL)
274 return OnControlMessageReceived(msg);
276 return router_.OnMessageReceived(msg);
279 bool ChildThread::OnControlMessageReceived(const IPC::Message& msg) {
280 return false;
283 void ChildThread::OnShutdown() {
284 MessageLoop::current()->Quit();
287 #if defined(IPC_MESSAGE_LOG_ENABLED)
288 void ChildThread::OnSetIPCLoggingEnabled(bool enable) {
289 if (enable)
290 IPC::Logging::GetInstance()->Enable();
291 else
292 IPC::Logging::GetInstance()->Disable();
294 #endif // IPC_MESSAGE_LOG_ENABLED
296 void ChildThread::OnSetProfilerStatus(ThreadData::Status status) {
297 ThreadData::InitializeAndSetTrackingStatus(status);
300 void ChildThread::OnGetChildProfilerData(int sequence_number) {
301 tracked_objects::ProcessDataSnapshot process_data;
302 ThreadData::Snapshot(false, &process_data);
304 Send(new ChildProcessHostMsg_ChildProfilerData(sequence_number,
305 process_data));
308 void ChildThread::OnDumpHandles() {
309 #if defined(OS_WIN)
310 scoped_refptr<HandleEnumerator> handle_enum(
311 new HandleEnumerator(
312 CommandLine::ForCurrentProcess()->HasSwitch(
313 switches::kAuditAllHandles)));
314 handle_enum->EnumerateHandles();
315 Send(new ChildProcessHostMsg_DumpHandlesDone);
316 return;
317 #endif
319 NOTIMPLEMENTED();
322 #if defined(USE_TCMALLOC)
323 void ChildThread::OnGetTcmallocStats() {
324 std::string result;
325 char buffer[1024 * 32];
326 base::allocator::GetStats(buffer, sizeof(buffer));
327 result.append(buffer);
328 Send(new ChildProcessHostMsg_TcmallocStats(result));
330 #endif
332 ChildThread* ChildThread::current() {
333 return ChildProcess::current()->main_thread();
336 bool ChildThread::IsWebFrameValid(WebKit::WebFrame* frame) {
337 // Return false so that it is overridden in any process in which it is used.
338 return false;
341 void ChildThread::OnProcessFinalRelease() {
342 if (on_channel_error_called_) {
343 MessageLoop::current()->Quit();
344 return;
347 // The child process shutdown sequence is a request response based mechanism,
348 // where we send out an initial feeler request to the child process host
349 // instance in the browser to verify if it's ok to shutdown the child process.
350 // The browser then sends back a response if it's ok to shutdown. This avoids
351 // race conditions if the process refcount is 0 but there's an IPC message
352 // inflight that would addref it.
353 Send(new ChildProcessHostMsg_ShutdownRequest);
356 void ChildThread::EnsureConnected() {
357 LOG(INFO) << "ChildThread::EnsureConnected()";
358 base::KillProcess(base::GetCurrentProcessHandle(), 0, false);
361 } // namespace content