OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "chrome/common/child_process_host.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/file_path.h" | |
9 #include "base/metrics/histogram.h" | |
10 #include "base/path_service.h" | |
11 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
12 #include "chrome/common/child_process_info.h" | |
13 #include "content/common/child_process_messages.h" | |
14 #include "content/common/content_paths.h" | |
15 #include "content/common/content_switches.h" | |
16 #include "ipc/ipc_logging.h" | |
17 | |
18 #if defined(OS_LINUX) | |
19 #include "base/linux_util.h" | |
20 #endif // OS_LINUX | |
21 | |
22 ChildProcessHost::ChildProcessHost() | |
23 : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), | |
24 opening_channel_(false) { | |
25 } | |
26 | |
27 ChildProcessHost::~ChildProcessHost() { | |
28 for (size_t i = 0; i < filters_.size(); ++i) { | |
29 filters_[i]->OnChannelClosing(); | |
30 filters_[i]->OnFilterRemoved(); | |
31 } | |
32 } | |
33 | |
34 void ChildProcessHost::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { | |
35 filters_.push_back(filter); | |
36 | |
37 if (channel_.get()) | |
38 filter->OnFilterAdded(channel_.get()); | |
39 } | |
40 | |
41 // static | |
42 FilePath ChildProcessHost::GetChildPath(bool allow_self) { | |
43 FilePath child_path; | |
44 | |
45 child_path = CommandLine::ForCurrentProcess()->GetSwitchValuePath( | |
46 switches::kBrowserSubprocessPath); | |
47 if (!child_path.empty()) | |
48 return child_path; | |
49 | |
50 #if defined(OS_LINUX) | |
51 // Use /proc/self/exe rather than our known binary path so updates | |
52 // can't swap out the binary from underneath us. | |
53 // When running under Valgrind, forking /proc/self/exe ends up forking the | |
54 // Valgrind executable, which then crashes. However, it's almost safe to | |
55 // assume that the updates won't happen while testing with Valgrind tools. | |
56 if (allow_self && !RunningOnValgrind()) | |
57 return FilePath("/proc/self/exe"); | |
58 #endif | |
59 | |
60 // On most platforms, the child executable is the same as the current | |
61 // executable. | |
62 PathService::Get(content::CHILD_PROCESS_EXE, &child_path); | |
63 return child_path; | |
64 } | |
65 | |
66 #if defined(OS_WIN) | |
67 // static | |
68 void ChildProcessHost::PreCacheFont(LOGFONT font) { | |
69 // If a child process is running in a sandbox, GetTextMetrics() | |
70 // can sometimes fail. If a font has not been loaded | |
71 // previously, GetTextMetrics() will try to load the font | |
72 // from the font file. However, the sandboxed process does | |
73 // not have permissions to access any font files and | |
74 // the call fails. So we make the browser pre-load the | |
75 // font for us by using a dummy call to GetTextMetrics of | |
76 // the same font. | |
77 | |
78 // Maintain a circular queue for the fonts and DCs to be cached. | |
79 // font_index maintains next available location in the queue. | |
80 static const int kFontCacheSize = 32; | |
81 static HFONT fonts[kFontCacheSize] = {0}; | |
82 static HDC hdcs[kFontCacheSize] = {0}; | |
83 static size_t font_index = 0; | |
84 | |
85 UMA_HISTOGRAM_COUNTS_100("Memory.CachedFontAndDC", | |
86 fonts[kFontCacheSize-1] ? kFontCacheSize : static_cast<int>(font_index)); | |
87 | |
88 HDC hdc = GetDC(NULL); | |
89 HFONT font_handle = CreateFontIndirect(&font); | |
90 DCHECK(NULL != font_handle); | |
91 | |
92 HGDIOBJ old_font = SelectObject(hdc, font_handle); | |
93 DCHECK(NULL != old_font); | |
94 | |
95 TEXTMETRIC tm; | |
96 BOOL ret = GetTextMetrics(hdc, &tm); | |
97 DCHECK(ret); | |
98 | |
99 if (fonts[font_index] || hdcs[font_index]) { | |
100 // We already have too many fonts, we will delete one and take it's place. | |
101 DeleteObject(fonts[font_index]); | |
102 ReleaseDC(NULL, hdcs[font_index]); | |
103 } | |
104 | |
105 fonts[font_index] = font_handle; | |
106 hdcs[font_index] = hdc; | |
107 font_index = (font_index + 1) % kFontCacheSize; | |
108 } | |
109 #endif // OS_WIN | |
110 | |
111 | |
112 bool ChildProcessHost::CreateChannel() { | |
113 channel_id_ = ChildProcessInfo::GenerateRandomChannelID(this); | |
114 channel_.reset(new IPC::Channel( | |
115 channel_id_, IPC::Channel::MODE_SERVER, &listener_)); | |
116 if (!channel_->Connect()) | |
117 return false; | |
118 | |
119 for (size_t i = 0; i < filters_.size(); ++i) | |
120 filters_[i]->OnFilterAdded(channel_.get()); | |
121 | |
122 // Make sure these messages get sent first. | |
123 #if defined(IPC_MESSAGE_LOG_ENABLED) | |
124 bool enabled = IPC::Logging::GetInstance()->Enabled(); | |
125 Send(new ChildProcessMsg_SetIPCLoggingEnabled(enabled)); | |
126 #endif | |
127 | |
128 Send(new ChildProcessMsg_AskBeforeShutdown()); | |
129 | |
130 opening_channel_ = true; | |
131 | |
132 return true; | |
133 } | |
134 | |
135 void ChildProcessHost::InstanceCreated() { | |
136 Notify(NotificationType::CHILD_INSTANCE_CREATED); | |
137 } | |
138 | |
139 bool ChildProcessHost::OnMessageReceived(const IPC::Message& msg) { | |
140 return false; | |
141 } | |
142 | |
143 void ChildProcessHost::OnChannelConnected(int32 peer_pid) { | |
144 } | |
145 | |
146 void ChildProcessHost::OnChannelError() { | |
147 } | |
148 | |
149 bool ChildProcessHost::Send(IPC::Message* message) { | |
150 if (!channel_.get()) { | |
151 delete message; | |
152 return false; | |
153 } | |
154 return channel_->Send(message); | |
155 } | |
156 | |
157 void ChildProcessHost::OnChildDied() { | |
158 delete this; | |
159 } | |
160 | |
161 void ChildProcessHost::ShutdownStarted() { | |
162 } | |
163 | |
164 void ChildProcessHost::Notify(NotificationType type) { | |
165 } | |
166 | |
167 ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) | |
168 : host_(host) { | |
169 } | |
170 | |
171 bool ChildProcessHost::ListenerHook::OnMessageReceived( | |
172 const IPC::Message& msg) { | |
173 #ifdef IPC_MESSAGE_LOG_ENABLED | |
174 IPC::Logging* logger = IPC::Logging::GetInstance(); | |
175 if (msg.type() == IPC_LOGGING_ID) { | |
176 logger->OnReceivedLoggingMessage(msg); | |
177 return true; | |
178 } | |
179 | |
180 if (logger->Enabled()) | |
181 logger->OnPreDispatchMessage(msg); | |
182 #endif | |
183 | |
184 bool handled = false; | |
185 for (size_t i = 0; i < host_->filters_.size(); ++i) { | |
186 if (host_->filters_[i]->OnMessageReceived(msg)) { | |
187 handled = true; | |
188 break; | |
189 } | |
190 } | |
191 | |
192 if (!handled && msg.type() == ChildProcessHostMsg_ShutdownRequest::ID) { | |
193 if (host_->CanShutdown()) | |
194 host_->Send(new ChildProcessMsg_Shutdown()); | |
195 handled = true; | |
196 } | |
197 | |
198 if (!handled) | |
199 handled = host_->OnMessageReceived(msg); | |
200 | |
201 #ifdef IPC_MESSAGE_LOG_ENABLED | |
202 if (logger->Enabled()) | |
203 logger->OnPostDispatchMessage(msg, host_->channel_id_); | |
204 #endif | |
205 return handled; | |
206 } | |
207 | |
208 void ChildProcessHost::ListenerHook::OnChannelConnected(int32 peer_pid) { | |
209 host_->opening_channel_ = false; | |
210 host_->OnChannelConnected(peer_pid); | |
211 // Notify in the main loop of the connection. | |
212 host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED); | |
213 | |
214 for (size_t i = 0; i < host_->filters_.size(); ++i) | |
215 host_->filters_[i]->OnChannelConnected(peer_pid); | |
216 } | |
217 | |
218 void ChildProcessHost::ListenerHook::OnChannelError() { | |
219 host_->opening_channel_ = false; | |
220 host_->OnChannelError(); | |
221 | |
222 for (size_t i = 0; i < host_->filters_.size(); ++i) | |
223 host_->filters_[i]->OnChannelError(); | |
224 | |
225 // This will delete host_, which will also destroy this! | |
226 host_->OnChildDied(); | |
227 } | |
228 | |
229 void ChildProcessHost::ForceShutdown() { | |
230 Send(new ChildProcessMsg_Shutdown()); | |
231 } | |
OLD | NEW |