1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
// Copyright (c) 2009 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 "chrome/common/child_thread.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/command_line.h"
#include "chrome/common/child_process.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/file_system/file_system_dispatcher.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/common/resource_dispatcher.h"
#include "chrome/common/socket_stream_dispatcher.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_sync_channel.h"
#include "ipc/ipc_sync_message_filter.h"
#include "ipc/ipc_switches.h"
#include "webkit/glue/webkit_glue.h"
ChildThread::ChildThread() {
channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kProcessChannelID);
Init();
}
ChildThread::ChildThread(const std::string& channel_name)
: channel_name_(channel_name) {
Init();
}
void ChildThread::Init() {
check_with_browser_before_shutdown_ = false;
on_channel_error_called_ = false;
message_loop_ = MessageLoop::current();
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserAgent)) {
webkit_glue::SetUserAgent(
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kUserAgent));
}
channel_.reset(new IPC::SyncChannel(channel_name_,
IPC::Channel::MODE_CLIENT, this,
ChildProcess::current()->io_message_loop(), true,
ChildProcess::current()->GetShutDownEvent()));
#ifdef IPC_MESSAGE_LOG_ENABLED
IPC::Logging::GetInstance()->SetIPCSender(this);
#endif
resource_dispatcher_.reset(new ResourceDispatcher(this));
socket_stream_dispatcher_.reset(new SocketStreamDispatcher());
file_system_dispatcher_.reset(new FileSystemDispatcher());
sync_message_filter_ =
new IPC::SyncMessageFilter(ChildProcess::current()->GetShutDownEvent());
channel_->AddFilter(sync_message_filter_.get());
// When running in unit tests, there is already a NotificationService object.
// Since only one can exist at a time per thread, check first.
if (!NotificationService::current())
notification_service_.reset(new NotificationService);
}
ChildThread::~ChildThread() {
#ifdef IPC_MESSAGE_LOG_ENABLED
IPC::Logging::GetInstance()->SetIPCSender(NULL);
#endif
channel_->RemoveFilter(sync_message_filter_.get());
// Close this channel before resetting the message loop attached to it so
// the message loop can call ChannelProxy::Context::OnChannelClosed(), which
// releases the reference count to this channel.
channel_->Close();
// The ChannelProxy object caches a pointer to the IPC thread, so need to
// reset it as it's not guaranteed to outlive this object.
// NOTE: this also has the side-effect of not closing the main IPC channel to
// the browser process. This is needed because this is the signal that the
// browser uses to know that this process has died, so we need it to be alive
// until this process is shut down, and the OS closes the handle
// automatically. We used to watch the object handle on Windows to do this,
// but it wasn't possible to do so on POSIX.
channel_->ClearIPCMessageLoop();
}
void ChildThread::OnChannelError() {
set_on_channel_error_called(true);
MessageLoop::current()->Quit();
}
bool ChildThread::Send(IPC::Message* msg) {
if (!channel_.get()) {
delete msg;
return false;
}
return channel_->Send(msg);
}
void ChildThread::AddRoute(int32 routing_id, IPC::Channel::Listener* listener) {
DCHECK(MessageLoop::current() == message_loop());
router_.AddRoute(routing_id, listener);
}
void ChildThread::RemoveRoute(int32 routing_id) {
DCHECK(MessageLoop::current() == message_loop());
router_.RemoveRoute(routing_id);
}
IPC::Channel::Listener* ChildThread::ResolveRoute(int32 routing_id) {
DCHECK(MessageLoop::current() == message_loop());
return router_.ResolveRoute(routing_id);
}
webkit_glue::ResourceLoaderBridge* ChildThread::CreateBridge(
const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info,
int host_renderer_id,
int host_render_view_id) {
return resource_dispatcher()->
CreateBridge(request_info, host_renderer_id, host_render_view_id);
}
ResourceDispatcher* ChildThread::resource_dispatcher() {
return resource_dispatcher_.get();
}
IPC::SyncMessageFilter* ChildThread::sync_message_filter() {
return sync_message_filter_;
}
MessageLoop* ChildThread::message_loop() {
return message_loop_;
}
bool ChildThread::OnMessageReceived(const IPC::Message& msg) {
// Resource responses are sent to the resource dispatcher.
if (resource_dispatcher_->OnMessageReceived(msg))
return true;
if (socket_stream_dispatcher_->OnMessageReceived(msg))
return true;
if (file_system_dispatcher_->OnMessageReceived(msg))
return true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ChildThread, msg)
IPC_MESSAGE_HANDLER(PluginProcessMsg_AskBeforeShutdown, OnAskBeforeShutdown)
IPC_MESSAGE_HANDLER(PluginProcessMsg_Shutdown, OnShutdown)
#if defined(IPC_MESSAGE_LOG_ENABLED)
IPC_MESSAGE_HANDLER(PluginProcessMsg_SetIPCLoggingEnabled,
OnSetIPCLoggingEnabled)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
if (handled)
return true;
if (msg.routing_id() == MSG_ROUTING_CONTROL)
return OnControlMessageReceived(msg);
return router_.OnMessageReceived(msg);
}
bool ChildThread::OnControlMessageReceived(const IPC::Message& msg) {
return false;
}
void ChildThread::OnAskBeforeShutdown() {
check_with_browser_before_shutdown_ = true;
}
void ChildThread::OnShutdown() {
MessageLoop::current()->Quit();
}
#if defined(IPC_MESSAGE_LOG_ENABLED)
void ChildThread::OnSetIPCLoggingEnabled(bool enable) {
if (enable)
IPC::Logging::GetInstance()->Enable();
else
IPC::Logging::GetInstance()->Disable();
}
#endif // IPC_MESSAGE_LOG_ENABLED
ChildThread* ChildThread::current() {
return ChildProcess::current()->main_thread();
}
void ChildThread::OnProcessFinalRelease() {
if (on_channel_error_called_ || !check_with_browser_before_shutdown_) {
MessageLoop::current()->Quit();
return;
}
// The child process shutdown sequence is a request response based mechanism,
// where we send out an initial feeler request to the child process host
// instance in the browser to verify if it's ok to shutdown the child process.
// The browser then sends back a response if it's ok to shutdown.
Send(new PluginProcessHostMsg_ShutdownRequest);
}
|