OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/worker_host/worker_process_host.h" | |
6 | |
7 #include <set> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/base_switches.h" | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/callback.h" | |
15 #include "base/command_line.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "content/browser/appcache/appcache_dispatcher_host.h" | |
20 #include "content/browser/appcache/chrome_appcache_service.h" | |
21 #include "content/browser/browser_child_process_host_impl.h" | |
22 #include "content/browser/child_process_security_policy_impl.h" | |
23 #include "content/browser/devtools/worker_devtools_manager.h" | |
24 #include "content/browser/devtools/worker_devtools_message_filter.h" | |
25 #include "content/browser/fileapi/fileapi_message_filter.h" | |
26 #include "content/browser/frame_host/render_frame_host_delegate.h" | |
27 #include "content/browser/frame_host/render_frame_host_impl.h" | |
28 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" | |
29 #include "content/browser/loader/resource_message_filter.h" | |
30 #include "content/browser/message_port_message_filter.h" | |
31 #include "content/browser/message_port_service.h" | |
32 #include "content/browser/mime_registry_message_filter.h" | |
33 #include "content/browser/quota_dispatcher_host.h" | |
34 #include "content/browser/renderer_host/database_message_filter.h" | |
35 #include "content/browser/renderer_host/file_utilities_message_filter.h" | |
36 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
37 #include "content/browser/renderer_host/render_view_host_impl.h" | |
38 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" | |
39 #include "content/browser/renderer_host/websocket_dispatcher_host.h" | |
40 #include "content/browser/resource_context_impl.h" | |
41 #include "content/browser/worker_host/worker_message_filter.h" | |
42 #include "content/browser/worker_host/worker_service_impl.h" | |
43 #include "content/common/child_process_host_impl.h" | |
44 #include "content/common/view_messages.h" | |
45 #include "content/common/worker_messages.h" | |
46 #include "content/public/browser/browser_thread.h" | |
47 #include "content/public/browser/content_browser_client.h" | |
48 #include "content/public/browser/user_metrics.h" | |
49 #include "content/public/common/content_switches.h" | |
50 #include "content/public/common/resource_type.h" | |
51 #include "content/public/common/result_codes.h" | |
52 #include "content/public/common/sandboxed_process_launcher_delegate.h" | |
53 #include "ipc/ipc_switches.h" | |
54 #include "net/base/mime_util.h" | |
55 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
56 #include "net/url_request/url_request_context_getter.h" | |
57 #include "ui/base/ui_base_switches.h" | |
58 #include "webkit/browser/fileapi/file_system_context.h" | |
59 #include "webkit/browser/fileapi/sandbox_file_system_backend.h" | |
60 | |
61 #if defined(OS_WIN) | |
62 #include "content/common/sandbox_win.h" | |
63 #endif | |
64 | |
65 namespace content { | |
66 namespace { | |
67 | |
68 // NOTE: changes to this class need to be reviewed by the security team. | |
69 class WorkerSandboxedProcessLauncherDelegate | |
70 : public content::SandboxedProcessLauncherDelegate { | |
71 public: | |
72 WorkerSandboxedProcessLauncherDelegate(ChildProcessHost* host, | |
73 bool debugging_child) | |
74 #if defined(OS_POSIX) | |
75 : ipc_fd_(host->TakeClientFileDescriptor()), | |
76 debugging_child_(debugging_child) | |
77 #endif // OS_POSIX | |
78 {} | |
79 | |
80 virtual ~WorkerSandboxedProcessLauncherDelegate() {} | |
81 | |
82 #if defined(OS_WIN) | |
83 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy, | |
84 bool* success) { | |
85 AddBaseHandleClosePolicy(policy); | |
86 } | |
87 #elif defined(OS_POSIX) | |
88 virtual bool ShouldUseZygote() OVERRIDE { | |
89 return !debugging_child_; | |
90 } | |
91 virtual int GetIpcFd() OVERRIDE { | |
92 return ipc_fd_; | |
93 } | |
94 #endif // OS_WIN | |
95 | |
96 private: | |
97 #if defined(OS_POSIX) | |
98 int ipc_fd_; | |
99 bool debugging_child_; | |
100 #endif // OS_POSIX | |
101 }; | |
102 | |
103 // Notifies RenderViewHost that one or more worker objects crashed. | |
104 void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) { | |
105 RenderFrameHostImpl* host = | |
106 RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id); | |
107 if (host) | |
108 host->delegate()->WorkerCrashed(host); | |
109 } | |
110 | |
111 void WorkerCreatedCallback(int render_process_id, | |
112 int render_frame_id, | |
113 int worker_process_id) { | |
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
115 RenderFrameHost* render_frame_host = | |
116 RenderFrameHost::FromID(render_process_id, render_frame_id); | |
117 if (!render_frame_host) | |
118 return; | |
119 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
120 GetContentClient()->browser()->WorkerProcessCreated(site_instance, | |
121 worker_process_id); | |
122 } | |
123 | |
124 void WorkerTerminatedCallback(int render_process_id, | |
125 int render_frame_id, | |
126 int worker_process_id) { | |
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
128 RenderFrameHost* render_frame_host = | |
129 RenderFrameHost::FromID(render_process_id, render_frame_id); | |
130 if (!render_frame_host) | |
131 return; | |
132 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
133 GetContentClient()->browser()->WorkerProcessTerminated(site_instance, | |
134 worker_process_id); | |
135 } | |
136 | |
137 } // namespace | |
138 | |
139 WorkerProcessHost::WorkerProcessHost( | |
140 ResourceContext* resource_context, | |
141 const WorkerStoragePartition& partition) | |
142 : resource_context_(resource_context), | |
143 partition_(partition), | |
144 process_launched_(false), | |
145 weak_factory_(this) { | |
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
147 DCHECK(resource_context_); | |
148 process_.reset( | |
149 new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this)); | |
150 } | |
151 | |
152 WorkerProcessHost::~WorkerProcessHost() { | |
153 // If we crashed, tell the RenderViewHosts. | |
154 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
155 if (!i->load_failed()) { | |
156 const WorkerDocumentSet::DocumentInfoSet& parents = | |
157 i->worker_document_set()->documents(); | |
158 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = | |
159 parents.begin(); parent_iter != parents.end(); ++parent_iter) { | |
160 BrowserThread::PostTask( | |
161 BrowserThread::UI, FROM_HERE, | |
162 base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(), | |
163 parent_iter->render_frame_id())); | |
164 } | |
165 } | |
166 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( | |
167 this, i->worker_route_id()); | |
168 } | |
169 | |
170 ChildProcessSecurityPolicyImpl::GetInstance()->Remove( | |
171 process_->GetData().id); | |
172 } | |
173 | |
174 bool WorkerProcessHost::Send(IPC::Message* message) { | |
175 return process_->Send(message); | |
176 } | |
177 | |
178 bool WorkerProcessHost::Init(int render_process_id, int render_frame_id) { | |
179 std::string channel_id = process_->GetHost()->CreateChannel(); | |
180 if (channel_id.empty()) | |
181 return false; | |
182 | |
183 #if defined(OS_LINUX) | |
184 int flags = ChildProcessHost::CHILD_ALLOW_SELF; | |
185 #else | |
186 int flags = ChildProcessHost::CHILD_NORMAL; | |
187 #endif | |
188 | |
189 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); | |
190 if (exe_path.empty()) | |
191 return false; | |
192 | |
193 CommandLine* cmd_line = new CommandLine(exe_path); | |
194 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess); | |
195 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | |
196 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); | |
197 cmd_line->AppendSwitchASCII(switches::kLang, locale); | |
198 | |
199 static const char* const kSwitchNames[] = { | |
200 switches::kDisableApplicationCache, | |
201 switches::kDisableDatabases, | |
202 #if defined(OS_WIN) | |
203 switches::kDisableDesktopNotifications, | |
204 #endif | |
205 switches::kDisableFileSystem, | |
206 switches::kDisableSeccompFilterSandbox, | |
207 switches::kEnableExperimentalWebPlatformFeatures, | |
208 switches::kEnablePreciseMemoryInfo, | |
209 #if defined(OS_MACOSX) | |
210 switches::kEnableSandboxLogging, | |
211 #endif | |
212 switches::kJavaScriptFlags, | |
213 switches::kNoSandbox | |
214 }; | |
215 cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames, | |
216 arraysize(kSwitchNames)); | |
217 | |
218 bool debugging_child = false; | |
219 #if defined(OS_POSIX) | |
220 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
221 switches::kWaitForDebuggerChildren)) { | |
222 // Look to pass-on the kWaitForDebugger flag. | |
223 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
224 switches::kWaitForDebuggerChildren); | |
225 if (value.empty() || value == switches::kWorkerProcess) { | |
226 cmd_line->AppendSwitch(switches::kWaitForDebugger); | |
227 debugging_child = true; | |
228 } | |
229 } | |
230 #endif | |
231 | |
232 process_->Launch( | |
233 new WorkerSandboxedProcessLauncherDelegate(process_->GetHost(), | |
234 debugging_child), | |
235 cmd_line); | |
236 | |
237 ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker( | |
238 process_->GetData().id, render_process_id); | |
239 CreateMessageFilters(render_process_id); | |
240 | |
241 BrowserThread::PostTask( | |
242 BrowserThread::UI, FROM_HERE, | |
243 base::Bind(&WorkerCreatedCallback, | |
244 render_process_id, | |
245 render_frame_id, | |
246 process_->GetData().id)); | |
247 return true; | |
248 } | |
249 | |
250 void WorkerProcessHost::CreateMessageFilters(int render_process_id) { | |
251 ChromeBlobStorageContext* blob_storage_context = | |
252 GetChromeBlobStorageContextForResourceContext(resource_context_); | |
253 StreamContext* stream_context = | |
254 GetStreamContextForResourceContext(resource_context_); | |
255 | |
256 net::URLRequestContextGetter* url_request_context = | |
257 partition_.url_request_context(); | |
258 | |
259 ResourceMessageFilter::GetContextsCallback get_contexts_callback( | |
260 base::Bind(&WorkerProcessHost::GetContexts, | |
261 base::Unretained(this))); | |
262 | |
263 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( | |
264 process_->GetData().id, PROCESS_TYPE_WORKER, | |
265 partition_.appcache_service(), | |
266 blob_storage_context, | |
267 partition_.filesystem_context(), | |
268 partition_.service_worker_context(), | |
269 get_contexts_callback); | |
270 process_->AddFilter(resource_message_filter); | |
271 | |
272 MessagePortMessageFilter* message_port_message_filter = | |
273 new MessagePortMessageFilter( | |
274 base::Bind(&WorkerServiceImpl::next_worker_route_id, | |
275 base::Unretained(WorkerServiceImpl::GetInstance()))); | |
276 process_->AddFilter(message_port_message_filter); | |
277 worker_message_filter_ = new WorkerMessageFilter(render_process_id, | |
278 resource_context_, | |
279 partition_, | |
280 message_port_message_filter); | |
281 process_->AddFilter(worker_message_filter_.get()); | |
282 process_->AddFilter(new AppCacheDispatcherHost( | |
283 partition_.appcache_service(), process_->GetData().id)); | |
284 process_->AddFilter(new FileAPIMessageFilter( | |
285 process_->GetData().id, | |
286 url_request_context, | |
287 partition_.filesystem_context(), | |
288 blob_storage_context, | |
289 stream_context)); | |
290 process_->AddFilter(new FileUtilitiesMessageFilter( | |
291 process_->GetData().id)); | |
292 process_->AddFilter(new MimeRegistryMessageFilter()); | |
293 process_->AddFilter(new DatabaseMessageFilter(partition_.database_tracker())); | |
294 process_->AddFilter(new QuotaDispatcherHost( | |
295 process_->GetData().id, | |
296 partition_.quota_manager(), | |
297 GetContentClient()->browser()->CreateQuotaPermissionContext())); | |
298 | |
299 SocketStreamDispatcherHost::GetRequestContextCallback | |
300 request_context_callback( | |
301 base::Bind(&WorkerProcessHost::GetRequestContext, | |
302 base::Unretained(this))); | |
303 | |
304 SocketStreamDispatcherHost* socket_stream_dispatcher_host = | |
305 new SocketStreamDispatcherHost( | |
306 render_process_id, | |
307 request_context_callback, | |
308 resource_context_); | |
309 socket_stream_dispatcher_host_ = socket_stream_dispatcher_host; | |
310 process_->AddFilter(socket_stream_dispatcher_host); | |
311 | |
312 WebSocketDispatcherHost::GetRequestContextCallback | |
313 websocket_request_context_callback( | |
314 base::Bind(&WorkerProcessHost::GetRequestContext, | |
315 base::Unretained(this), | |
316 ResourceType::SUB_RESOURCE)); | |
317 | |
318 process_->AddFilter(new WebSocketDispatcherHost( | |
319 render_process_id, websocket_request_context_callback)); | |
320 | |
321 process_->AddFilter(new WorkerDevToolsMessageFilter(process_->GetData().id)); | |
322 process_->AddFilter( | |
323 new IndexedDBDispatcherHost(process_->GetData().id, | |
324 url_request_context, | |
325 partition_.indexed_db_context(), | |
326 blob_storage_context)); | |
327 } | |
328 | |
329 void WorkerProcessHost::CreateWorker(const WorkerInstance& instance, | |
330 bool pause_on_start) { | |
331 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( | |
332 process_->GetData().id, instance.url()); | |
333 | |
334 instances_.push_back(instance); | |
335 | |
336 WorkerProcessMsg_CreateWorker_Params params; | |
337 params.url = instance.url(); | |
338 params.name = instance.name(); | |
339 params.content_security_policy = instance.content_security_policy(); | |
340 params.security_policy_type = instance.security_policy_type(); | |
341 params.pause_on_start = pause_on_start; | |
342 params.route_id = instance.worker_route_id(); | |
343 Send(new WorkerProcessMsg_CreateWorker(params)); | |
344 | |
345 UpdateTitle(); | |
346 | |
347 // Walk all pending filters and let them know the worker has been created | |
348 // (could be more than one in the case where we had to queue up worker | |
349 // creation because the worker process limit was reached). | |
350 for (WorkerInstance::FilterList::const_iterator i = | |
351 instance.filters().begin(); | |
352 i != instance.filters().end(); ++i) { | |
353 i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id())); | |
354 } | |
355 } | |
356 | |
357 bool WorkerProcessHost::FilterMessage(const IPC::Message& message, | |
358 WorkerMessageFilter* filter) { | |
359 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
360 if (!i->closed() && i->HasFilter(filter, message.routing_id())) { | |
361 RelayMessage(message, filter, &(*i)); | |
362 return true; | |
363 } | |
364 } | |
365 | |
366 return false; | |
367 } | |
368 | |
369 void WorkerProcessHost::OnProcessLaunched() { | |
370 process_launched_ = true; | |
371 | |
372 WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated(); | |
373 } | |
374 | |
375 bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) { | |
376 bool handled = true; | |
377 IPC_BEGIN_MESSAGE_MAP(WorkerProcessHost, message) | |
378 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed, | |
379 OnWorkerContextClosed) | |
380 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed, | |
381 OnWorkerContextDestroyed) | |
382 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded, | |
383 OnWorkerScriptLoaded) | |
384 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed, | |
385 OnWorkerScriptLoadFailed) | |
386 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected, | |
387 OnWorkerConnected) | |
388 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase) | |
389 IPC_MESSAGE_HANDLER_DELAY_REPLY( | |
390 WorkerProcessHostMsg_RequestFileSystemAccessSync, | |
391 OnRequestFileSystemAccess) | |
392 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB) | |
393 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker, | |
394 OnForceKillWorkerProcess) | |
395 IPC_MESSAGE_UNHANDLED(handled = false) | |
396 IPC_END_MESSAGE_MAP() | |
397 | |
398 return handled; | |
399 } | |
400 | |
401 // Sent to notify the browser process when a worker context invokes close(), so | |
402 // no new connections are sent to shared workers. | |
403 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) { | |
404 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
405 if (i->worker_route_id() == worker_route_id) { | |
406 // Set the closed flag - this will stop any further messages from | |
407 // being sent to the worker (messages can still be sent from the worker, | |
408 // for exception reporting, etc). | |
409 i->set_closed(true); | |
410 break; | |
411 } | |
412 } | |
413 } | |
414 | |
415 void WorkerProcessHost::OnWorkerContextDestroyed(int worker_route_id) { | |
416 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( | |
417 this, worker_route_id); | |
418 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
419 if (i->worker_route_id() == worker_route_id) { | |
420 instances_.erase(i); | |
421 UpdateTitle(); | |
422 return; | |
423 } | |
424 } | |
425 } | |
426 | |
427 void WorkerProcessHost::OnWorkerScriptLoaded(int worker_route_id) { | |
428 WorkerDevToolsManager::GetInstance()->WorkerContextStarted(this, | |
429 worker_route_id); | |
430 } | |
431 | |
432 void WorkerProcessHost::OnWorkerScriptLoadFailed(int worker_route_id) { | |
433 bool shutdown = true; | |
434 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
435 if (i->worker_route_id() != worker_route_id) { | |
436 shutdown = false; | |
437 continue; | |
438 } | |
439 i->set_load_failed(true); | |
440 for (WorkerInstance::FilterList::const_iterator j = i->filters().begin(); | |
441 j != i->filters().end(); ++j) { | |
442 j->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(j->route_id())); | |
443 } | |
444 } | |
445 if (shutdown) { | |
446 base::KillProcess( | |
447 process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false); | |
448 } | |
449 } | |
450 | |
451 void WorkerProcessHost::OnWorkerConnected(int message_port_id, | |
452 int worker_route_id) { | |
453 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
454 if (i->worker_route_id() != worker_route_id) | |
455 continue; | |
456 for (WorkerInstance::FilterList::const_iterator j = i->filters().begin(); | |
457 j != i->filters().end(); ++j) { | |
458 if (j->message_port_id() != message_port_id) | |
459 continue; | |
460 j->filter()->Send(new ViewMsg_WorkerConnected(j->route_id())); | |
461 return; | |
462 } | |
463 } | |
464 } | |
465 | |
466 void WorkerProcessHost::OnAllowDatabase(int worker_route_id, | |
467 const GURL& url, | |
468 const base::string16& name, | |
469 const base::string16& display_name, | |
470 unsigned long estimated_size, | |
471 bool* result) { | |
472 *result = GetContentClient()->browser()->AllowWorkerDatabase( | |
473 url, name, display_name, estimated_size, resource_context_, | |
474 GetRenderFrameIDsForWorker(worker_route_id)); | |
475 } | |
476 | |
477 void WorkerProcessHost::OnRequestFileSystemAccess(int worker_route_id, | |
478 const GURL& url, | |
479 IPC::Message* reply_msg) { | |
480 GetContentClient()->browser()->AllowWorkerFileSystem( | |
481 url, | |
482 resource_context_, | |
483 GetRenderFrameIDsForWorker(worker_route_id), | |
484 base::Bind(&WorkerProcessHost::OnRequestFileSystemAccessResponse, | |
485 weak_factory_.GetWeakPtr(), | |
486 base::Passed(scoped_ptr<IPC::Message>(reply_msg)))); | |
487 } | |
488 | |
489 void WorkerProcessHost::OnRequestFileSystemAccessResponse( | |
490 scoped_ptr<IPC::Message> reply_msg, | |
491 bool allowed) { | |
492 WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams( | |
493 reply_msg.get(), | |
494 allowed); | |
495 Send(reply_msg.release()); | |
496 } | |
497 | |
498 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id, | |
499 const GURL& url, | |
500 const base::string16& name, | |
501 bool* result) { | |
502 *result = GetContentClient()->browser()->AllowWorkerIndexedDB( | |
503 url, name, resource_context_, | |
504 GetRenderFrameIDsForWorker(worker_route_id)); | |
505 } | |
506 | |
507 void WorkerProcessHost::OnForceKillWorkerProcess() { | |
508 if (process_ && process_launched_) | |
509 base::KillProcess( | |
510 process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false); | |
511 else | |
512 RecordAction(base::UserMetricsAction("WorkerProcess_BadProcessToKill")); | |
513 } | |
514 | |
515 void WorkerProcessHost::RelayMessage( | |
516 const IPC::Message& message, | |
517 WorkerMessageFilter* incoming_filter, | |
518 WorkerInstance* instance) { | |
519 if (message.type() == WorkerMsg_Connect::ID) { | |
520 // Crack the SharedWorker Connect message to setup routing for the port. | |
521 WorkerMsg_Connect::Param params; | |
522 if (!WorkerMsg_Connect::Read(&message, ¶ms)) | |
523 return; | |
524 | |
525 int sent_message_port_id = params.a; | |
526 int new_routing_id = params.b; | |
527 new_routing_id = worker_message_filter_->GetNextRoutingID(); | |
528 MessagePortService::GetInstance()->UpdateMessagePort( | |
529 sent_message_port_id, | |
530 worker_message_filter_->message_port_message_filter(), | |
531 new_routing_id); | |
532 | |
533 instance->SetMessagePortID(incoming_filter, | |
534 message.routing_id(), | |
535 sent_message_port_id); | |
536 // Resend the message with the new routing id. | |
537 worker_message_filter_->Send(new WorkerMsg_Connect( | |
538 instance->worker_route_id(), sent_message_port_id, new_routing_id)); | |
539 | |
540 // Send any queued messages for the sent port. | |
541 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible( | |
542 sent_message_port_id); | |
543 } else { | |
544 IPC::Message* new_message = new IPC::Message(message); | |
545 new_message->set_routing_id(instance->worker_route_id()); | |
546 worker_message_filter_->Send(new_message); | |
547 return; | |
548 } | |
549 } | |
550 | |
551 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() { | |
552 if (!instances_.size() && socket_stream_dispatcher_host_.get()) { | |
553 // We can assume that this object is going to delete, because | |
554 // currently a WorkerInstance will never be added to a WorkerProcessHost | |
555 // once it is initialized. | |
556 | |
557 // SocketStreamDispatcherHost should be notified now that the worker | |
558 // process will shutdown soon. | |
559 socket_stream_dispatcher_host_->Shutdown(); | |
560 socket_stream_dispatcher_host_ = NULL; | |
561 } | |
562 } | |
563 | |
564 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) { | |
565 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { | |
566 bool shutdown = false; | |
567 i->RemoveFilters(filter); | |
568 | |
569 int render_frame_id = 0; | |
570 const WorkerDocumentSet::DocumentInfoSet& documents = | |
571 i->worker_document_set()->documents(); | |
572 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
573 documents.begin(); doc != documents.end(); ++doc) { | |
574 if (doc->filter() == filter) { | |
575 render_frame_id = doc->render_frame_id(); | |
576 break; | |
577 } | |
578 } | |
579 i->worker_document_set()->RemoveAll(filter); | |
580 if (i->worker_document_set()->IsEmpty()) { | |
581 shutdown = true; | |
582 } | |
583 if (shutdown) { | |
584 BrowserThread::PostTask( | |
585 BrowserThread::UI, FROM_HERE, | |
586 base::Bind(&WorkerTerminatedCallback, | |
587 filter->render_process_id(), | |
588 render_frame_id, | |
589 process_->GetData().id)); | |
590 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); | |
591 i = instances_.erase(i); | |
592 } else { | |
593 ++i; | |
594 } | |
595 } | |
596 ShutdownSocketStreamDispatcherHostIfNecessary(); | |
597 } | |
598 | |
599 bool WorkerProcessHost::CanShutdown() { | |
600 return instances_.empty(); | |
601 } | |
602 | |
603 void WorkerProcessHost::UpdateTitle() { | |
604 std::set<std::string> titles; | |
605 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
606 // Allow the embedder first crack at special casing the title. | |
607 std::string title = GetContentClient()->browser()-> | |
608 GetWorkerProcessTitle(i->url(), resource_context_); | |
609 | |
610 if (title.empty()) { | |
611 title = net::registry_controlled_domains::GetDomainAndRegistry( | |
612 i->url(), | |
613 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | |
614 } | |
615 | |
616 // Use the host name if the domain is empty, i.e. localhost or IP address. | |
617 if (title.empty()) | |
618 title = i->url().host(); | |
619 | |
620 // If the host name is empty, i.e. file url, use the path. | |
621 if (title.empty()) | |
622 title = i->url().path(); | |
623 titles.insert(title); | |
624 } | |
625 | |
626 std::string display_title; | |
627 for (std::set<std::string>::iterator i = titles.begin(); | |
628 i != titles.end(); ++i) { | |
629 if (!display_title.empty()) | |
630 display_title += ", "; | |
631 display_title += *i; | |
632 } | |
633 | |
634 process_->SetName(base::UTF8ToUTF16(display_title)); | |
635 } | |
636 | |
637 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter, | |
638 unsigned long long document_id) { | |
639 // Walk all instances and remove the document from their document set. | |
640 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { | |
641 int render_frame_id = 0; | |
642 const WorkerDocumentSet::DocumentInfoSet& documents = | |
643 i->worker_document_set()->documents(); | |
644 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
645 documents.begin(); doc != documents.end(); ++doc) { | |
646 if (doc->filter() == filter && doc->document_id() == document_id) { | |
647 render_frame_id = doc->render_frame_id(); | |
648 break; | |
649 } | |
650 } | |
651 i->worker_document_set()->Remove(filter, document_id); | |
652 if (i->worker_document_set()->IsEmpty()) { | |
653 BrowserThread::PostTask( | |
654 BrowserThread::UI, FROM_HERE, | |
655 base::Bind(&WorkerTerminatedCallback, | |
656 filter->render_process_id(), | |
657 render_frame_id, | |
658 process_->GetData().id)); | |
659 // This worker has no more associated documents - shut it down. | |
660 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); | |
661 i = instances_.erase(i); | |
662 } else { | |
663 ++i; | |
664 } | |
665 } | |
666 ShutdownSocketStreamDispatcherHostIfNecessary(); | |
667 } | |
668 | |
669 void WorkerProcessHost::TerminateWorker(int worker_route_id) { | |
670 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id)); | |
671 } | |
672 | |
673 void WorkerProcessHost::SetBackgrounded(bool backgrounded) { | |
674 process_->SetBackgrounded(backgrounded); | |
675 } | |
676 | |
677 const ChildProcessData& WorkerProcessHost::GetData() { | |
678 return process_->GetData(); | |
679 } | |
680 | |
681 std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker( | |
682 int worker_route_id) { | |
683 std::vector<std::pair<int, int> > result; | |
684 WorkerProcessHost::Instances::const_iterator i; | |
685 for (i = instances_.begin(); i != instances_.end(); ++i) { | |
686 if (i->worker_route_id() != worker_route_id) | |
687 continue; | |
688 const WorkerDocumentSet::DocumentInfoSet& documents = | |
689 i->worker_document_set()->documents(); | |
690 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
691 documents.begin(); doc != documents.end(); ++doc) { | |
692 result.push_back( | |
693 std::make_pair(doc->render_process_id(), doc->render_frame_id())); | |
694 } | |
695 break; | |
696 } | |
697 return result; | |
698 } | |
699 | |
700 void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request& request, | |
701 ResourceContext** resource_context, | |
702 net::URLRequestContext** request_context) { | |
703 *resource_context = resource_context_; | |
704 *request_context = partition_.url_request_context()->GetURLRequestContext(); | |
705 } | |
706 | |
707 net::URLRequestContext* WorkerProcessHost::GetRequestContext( | |
708 ResourceType::Type resource_type) { | |
709 return partition_.url_request_context()->GetURLRequestContext(); | |
710 } | |
711 | |
712 WorkerProcessHost::WorkerInstance::WorkerInstance( | |
713 const GURL& url, | |
714 const base::string16& name, | |
715 const base::string16& content_security_policy, | |
716 blink::WebContentSecurityPolicyType security_policy_type, | |
717 int worker_route_id, | |
718 int render_frame_id, | |
719 ResourceContext* resource_context, | |
720 const WorkerStoragePartition& partition) | |
721 : url_(url), | |
722 closed_(false), | |
723 name_(name), | |
724 content_security_policy_(content_security_policy), | |
725 security_policy_type_(security_policy_type), | |
726 worker_route_id_(worker_route_id), | |
727 render_frame_id_(render_frame_id), | |
728 worker_document_set_(new WorkerDocumentSet()), | |
729 resource_context_(resource_context), | |
730 partition_(partition), | |
731 load_failed_(false) { | |
732 DCHECK(resource_context_); | |
733 } | |
734 | |
735 WorkerProcessHost::WorkerInstance::~WorkerInstance() { | |
736 } | |
737 | |
738 void WorkerProcessHost::WorkerInstance::SetMessagePortID( | |
739 WorkerMessageFilter* filter, | |
740 int route_id, | |
741 int message_port_id) { | |
742 for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) { | |
743 if (i->filter() == filter && i->route_id() == route_id) { | |
744 i->set_message_port_id(message_port_id); | |
745 return; | |
746 } | |
747 } | |
748 } | |
749 | |
750 // Compares an instance based on the algorithm in the WebWorkers spec - an | |
751 // instance matches if the origins of the URLs match, and: | |
752 // a) the names are non-empty and equal | |
753 // -or- | |
754 // b) the names are both empty, and the urls are equal | |
755 bool WorkerProcessHost::WorkerInstance::Matches( | |
756 const GURL& match_url, | |
757 const base::string16& match_name, | |
758 const WorkerStoragePartition& partition, | |
759 ResourceContext* resource_context) const { | |
760 // Only match open shared workers. | |
761 if (closed_) | |
762 return false; | |
763 | |
764 // ResourceContext equivalence is being used as a proxy to ensure we only | |
765 // matched shared workers within the same BrowserContext. | |
766 if (resource_context_ != resource_context) | |
767 return false; | |
768 | |
769 // We must be in the same storage partition otherwise sharing will violate | |
770 // isolation. | |
771 if (!partition_.Equals(partition)) | |
772 return false; | |
773 | |
774 if (url_.GetOrigin() != match_url.GetOrigin()) | |
775 return false; | |
776 | |
777 if (name_.empty() && match_name.empty()) | |
778 return url_ == match_url; | |
779 | |
780 return name_ == match_name; | |
781 } | |
782 | |
783 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter, | |
784 int route_id) { | |
785 CHECK(filter); | |
786 if (!HasFilter(filter, route_id)) { | |
787 FilterInfo info(filter, route_id); | |
788 filters_.push_back(info); | |
789 } | |
790 } | |
791 | |
792 void WorkerProcessHost::WorkerInstance::RemoveFilter( | |
793 WorkerMessageFilter* filter, int route_id) { | |
794 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { | |
795 if (i->filter() == filter && i->route_id() == route_id) | |
796 i = filters_.erase(i); | |
797 else | |
798 ++i; | |
799 } | |
800 // Should not be duplicate copies in the filter set. | |
801 DCHECK(!HasFilter(filter, route_id)); | |
802 } | |
803 | |
804 void WorkerProcessHost::WorkerInstance::RemoveFilters( | |
805 WorkerMessageFilter* filter) { | |
806 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { | |
807 if (i->filter() == filter) | |
808 i = filters_.erase(i); | |
809 else | |
810 ++i; | |
811 } | |
812 } | |
813 | |
814 bool WorkerProcessHost::WorkerInstance::HasFilter( | |
815 WorkerMessageFilter* filter, int route_id) const { | |
816 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end(); | |
817 ++i) { | |
818 if (i->filter() == filter && i->route_id() == route_id) | |
819 return true; | |
820 } | |
821 return false; | |
822 } | |
823 | |
824 bool WorkerProcessHost::WorkerInstance::FrameIsParent( | |
825 int render_process_id, int render_frame_id) const { | |
826 const WorkerDocumentSet::DocumentInfoSet& parents = | |
827 worker_document_set()->documents(); | |
828 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = | |
829 parents.begin(); | |
830 parent_iter != parents.end(); ++parent_iter) { | |
831 if (parent_iter->render_process_id() == render_process_id && | |
832 parent_iter->render_frame_id() == render_frame_id) { | |
833 return true; | |
834 } | |
835 } | |
836 return false; | |
837 } | |
838 | |
839 WorkerProcessHost::WorkerInstance::FilterInfo | |
840 WorkerProcessHost::WorkerInstance::GetFilter() const { | |
841 DCHECK(NumFilters() == 1); | |
842 return *filters_.begin(); | |
843 } | |
844 | |
845 } // namespace content | |
OLD | NEW |