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
208
209
210
211
212
213
214
215
216
217
218
|
// Copyright (c) 2010 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_process_host.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "chrome/common/child_process_info.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/plugin_messages.h"
#include "ipc/ipc_logging.h"
#if defined(OS_LINUX)
#include "base/linux_util.h"
#endif // OS_LINUX
ChildProcessHost::ChildProcessHost()
: ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)),
opening_channel_(false) {
}
ChildProcessHost::~ChildProcessHost() {
for (size_t i = 0; i < filters_.size(); ++i) {
filters_[i]->OnChannelClosing();
filters_[i]->OnFilterRemoved();
}
}
void ChildProcessHost::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
filters_.push_back(filter);
if (channel_.get())
filter->OnFilterAdded(channel_.get());
}
// static
FilePath ChildProcessHost::GetChildPath(bool allow_self) {
FilePath child_path;
child_path = CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kBrowserSubprocessPath);
if (!child_path.empty())
return child_path;
#if defined(OS_MACOSX)
// On the Mac, the child executable lives at a predefined location within
// the app bundle's versioned directory.
return chrome::GetVersionedDirectory().
Append(chrome::kHelperProcessExecutablePath);
#endif
#if defined(OS_LINUX)
// Use /proc/self/exe rather than our known binary path so updates
// can't swap out the binary from underneath us.
if (allow_self)
return FilePath("/proc/self/exe");
#endif
// On most platforms, the child executable is the same as the current
// executable.
PathService::Get(base::FILE_EXE, &child_path);
return child_path;
}
#if defined(OS_WIN)
// static
void ChildProcessHost::PreCacheFont(LOGFONT font) {
// If a child process is running in a sandbox, GetTextMetrics()
// can sometimes fail. If a font has not been loaded
// previously, GetTextMetrics() will try to load the font
// from the font file. However, the sandboxed process does
// not have permissions to access any font files and
// the call fails. So we make the browser pre-load the
// font for us by using a dummy call to GetTextMetrics of
// the same font.
// Maintain a circular queue for the fonts and DCs to be cached.
// font_index maintains next available location in the queue.
static const int kFontCacheSize = 32;
static HFONT fonts[kFontCacheSize] = {0};
static HDC hdcs[kFontCacheSize] = {0};
static size_t font_index = 0;
UMA_HISTOGRAM_COUNTS_100("Memory.CachedFontAndDC",
fonts[kFontCacheSize-1] ? kFontCacheSize : static_cast<int>(font_index));
HDC hdc = GetDC(NULL);
HFONT font_handle = CreateFontIndirect(&font);
DCHECK(NULL != font_handle);
HGDIOBJ old_font = SelectObject(hdc, font_handle);
DCHECK(NULL != old_font);
TEXTMETRIC tm;
BOOL ret = GetTextMetrics(hdc, &tm);
DCHECK(ret);
if (fonts[font_index] || hdcs[font_index]) {
// We already have too many fonts, we will delete one and take it's place.
DeleteObject(fonts[font_index]);
ReleaseDC(NULL, hdcs[font_index]);
}
fonts[font_index] = font_handle;
hdcs[font_index] = hdc;
font_index = (font_index + 1) % kFontCacheSize;
}
#endif // OS_WIN
bool ChildProcessHost::CreateChannel() {
channel_id_ = ChildProcessInfo::GenerateRandomChannelID(this);
channel_.reset(new IPC::Channel(
channel_id_, IPC::Channel::MODE_SERVER, &listener_));
if (!channel_->Connect())
return false;
for (size_t i = 0; i < filters_.size(); ++i)
filters_[i]->OnFilterAdded(channel_.get());
// Make sure these messages get sent first.
#if defined(IPC_MESSAGE_LOG_ENABLED)
bool enabled = IPC::Logging::GetInstance()->Enabled();
Send(new PluginProcessMsg_SetIPCLoggingEnabled(enabled));
#endif
Send(new PluginProcessMsg_AskBeforeShutdown());
opening_channel_ = true;
return true;
}
void ChildProcessHost::InstanceCreated() {
Notify(NotificationType::CHILD_INSTANCE_CREATED);
}
bool ChildProcessHost::Send(IPC::Message* message) {
if (!channel_.get()) {
delete message;
return false;
}
return channel_->Send(message);
}
void ChildProcessHost::OnChildDied() {
delete this;
}
ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
: host_(host) {
}
bool ChildProcessHost::ListenerHook::OnMessageReceived(
const IPC::Message& msg) {
#ifdef IPC_MESSAGE_LOG_ENABLED
IPC::Logging* logger = IPC::Logging::GetInstance();
if (msg.type() == IPC_LOGGING_ID) {
logger->OnReceivedLoggingMessage(msg);
return true;
}
if (logger->Enabled())
logger->OnPreDispatchMessage(msg);
#endif
bool handled = false;
for (size_t i = 0; i < host_->filters_.size(); ++i) {
if (host_->filters_[i]->OnMessageReceived(msg)) {
handled = true;
break;
}
}
if (!handled && msg.type() == PluginProcessHostMsg_ShutdownRequest::ID) {
if (host_->CanShutdown())
host_->Send(new PluginProcessMsg_Shutdown());
handled = true;
}
if (!handled)
handled = host_->OnMessageReceived(msg);
#ifdef IPC_MESSAGE_LOG_ENABLED
if (logger->Enabled())
logger->OnPostDispatchMessage(msg, host_->channel_id_);
#endif
return handled;
}
void ChildProcessHost::ListenerHook::OnChannelConnected(int32 peer_pid) {
host_->opening_channel_ = false;
host_->OnChannelConnected(peer_pid);
// Notify in the main loop of the connection.
host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED);
for (size_t i = 0; i < host_->filters_.size(); ++i)
host_->filters_[i]->OnChannelConnected(peer_pid);
}
void ChildProcessHost::ListenerHook::OnChannelError() {
host_->opening_channel_ = false;
host_->OnChannelError();
for (size_t i = 0; i < host_->filters_.size(); ++i)
host_->filters_[i]->OnChannelError();
// This will delete host_, which will also destroy this!
host_->OnChildDied();
}
void ChildProcessHost::ForceShutdown() {
Send(new PluginProcessMsg_Shutdown());
}
|