OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 // Represents the browser side of the browser <--> renderer communication | |
6 // channel. There will be one RenderProcessHost per renderer process. | |
7 | |
8 #include "content/browser/renderer_host/browser_render_process_host.h" | |
9 | |
10 #include <algorithm> | |
11 #include <limits> | |
12 #include <vector> | |
13 | |
14 #if defined(OS_POSIX) | |
15 #include <utility> // for pair<> | |
16 #endif | |
17 | |
18 #include "base/base_switches.h" | |
19 #include "base/bind.h" | |
20 #include "base/bind_helpers.h" | |
21 #include "base/callback.h" | |
22 #include "base/command_line.h" | |
23 #include "base/logging.h" | |
24 #include "base/metrics/field_trial.h" | |
25 #include "base/metrics/histogram.h" | |
26 #include "base/path_service.h" | |
27 #include "base/platform_file.h" | |
28 #include "base/stl_util.h" | |
29 #include "base/string_util.h" | |
30 #include "base/threading/thread.h" | |
31 #include "base/threading/thread_restrictions.h" | |
32 #include "content/browser/appcache/appcache_dispatcher_host.h" | |
33 #include "content/browser/browser_child_process_host.h" | |
34 #include "content/browser/browser_context.h" | |
35 #include "content/browser/child_process_security_policy.h" | |
36 #include "content/browser/device_orientation/message_filter.h" | |
37 #include "content/browser/download/mhtml_generation_manager.h" | |
38 #include "content/browser/file_system/file_system_dispatcher_host.h" | |
39 #include "content/browser/geolocation/geolocation_dispatcher_host.h" | |
40 #include "content/browser/gpu/gpu_data_manager.h" | |
41 #include "content/browser/gpu/gpu_process_host.h" | |
42 #include "content/browser/in_process_webkit/dom_storage_message_filter.h" | |
43 #include "content/browser/in_process_webkit/indexed_db_dispatcher_host.h" | |
44 #include "content/browser/mime_registry_message_filter.h" | |
45 #include "content/browser/plugin_service.h" | |
46 #include "content/browser/renderer_host/blob_message_filter.h" | |
47 #include "content/browser/renderer_host/clipboard_message_filter.h" | |
48 #include "content/browser/renderer_host/database_message_filter.h" | |
49 #include "content/browser/renderer_host/file_utilities_message_filter.h" | |
50 #include "content/browser/renderer_host/gpu_message_filter.h" | |
51 #include "content/browser/renderer_host/media/audio_input_renderer_host.h" | |
52 #include "content/browser/renderer_host/media/audio_renderer_host.h" | |
53 #include "content/browser/renderer_host/media/media_stream_dispatcher_host.h" | |
54 #include "content/browser/renderer_host/media/video_capture_host.h" | |
55 #include "content/browser/renderer_host/p2p/socket_dispatcher_host.h" | |
56 #include "content/browser/renderer_host/pepper_file_message_filter.h" | |
57 #include "content/browser/renderer_host/pepper_message_filter.h" | |
58 #include "content/browser/renderer_host/quota_dispatcher_host.h" | |
59 #include "content/browser/renderer_host/render_message_filter.h" | |
60 #include "content/browser/renderer_host/render_view_host.h" | |
61 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
62 #include "content/browser/renderer_host/render_widget_helper.h" | |
63 #include "content/browser/renderer_host/render_widget_host.h" | |
64 #include "content/browser/renderer_host/resource_message_filter.h" | |
65 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" | |
66 #include "content/browser/renderer_host/text_input_client_message_filter.h" | |
67 #include "content/browser/resolve_proxy_msg_helper.h" | |
68 #include "content/browser/speech/speech_input_dispatcher_host.h" | |
69 #include "content/browser/trace_message_filter.h" | |
70 #include "content/browser/user_metrics.h" | |
71 #include "content/browser/worker_host/worker_message_filter.h" | |
72 #include "content/common/child_process_info.h" | |
73 #include "content/common/child_process_messages.h" | |
74 #include "content/common/gpu/gpu_messages.h" | |
75 #include "content/public/browser/notification_service.h" | |
76 #include "content/common/process_watcher.h" | |
77 #include "content/common/resource_messages.h" | |
78 #include "content/common/view_messages.h" | |
79 #include "content/public/browser/content_browser_client.h" | |
80 #include "content/public/common/content_switches.h" | |
81 #include "content/public/common/result_codes.h" | |
82 #include "content/renderer/render_process_impl.h" | |
83 #include "content/renderer/render_thread_impl.h" | |
84 #include "ipc/ipc_logging.h" | |
85 #include "ipc/ipc_platform_file.h" | |
86 #include "ipc/ipc_switches.h" | |
87 #include "media/base/media_switches.h" | |
88 #include "net/url_request/url_request_context_getter.h" | |
89 #include "ui/base/ui_base_switches.h" | |
90 #include "ui/gfx/gl/gl_switches.h" | |
91 #include "webkit/fileapi/file_system_path_manager.h" | |
92 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
93 #include "webkit/glue/resource_type.h" | |
94 #include "webkit/plugins/plugin_switches.h" | |
95 | |
96 #if defined(OS_WIN) | |
97 #include <objbase.h> | |
98 #include "base/synchronization/waitable_event.h" | |
99 #include "content/common/section_util_win.h" | |
100 #endif | |
101 | |
102 #include "third_party/skia/include/core/SkBitmap.h" | |
103 | |
104 using content::BrowserThread; | |
105 | |
106 // This class creates the IO thread for the renderer when running in | |
107 // single-process mode. It's not used in multi-process mode. | |
108 class RendererMainThread : public base::Thread { | |
109 public: | |
110 explicit RendererMainThread(const std::string& channel_id) | |
111 : base::Thread("Chrome_InProcRendererThread"), | |
112 channel_id_(channel_id), | |
113 render_process_(NULL) { | |
114 } | |
115 | |
116 ~RendererMainThread() { | |
117 Stop(); | |
118 } | |
119 | |
120 protected: | |
121 virtual void Init() { | |
122 #if defined(OS_WIN) | |
123 CoInitialize(NULL); | |
124 #endif | |
125 | |
126 render_process_ = new RenderProcessImpl(); | |
127 render_process_->set_main_thread(new RenderThreadImpl(channel_id_)); | |
128 } | |
129 | |
130 virtual void CleanUp() { | |
131 delete render_process_; | |
132 | |
133 #if defined(OS_WIN) | |
134 CoUninitialize(); | |
135 #endif | |
136 // It's a little lame to manually set this flag. But the single process | |
137 // RendererThread will receive the WM_QUIT. We don't need to assert on | |
138 // this thread, so just force the flag manually. | |
139 // If we want to avoid this, we could create the InProcRendererThread | |
140 // directly with _beginthreadex() rather than using the Thread class. | |
141 // We used to set this flag in the Init function above. However there | |
142 // other threads like WebThread which are created by this thread | |
143 // which resets this flag. Please see Thread::StartWithOptions. Setting | |
144 // this flag to true in Cleanup works around these problems. | |
145 base::Thread::SetThreadWasQuitProperly(true); | |
146 } | |
147 | |
148 private: | |
149 std::string channel_id_; | |
150 // Deleted in CleanUp() on the renderer thread, so don't use a smart pointer. | |
151 RenderProcess* render_process_; | |
152 }; | |
153 | |
154 namespace { | |
155 | |
156 // Helper class that we pass to ResourceMessageFilter so that it can find the | |
157 // right net::URLRequestContext for a request. | |
158 class RendererURLRequestContextSelector | |
159 : public ResourceMessageFilter::URLRequestContextSelector { | |
160 public: | |
161 RendererURLRequestContextSelector(content::BrowserContext* browser_context, | |
162 int render_child_id) | |
163 : request_context_(browser_context->GetRequestContextForRenderProcess( | |
164 render_child_id)), | |
165 media_request_context_(browser_context->GetRequestContextForMedia()) { | |
166 } | |
167 | |
168 virtual net::URLRequestContext* GetRequestContext( | |
169 ResourceType::Type resource_type) { | |
170 net::URLRequestContextGetter* request_context = request_context_; | |
171 // If the request has resource type of ResourceType::MEDIA, we use a request | |
172 // context specific to media for handling it because these resources have | |
173 // specific needs for caching. | |
174 if (resource_type == ResourceType::MEDIA) | |
175 request_context = media_request_context_; | |
176 return request_context->GetURLRequestContext(); | |
177 } | |
178 | |
179 private: | |
180 virtual ~RendererURLRequestContextSelector() {} | |
181 | |
182 scoped_refptr<net::URLRequestContextGetter> request_context_; | |
183 scoped_refptr<net::URLRequestContextGetter> media_request_context_; | |
184 }; | |
185 | |
186 } // namespace | |
187 | |
188 BrowserRenderProcessHost::BrowserRenderProcessHost( | |
189 content::BrowserContext* browser_context) | |
190 : RenderProcessHost(browser_context), | |
191 visible_widgets_(0), | |
192 backgrounded_(true), | |
193 ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_( | |
194 FROM_HERE, base::TimeDelta::FromSeconds(5), | |
195 this, &BrowserRenderProcessHost::ClearTransportDIBCache)), | |
196 accessibility_enabled_(false), | |
197 is_initialized_(false) { | |
198 widget_helper_ = new RenderWidgetHelper(); | |
199 | |
200 ChildProcessSecurityPolicy::GetInstance()->Add(id()); | |
201 | |
202 // Grant most file permissions to this renderer. | |
203 // PLATFORM_FILE_TEMPORARY, PLATFORM_FILE_HIDDEN and | |
204 // PLATFORM_FILE_DELETE_ON_CLOSE are not granted, because no existing API | |
205 // requests them. | |
206 // This is for the filesystem sandbox. | |
207 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( | |
208 id(), browser_context->GetPath().Append( | |
209 fileapi::SandboxMountPointProvider::kNewFileSystemDirectory), | |
210 base::PLATFORM_FILE_OPEN | | |
211 base::PLATFORM_FILE_CREATE | | |
212 base::PLATFORM_FILE_OPEN_ALWAYS | | |
213 base::PLATFORM_FILE_CREATE_ALWAYS | | |
214 base::PLATFORM_FILE_OPEN_TRUNCATED | | |
215 base::PLATFORM_FILE_READ | | |
216 base::PLATFORM_FILE_WRITE | | |
217 base::PLATFORM_FILE_EXCLUSIVE_READ | | |
218 base::PLATFORM_FILE_EXCLUSIVE_WRITE | | |
219 base::PLATFORM_FILE_ASYNC | | |
220 base::PLATFORM_FILE_WRITE_ATTRIBUTES | | |
221 base::PLATFORM_FILE_ENUMERATE); | |
222 // This is so that we can read and move stuff out of the old filesystem | |
223 // sandbox. | |
224 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( | |
225 id(), browser_context->GetPath().Append( | |
226 fileapi::SandboxMountPointProvider::kOldFileSystemDirectory), | |
227 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_WRITE | | |
228 base::PLATFORM_FILE_WRITE_ATTRIBUTES | base::PLATFORM_FILE_ENUMERATE); | |
229 // This is so that we can rename the old sandbox out of the way so that we | |
230 // know we've taken care of it. | |
231 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( | |
232 id(), browser_context->GetPath().Append( | |
233 fileapi::SandboxMountPointProvider::kRenamedOldFileSystemDirectory), | |
234 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_CREATE_ALWAYS | | |
235 base::PLATFORM_FILE_WRITE); | |
236 | |
237 // Note: When we create the BrowserRenderProcessHost, it's technically | |
238 // backgrounded, because it has no visible listeners. But the process | |
239 // doesn't actually exist yet, so we'll Background it later, after | |
240 // creation. | |
241 } | |
242 | |
243 BrowserRenderProcessHost::~BrowserRenderProcessHost() { | |
244 ChildProcessSecurityPolicy::GetInstance()->Remove(id()); | |
245 | |
246 // We may have some unsent messages at this point, but that's OK. | |
247 channel_.reset(); | |
248 while (!queued_messages_.empty()) { | |
249 delete queued_messages_.front(); | |
250 queued_messages_.pop(); | |
251 } | |
252 | |
253 ClearTransportDIBCache(); | |
254 } | |
255 | |
256 void BrowserRenderProcessHost::EnableSendQueue() { | |
257 is_initialized_ = false; | |
258 } | |
259 | |
260 bool BrowserRenderProcessHost::Init(bool is_accessibility_enabled) { | |
261 // calling Init() more than once does nothing, this makes it more convenient | |
262 // for the view host which may not be sure in some cases | |
263 if (channel_.get()) | |
264 return true; | |
265 | |
266 accessibility_enabled_ = is_accessibility_enabled; | |
267 | |
268 CommandLine::StringType renderer_prefix; | |
269 #if defined(OS_POSIX) | |
270 // A command prefix is something prepended to the command line of the spawned | |
271 // process. It is supported only on POSIX systems. | |
272 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | |
273 renderer_prefix = | |
274 browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); | |
275 #endif // defined(OS_POSIX) | |
276 | |
277 #if defined(OS_LINUX) | |
278 int flags = renderer_prefix.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : | |
279 ChildProcessHost::CHILD_NORMAL; | |
280 #else | |
281 int flags = ChildProcessHost::CHILD_NORMAL; | |
282 #endif | |
283 | |
284 // Find the renderer before creating the channel so if this fails early we | |
285 // return without creating the channel. | |
286 FilePath renderer_path = ChildProcessHost::GetChildPath(flags); | |
287 if (renderer_path.empty()) | |
288 return false; | |
289 | |
290 // Setup the IPC channel. | |
291 const std::string channel_id = | |
292 ChildProcessInfo::GenerateRandomChannelID(this); | |
293 channel_.reset(new IPC::ChannelProxy( | |
294 channel_id, IPC::Channel::MODE_SERVER, this, | |
295 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); | |
296 | |
297 // Call the embedder first so that their IPC filters have priority. | |
298 content::GetContentClient()->browser()->BrowserRenderProcessHostCreated(this); | |
299 | |
300 CreateMessageFilters(); | |
301 | |
302 if (run_renderer_in_process()) { | |
303 // Crank up a thread and run the initialization there. With the way that | |
304 // messages flow between the browser and renderer, this thread is required | |
305 // to prevent a deadlock in single-process mode. Since the primordial | |
306 // thread in the renderer process runs the WebKit code and can sometimes | |
307 // make blocking calls to the UI thread (i.e. this thread), they need to run | |
308 // on separate threads. | |
309 in_process_renderer_.reset(new RendererMainThread(channel_id)); | |
310 | |
311 base::Thread::Options options; | |
312 #if !defined(TOOLKIT_USES_GTK) | |
313 // In-process plugins require this to be a UI message loop. | |
314 options.message_loop_type = MessageLoop::TYPE_UI; | |
315 #else | |
316 // We can't have multiple UI loops on GTK, so we don't support | |
317 // in-process plugins. | |
318 options.message_loop_type = MessageLoop::TYPE_DEFAULT; | |
319 #endif | |
320 in_process_renderer_->StartWithOptions(options); | |
321 | |
322 OnProcessLaunched(); // Fake a callback that the process is ready. | |
323 } else { | |
324 // Build command line for renderer. We call AppendRendererCommandLine() | |
325 // first so the process type argument will appear first. | |
326 CommandLine* cmd_line = new CommandLine(renderer_path); | |
327 if (!renderer_prefix.empty()) | |
328 cmd_line->PrependWrapper(renderer_prefix); | |
329 AppendRendererCommandLine(cmd_line); | |
330 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | |
331 | |
332 // Spawn the child process asynchronously to avoid blocking the UI thread. | |
333 // As long as there's no renderer prefix, we can use the zygote process | |
334 // at this stage. | |
335 child_process_launcher_.reset(new ChildProcessLauncher( | |
336 #if defined(OS_WIN) | |
337 FilePath(), | |
338 #elif defined(OS_POSIX) | |
339 renderer_prefix.empty(), | |
340 base::environment_vector(), | |
341 channel_->TakeClientFileDescriptor(), | |
342 #endif | |
343 cmd_line, | |
344 this)); | |
345 | |
346 fast_shutdown_started_ = false; | |
347 } | |
348 | |
349 is_initialized_ = true; | |
350 return true; | |
351 } | |
352 | |
353 void BrowserRenderProcessHost::CreateMessageFilters() { | |
354 scoped_refptr<RenderMessageFilter> render_message_filter( | |
355 new RenderMessageFilter( | |
356 id(), | |
357 PluginService::GetInstance(), | |
358 browser_context(), | |
359 browser_context()->GetRequestContextForRenderProcess(id()), | |
360 widget_helper_)); | |
361 channel_->AddFilter(render_message_filter); | |
362 | |
363 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( | |
364 id(), ChildProcessInfo::RENDER_PROCESS, | |
365 &browser_context()->GetResourceContext(), | |
366 new RendererURLRequestContextSelector(browser_context(), id()), | |
367 content::GetContentClient()->browser()->GetResourceDispatcherHost()); | |
368 | |
369 channel_->AddFilter(resource_message_filter); | |
370 channel_->AddFilter(new AudioInputRendererHost( | |
371 &browser_context()->GetResourceContext())); | |
372 channel_->AddFilter( | |
373 new AudioRendererHost(&browser_context()->GetResourceContext())); | |
374 channel_->AddFilter( | |
375 new VideoCaptureHost(&browser_context()->GetResourceContext())); | |
376 channel_->AddFilter( | |
377 new AppCacheDispatcherHost(browser_context()->GetAppCacheService(), | |
378 id())); | |
379 channel_->AddFilter(new ClipboardMessageFilter()); | |
380 channel_->AddFilter( | |
381 new DOMStorageMessageFilter(id(), browser_context()->GetWebKitContext())); | |
382 channel_->AddFilter( | |
383 new IndexedDBDispatcherHost(id(), browser_context()->GetWebKitContext())); | |
384 channel_->AddFilter( | |
385 GeolocationDispatcherHost::New( | |
386 id(), browser_context()->GetGeolocationPermissionContext())); | |
387 channel_->AddFilter(new GpuMessageFilter(id(), widget_helper_.get())); | |
388 channel_->AddFilter(new media_stream::MediaStreamDispatcherHost( | |
389 &browser_context()->GetResourceContext(), id())); | |
390 channel_->AddFilter(new PepperFileMessageFilter(id(), browser_context())); | |
391 channel_->AddFilter( | |
392 new PepperMessageFilter(&browser_context()->GetResourceContext())); | |
393 channel_->AddFilter(new speech_input::SpeechInputDispatcherHost( | |
394 id(), browser_context()->GetRequestContext(), | |
395 browser_context()->GetSpeechInputPreferences())); | |
396 channel_->AddFilter( | |
397 new FileSystemDispatcherHost(browser_context()->GetRequestContext(), | |
398 browser_context()->GetFileSystemContext())); | |
399 channel_->AddFilter(new device_orientation::MessageFilter()); | |
400 channel_->AddFilter( | |
401 new BlobMessageFilter(id(), browser_context()->GetBlobStorageContext())); | |
402 channel_->AddFilter(new FileUtilitiesMessageFilter(id())); | |
403 channel_->AddFilter(new MimeRegistryMessageFilter()); | |
404 channel_->AddFilter(new DatabaseMessageFilter( | |
405 browser_context()->GetDatabaseTracker())); | |
406 #if defined(OS_MACOSX) | |
407 channel_->AddFilter(new TextInputClientMessageFilter(id())); | |
408 #endif | |
409 | |
410 SocketStreamDispatcherHost* socket_stream_dispatcher_host = | |
411 new SocketStreamDispatcherHost( | |
412 new RendererURLRequestContextSelector(browser_context(), id()), | |
413 &browser_context()->GetResourceContext()); | |
414 channel_->AddFilter(socket_stream_dispatcher_host); | |
415 | |
416 channel_->AddFilter( | |
417 new WorkerMessageFilter( | |
418 id(), | |
419 &browser_context()->GetResourceContext(), | |
420 content::GetContentClient()->browser()->GetResourceDispatcherHost(), | |
421 base::Bind(&RenderWidgetHelper::GetNextRoutingID, | |
422 base::Unretained(widget_helper_.get())))); | |
423 | |
424 #if defined(ENABLE_P2P_APIS) | |
425 channel_->AddFilter(new content::P2PSocketDispatcherHost( | |
426 &browser_context()->GetResourceContext())); | |
427 #endif | |
428 | |
429 channel_->AddFilter(new TraceMessageFilter()); | |
430 channel_->AddFilter(new ResolveProxyMsgHelper( | |
431 browser_context()->GetRequestContextForRenderProcess(id()))); | |
432 channel_->AddFilter(new QuotaDispatcherHost( | |
433 id(), browser_context()->GetQuotaManager(), | |
434 content::GetContentClient()->browser()->CreateQuotaPermissionContext())); | |
435 } | |
436 | |
437 int BrowserRenderProcessHost::GetNextRoutingID() { | |
438 return widget_helper_->GetNextRoutingID(); | |
439 } | |
440 | |
441 void BrowserRenderProcessHost::CancelResourceRequests(int render_widget_id) { | |
442 widget_helper_->CancelResourceRequests(render_widget_id); | |
443 } | |
444 | |
445 void BrowserRenderProcessHost::CrossSiteSwapOutACK( | |
446 const ViewMsg_SwapOut_Params& params) { | |
447 widget_helper_->CrossSiteSwapOutACK(params); | |
448 } | |
449 | |
450 bool BrowserRenderProcessHost::WaitForUpdateMsg( | |
451 int render_widget_id, | |
452 const base::TimeDelta& max_delay, | |
453 IPC::Message* msg) { | |
454 // The post task to this thread with the process id could be in queue, and we | |
455 // don't want to dispatch a message before then since it will need the handle. | |
456 if (child_process_launcher_.get() && child_process_launcher_->IsStarting()) | |
457 return false; | |
458 | |
459 return widget_helper_->WaitForUpdateMsg(render_widget_id, max_delay, msg); | |
460 } | |
461 | |
462 void BrowserRenderProcessHost::ReceivedBadMessage() { | |
463 if (run_renderer_in_process()) { | |
464 // In single process mode it is better if we don't suicide but just | |
465 // crash. | |
466 CHECK(false); | |
467 } | |
468 NOTREACHED(); | |
469 base::KillProcess(GetHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE, | |
470 false); | |
471 } | |
472 | |
473 void BrowserRenderProcessHost::WidgetRestored() { | |
474 // Verify we were properly backgrounded. | |
475 DCHECK_EQ(backgrounded_, (visible_widgets_ == 0)); | |
476 visible_widgets_++; | |
477 SetBackgrounded(false); | |
478 } | |
479 | |
480 void BrowserRenderProcessHost::WidgetHidden() { | |
481 // On startup, the browser will call Hide | |
482 if (backgrounded_) | |
483 return; | |
484 | |
485 DCHECK_EQ(backgrounded_, (visible_widgets_ == 0)); | |
486 visible_widgets_--; | |
487 DCHECK_GE(visible_widgets_, 0); | |
488 if (visible_widgets_ == 0) { | |
489 DCHECK(!backgrounded_); | |
490 SetBackgrounded(true); | |
491 } | |
492 } | |
493 | |
494 int BrowserRenderProcessHost::VisibleWidgetCount() const { | |
495 return visible_widgets_; | |
496 } | |
497 | |
498 void BrowserRenderProcessHost::AppendRendererCommandLine( | |
499 CommandLine* command_line) const { | |
500 // Pass the process type first, so it shows first in process listings. | |
501 command_line->AppendSwitchASCII(switches::kProcessType, | |
502 switches::kRendererProcess); | |
503 | |
504 if (accessibility_enabled_) | |
505 command_line->AppendSwitch(switches::kEnableAccessibility); | |
506 | |
507 // Now send any options from our own command line we want to propagate. | |
508 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | |
509 PropagateBrowserCommandLineToRenderer(browser_command_line, command_line); | |
510 | |
511 // Pass on the browser locale. | |
512 const std::string locale = | |
513 content::GetContentClient()->browser()->GetApplicationLocale(); | |
514 command_line->AppendSwitchASCII(switches::kLang, locale); | |
515 | |
516 // If we run base::FieldTrials, we want to pass to their state to the | |
517 // renderer so that it can act in accordance with each state, or record | |
518 // histograms relating to the base::FieldTrial states. | |
519 std::string field_trial_states; | |
520 base::FieldTrialList::StatesToString(&field_trial_states); | |
521 if (!field_trial_states.empty()) { | |
522 command_line->AppendSwitchASCII(switches::kForceFieldTestNameAndValue, | |
523 field_trial_states); | |
524 } | |
525 | |
526 content::GetContentClient()->browser()->AppendExtraCommandLineSwitches( | |
527 command_line, id()); | |
528 | |
529 // Appending disable-gpu-feature switches due to software rendering list. | |
530 GpuDataManager* gpu_data_manager = GpuDataManager::GetInstance(); | |
531 DCHECK(gpu_data_manager); | |
532 gpu_data_manager->AppendRendererCommandLine(command_line); | |
533 } | |
534 | |
535 void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( | |
536 const CommandLine& browser_cmd, | |
537 CommandLine* renderer_cmd) const { | |
538 // Propagate the following switches to the renderer command line (along | |
539 // with any associated values) if present in the browser command line. | |
540 static const char* const kSwitchNames[] = { | |
541 // We propagate the Chrome Frame command line here as well in case the | |
542 // renderer is not run in the sandbox. | |
543 switches::kAuditAllHandles, | |
544 switches::kAuditHandles, | |
545 switches::kChromeFrame, | |
546 switches::kDisable3DAPIs, | |
547 switches::kDisableAcceleratedCompositing, | |
548 switches::kDisableApplicationCache, | |
549 switches::kDisableAudio, | |
550 switches::kDisableBreakpad, | |
551 switches::kDisableDataTransferItems, | |
552 switches::kDisableDatabases, | |
553 switches::kDisableDesktopNotifications, | |
554 switches::kDisableDeviceOrientation, | |
555 switches::kDisableFileSystem, | |
556 switches::kDisableGeolocation, | |
557 switches::kDisableGLMultisampling, | |
558 switches::kDisableGLSLTranslator, | |
559 switches::kDisableGpuDriverBugWorkarounds, | |
560 switches::kDisableGpuVsync, | |
561 switches::kDisableIndexedDatabase, | |
562 switches::kDisableJavaScriptI18NAPI, | |
563 switches::kDisableLocalStorage, | |
564 switches::kDisableLogging, | |
565 switches::kDisableSeccompSandbox, | |
566 switches::kDisableSessionStorage, | |
567 switches::kDisableSharedWorkers, | |
568 switches::kDisableSpeechInput, | |
569 switches::kDisableWebAudio, | |
570 switches::kDisableWebSockets, | |
571 switches::kEnableAccessibilityLogging, | |
572 switches::kEnableDCHECK, | |
573 switches::kEnableGamepad, | |
574 switches::kEnableGPUServiceLogging, | |
575 switches::kEnableGPUClientLogging, | |
576 switches::kEnableLogging, | |
577 switches::kEnableMediaSource, | |
578 switches::kEnableMediaStream, | |
579 switches::kEnableStrictSiteIsolation, | |
580 switches::kDisableFullScreen, | |
581 switches::kEnablePepperTesting, | |
582 #if defined(OS_MACOSX) | |
583 // Allow this to be set when invoking the browser and relayed along. | |
584 switches::kEnableSandboxLogging, | |
585 #endif | |
586 switches::kEnableSeccompSandbox, | |
587 switches::kEnableStatsTable, | |
588 switches::kEnableThreadedCompositing, | |
589 switches::kEnableVideoFullscreen, | |
590 switches::kEnableVideoLogging, | |
591 switches::kEnableVideoTrack, | |
592 switches::kFullMemoryCrashReport, | |
593 #if !defined (GOOGLE_CHROME_BUILD) | |
594 // These are unsupported and not fully tested modes, so don't enable them | |
595 // for official Google Chrome builds. | |
596 switches::kInProcessPlugins, | |
597 #endif // GOOGLE_CHROME_BUILD | |
598 switches::kInProcessWebGL, | |
599 switches::kJavaScriptFlags, | |
600 switches::kLoggingLevel, | |
601 switches::kHighLatencyAudio, | |
602 switches::kNoJsRandomness, | |
603 switches::kNoReferrers, | |
604 switches::kNoSandbox, | |
605 switches::kPlaybackMode, | |
606 switches::kPpapiOutOfProcess, | |
607 switches::kRecordMode, | |
608 switches::kRegisterPepperPlugins, | |
609 switches::kRemoteShellPort, | |
610 switches::kRendererAssertTest, | |
611 #if !defined(OFFICIAL_BUILD) | |
612 switches::kRendererCheckFalseTest, | |
613 #endif // !defined(OFFICIAL_BUILD) | |
614 switches::kRendererCrashTest, | |
615 switches::kRendererStartupDialog, | |
616 switches::kShowPaintRects, | |
617 switches::kSimpleDataSource, | |
618 switches::kTestSandbox, | |
619 switches::kTraceStartup, | |
620 // This flag needs to be propagated to the renderer process for | |
621 // --in-process-webgl. | |
622 switches::kUseGL, | |
623 switches::kUserAgent, | |
624 switches::kV, | |
625 switches::kVideoThreads, | |
626 switches::kVModule, | |
627 switches::kWebCoreLogChannels, | |
628 }; | |
629 renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, | |
630 arraysize(kSwitchNames)); | |
631 | |
632 // Disable databases in incognito mode. | |
633 if (browser_context()->IsOffTheRecord() && | |
634 !browser_cmd.HasSwitch(switches::kDisableDatabases)) { | |
635 renderer_cmd->AppendSwitch(switches::kDisableDatabases); | |
636 } | |
637 } | |
638 | |
639 base::ProcessHandle BrowserRenderProcessHost::GetHandle() { | |
640 // child_process_launcher_ is null either because we're in single process | |
641 // mode, we have done fast termination, or the process has crashed. | |
642 if (run_renderer_in_process() || !child_process_launcher_.get()) | |
643 return base::Process::Current().handle(); | |
644 | |
645 if (child_process_launcher_->IsStarting()) | |
646 return base::kNullProcessHandle; | |
647 | |
648 return child_process_launcher_->GetHandle(); | |
649 } | |
650 | |
651 bool BrowserRenderProcessHost::FastShutdownIfPossible() { | |
652 if (run_renderer_in_process()) | |
653 return false; // Single process mode can't do fast shutdown. | |
654 | |
655 if (!content::GetContentClient()->browser()->IsFastShutdownPossible()) | |
656 return false; | |
657 | |
658 if (!child_process_launcher_.get() || | |
659 child_process_launcher_->IsStarting() || | |
660 !GetHandle()) | |
661 return false; // Render process hasn't started or is probably crashed. | |
662 | |
663 // Test if there's an unload listener. | |
664 // NOTE: It's possible that an onunload listener may be installed | |
665 // while we're shutting down, so there's a small race here. Given that | |
666 // the window is small, it's unlikely that the web page has much | |
667 // state that will be lost by not calling its unload handlers properly. | |
668 if (!sudden_termination_allowed()) | |
669 return false; | |
670 | |
671 // Store the handle before it gets changed. | |
672 base::ProcessHandle handle = GetHandle(); | |
673 ProcessDied(handle, base::TERMINATION_STATUS_NORMAL_TERMINATION, 0, false); | |
674 fast_shutdown_started_ = true; | |
675 return true; | |
676 } | |
677 | |
678 void BrowserRenderProcessHost::DumpHandles() { | |
679 #if defined(OS_WIN) | |
680 Send(new ChildProcessMsg_DumpHandles()); | |
681 return; | |
682 #endif | |
683 | |
684 NOTIMPLEMENTED(); | |
685 } | |
686 | |
687 // This is a platform specific function for mapping a transport DIB given its id | |
688 TransportDIB* BrowserRenderProcessHost::MapTransportDIB( | |
689 TransportDIB::Id dib_id) { | |
690 #if defined(OS_WIN) | |
691 // On Windows we need to duplicate the handle from the remote process | |
692 HANDLE section = chrome::GetSectionFromProcess( | |
693 dib_id.handle, GetHandle(), false /* read write */); | |
694 return TransportDIB::Map(section); | |
695 #elif defined(OS_MACOSX) | |
696 // On OSX, the browser allocates all DIBs and keeps a file descriptor around | |
697 // for each. | |
698 return widget_helper_->MapTransportDIB(dib_id); | |
699 #elif defined(OS_POSIX) | |
700 return TransportDIB::Map(dib_id.shmkey); | |
701 #endif // defined(OS_POSIX) | |
702 } | |
703 | |
704 TransportDIB* BrowserRenderProcessHost::GetTransportDIB( | |
705 TransportDIB::Id dib_id) { | |
706 if (!TransportDIB::is_valid_id(dib_id)) | |
707 return NULL; | |
708 | |
709 const std::map<TransportDIB::Id, TransportDIB*>::iterator | |
710 i = cached_dibs_.find(dib_id); | |
711 if (i != cached_dibs_.end()) { | |
712 cached_dibs_cleaner_.Reset(); | |
713 return i->second; | |
714 } | |
715 | |
716 TransportDIB* dib = MapTransportDIB(dib_id); | |
717 if (!dib) | |
718 return NULL; | |
719 | |
720 if (cached_dibs_.size() >= MAX_MAPPED_TRANSPORT_DIBS) { | |
721 // Clean a single entry from the cache | |
722 std::map<TransportDIB::Id, TransportDIB*>::iterator smallest_iterator; | |
723 size_t smallest_size = std::numeric_limits<size_t>::max(); | |
724 | |
725 for (std::map<TransportDIB::Id, TransportDIB*>::iterator | |
726 i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { | |
727 if (i->second->size() <= smallest_size) { | |
728 smallest_iterator = i; | |
729 smallest_size = i->second->size(); | |
730 } | |
731 } | |
732 | |
733 delete smallest_iterator->second; | |
734 cached_dibs_.erase(smallest_iterator); | |
735 } | |
736 | |
737 cached_dibs_[dib_id] = dib; | |
738 cached_dibs_cleaner_.Reset(); | |
739 return dib; | |
740 } | |
741 | |
742 void BrowserRenderProcessHost::ClearTransportDIBCache() { | |
743 STLDeleteContainerPairSecondPointers( | |
744 cached_dibs_.begin(), cached_dibs_.end()); | |
745 cached_dibs_.clear(); | |
746 } | |
747 | |
748 void BrowserRenderProcessHost::SetCompositingSurface( | |
749 int render_widget_id, | |
750 gfx::PluginWindowHandle compositing_surface) { | |
751 widget_helper_->SetCompositingSurface(render_widget_id, compositing_surface); | |
752 } | |
753 | |
754 bool BrowserRenderProcessHost::Send(IPC::Message* msg) { | |
755 if (!channel_.get()) { | |
756 if (!is_initialized_) { | |
757 queued_messages_.push(msg); | |
758 return true; | |
759 } else { | |
760 delete msg; | |
761 return false; | |
762 } | |
763 } | |
764 | |
765 if (child_process_launcher_.get() && child_process_launcher_->IsStarting()) { | |
766 queued_messages_.push(msg); | |
767 return true; | |
768 } | |
769 | |
770 return channel_->Send(msg); | |
771 } | |
772 | |
773 bool BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { | |
774 // If we're about to be deleted, or have initiated the fast shutdown sequence, | |
775 // we ignore incoming messages. | |
776 | |
777 if (deleting_soon_ || fast_shutdown_started_) | |
778 return false; | |
779 | |
780 mark_child_process_activity_time(); | |
781 if (msg.routing_id() == MSG_ROUTING_CONTROL) { | |
782 // Dispatch control messages. | |
783 bool msg_is_ok = true; | |
784 IPC_BEGIN_MESSAGE_MAP_EX(BrowserRenderProcessHost, msg, msg_is_ok) | |
785 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ShutdownRequest, | |
786 OnShutdownRequest) | |
787 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DumpHandlesDone, | |
788 OnDumpHandlesDone) | |
789 IPC_MESSAGE_HANDLER(ViewHostMsg_SuddenTerminationChanged, | |
790 SuddenTerminationChanged) | |
791 IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction, | |
792 OnUserMetricsRecordAction) | |
793 IPC_MESSAGE_HANDLER(ViewHostMsg_RevealFolderInOS, OnRevealFolderInOS) | |
794 IPC_MESSAGE_HANDLER(ViewHostMsg_SavedPageAsMHTML, OnSavedPageAsMHTML) | |
795 IPC_MESSAGE_UNHANDLED_ERROR() | |
796 IPC_END_MESSAGE_MAP_EX() | |
797 | |
798 if (!msg_is_ok) { | |
799 // The message had a handler, but its de-serialization failed. | |
800 // We consider this a capital crime. Kill the renderer if we have one. | |
801 LOG(ERROR) << "bad message " << msg.type() << " terminating renderer."; | |
802 UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_BRPH")); | |
803 ReceivedBadMessage(); | |
804 } | |
805 return true; | |
806 } | |
807 | |
808 // Dispatch incoming messages to the appropriate RenderView/WidgetHost. | |
809 IPC::Channel::Listener* listener = GetListenerByID(msg.routing_id()); | |
810 if (!listener) { | |
811 if (msg.is_sync()) { | |
812 // The listener has gone away, so we must respond or else the caller will | |
813 // hang waiting for a reply. | |
814 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); | |
815 reply->set_reply_error(); | |
816 Send(reply); | |
817 } | |
818 return true; | |
819 } | |
820 return listener->OnMessageReceived(msg); | |
821 } | |
822 | |
823 void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) { | |
824 #if defined(IPC_MESSAGE_LOG_ENABLED) | |
825 Send(new ChildProcessMsg_SetIPCLoggingEnabled( | |
826 IPC::Logging::GetInstance()->Enabled())); | |
827 #endif | |
828 | |
829 // Make sure the child checks with us before exiting, so that we do not try | |
830 // to schedule a new navigation in a swapped out and exiting renderer. | |
831 Send(new ChildProcessMsg_AskBeforeShutdown()); | |
832 } | |
833 | |
834 void BrowserRenderProcessHost::OnChannelError() { | |
835 if (!channel_.get()) | |
836 return; | |
837 | |
838 // Store the handle before it gets changed. | |
839 base::ProcessHandle handle = GetHandle(); | |
840 | |
841 // child_process_launcher_ can be NULL in single process mode or if fast | |
842 // termination happened. | |
843 int exit_code = 0; | |
844 base::TerminationStatus status = | |
845 child_process_launcher_.get() ? | |
846 child_process_launcher_->GetChildTerminationStatus(&exit_code) : | |
847 base::TERMINATION_STATUS_NORMAL_TERMINATION; | |
848 | |
849 #if defined(OS_WIN) | |
850 if (!run_renderer_in_process()) { | |
851 if (status == base::TERMINATION_STATUS_STILL_RUNNING) { | |
852 HANDLE process = child_process_launcher_->GetHandle(); | |
853 child_process_watcher_.StartWatching( | |
854 new base::WaitableEvent(process), this); | |
855 return; | |
856 } | |
857 } | |
858 #endif | |
859 ProcessDied(handle, status, exit_code, false); | |
860 } | |
861 | |
862 // Called when the renderer process handle has been signaled. | |
863 void BrowserRenderProcessHost::OnWaitableEventSignaled( | |
864 base::WaitableEvent* waitable_event) { | |
865 #if defined (OS_WIN) | |
866 base::ProcessHandle handle = GetHandle(); | |
867 int exit_code = 0; | |
868 base::TerminationStatus status = | |
869 base::GetTerminationStatus(waitable_event->Release(), &exit_code); | |
870 delete waitable_event; | |
871 ProcessDied(handle, status, exit_code, true); | |
872 #endif | |
873 } | |
874 | |
875 void BrowserRenderProcessHost::ProcessDied(base::ProcessHandle handle, | |
876 base::TerminationStatus status, | |
877 int exit_code, | |
878 bool was_alive) { | |
879 // Our child process has died. If we didn't expect it, it's a crash. | |
880 // In any case, we need to let everyone know it's gone. | |
881 // The OnChannelError notification can fire multiple times due to nested sync | |
882 // calls to a renderer. If we don't have a valid channel here it means we | |
883 // already handled the error. | |
884 | |
885 RendererClosedDetails details(handle, status, exit_code, was_alive); | |
886 content::NotificationService::current()->Notify( | |
887 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
888 content::Source<RenderProcessHost>(this), | |
889 content::Details<RendererClosedDetails>(&details)); | |
890 | |
891 child_process_launcher_.reset(); | |
892 channel_.reset(); | |
893 | |
894 IDMap<IPC::Channel::Listener>::iterator iter(&listeners_); | |
895 while (!iter.IsAtEnd()) { | |
896 iter.GetCurrentValue()->OnMessageReceived( | |
897 ViewHostMsg_RenderViewGone(iter.GetCurrentKey(), | |
898 static_cast<int>(status), | |
899 exit_code)); | |
900 iter.Advance(); | |
901 } | |
902 | |
903 ClearTransportDIBCache(); | |
904 | |
905 // this object is not deleted at this point and may be reused later. | |
906 // TODO(darin): clean this up | |
907 } | |
908 | |
909 void BrowserRenderProcessHost::OnShutdownRequest() { | |
910 // Don't shutdown if there are pending RenderViews being swapped back in. | |
911 if (pending_views_) | |
912 return; | |
913 | |
914 // Notify any tabs that might have swapped out renderers from this process. | |
915 // They should not attempt to swap them back in. | |
916 content::NotificationService::current()->Notify( | |
917 content::NOTIFICATION_RENDERER_PROCESS_CLOSING, | |
918 content::Source<RenderProcessHost>(this), | |
919 content::NotificationService::NoDetails()); | |
920 | |
921 Send(new ChildProcessMsg_Shutdown()); | |
922 } | |
923 | |
924 void BrowserRenderProcessHost::OnDumpHandlesDone() { | |
925 Cleanup(); | |
926 } | |
927 | |
928 void BrowserRenderProcessHost::SuddenTerminationChanged(bool enabled) { | |
929 set_sudden_termination_allowed(enabled); | |
930 } | |
931 | |
932 void BrowserRenderProcessHost::SetBackgrounded(bool backgrounded) { | |
933 // Note: we always set the backgrounded_ value. If the process is NULL | |
934 // (and hence hasn't been created yet), we will set the process priority | |
935 // later when we create the process. | |
936 backgrounded_ = backgrounded; | |
937 if (!child_process_launcher_.get() || child_process_launcher_->IsStarting()) | |
938 return; | |
939 | |
940 #if defined(OS_WIN) | |
941 // The cbstext.dll loads as a global GetMessage hook in the browser process | |
942 // and intercepts/unintercepts the kernel32 API SetPriorityClass in a | |
943 // background thread. If the UI thread invokes this API just when it is | |
944 // intercepted the stack is messed up on return from the interceptor | |
945 // which causes random crashes in the browser process. Our hack for now | |
946 // is to not invoke the SetPriorityClass API if the dll is loaded. | |
947 if (GetModuleHandle(L"cbstext.dll")) | |
948 return; | |
949 #endif // OS_WIN | |
950 | |
951 child_process_launcher_->SetProcessBackgrounded(backgrounded); | |
952 } | |
953 | |
954 void BrowserRenderProcessHost::OnProcessLaunched() { | |
955 // No point doing anything, since this object will be destructed soon. We | |
956 // especially don't want to send the RENDERER_PROCESS_CREATED notification, | |
957 // since some clients might expect a RENDERER_PROCESS_TERMINATED afterwards to | |
958 // properly cleanup. | |
959 if (deleting_soon_) | |
960 return; | |
961 | |
962 if (child_process_launcher_.get()) { | |
963 if (!child_process_launcher_->GetHandle()) { | |
964 OnChannelError(); | |
965 return; | |
966 } | |
967 | |
968 child_process_launcher_->SetProcessBackgrounded(backgrounded_); | |
969 } | |
970 | |
971 if (max_page_id_ != -1) | |
972 Send(new ViewMsg_SetNextPageID(max_page_id_ + 1)); | |
973 | |
974 // NOTE: This needs to be before sending queued messages because | |
975 // ExtensionService uses this notification to initialize the renderer process | |
976 // with state that must be there before any JavaScript executes. | |
977 // | |
978 // The queued messages contain such things as "navigate". If this notification | |
979 // was after, we can end up executing JavaScript before the initialization | |
980 // happens. | |
981 content::NotificationService::current()->Notify( | |
982 content::NOTIFICATION_RENDERER_PROCESS_CREATED, | |
983 content::Source<RenderProcessHost>(this), | |
984 content::NotificationService::NoDetails()); | |
985 | |
986 while (!queued_messages_.empty()) { | |
987 Send(queued_messages_.front()); | |
988 queued_messages_.pop(); | |
989 } | |
990 } | |
991 | |
992 void BrowserRenderProcessHost::OnUserMetricsRecordAction( | |
993 const std::string& action) { | |
994 UserMetrics::RecordComputedAction(action); | |
995 } | |
996 | |
997 void BrowserRenderProcessHost::OnRevealFolderInOS(const FilePath& path) { | |
998 // Only honor the request if appropriate persmissions are granted. | |
999 if (ChildProcessSecurityPolicy::GetInstance()->CanReadFile(id(), path)) | |
1000 content::GetContentClient()->browser()->OpenItem(path); | |
1001 } | |
1002 | |
1003 void BrowserRenderProcessHost::OnSavedPageAsMHTML(int job_id, int64 data_size) { | |
1004 content::GetContentClient()->browser()->GetMHTMLGenerationManager()-> | |
1005 MHTMLGenerated(job_id, data_size); | |
1006 } | |
OLD | NEW |