Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / worker_host / worker_process_host.cc
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/result_codes.h"
51 #include "content/public/common/sandboxed_process_launcher_delegate.h"
52 #include "ipc/ipc_switches.h"
53 #include "net/base/mime_util.h"
54 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
55 #include "net/url_request/url_request_context_getter.h"
56 #include "ui/base/ui_base_switches.h"
57 #include "webkit/browser/fileapi/file_system_context.h"
58 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
59 #include "webkit/common/resource_type.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   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146   DCHECK(resource_context_);
147   process_.reset(
148       new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this));
149 }
150
151 WorkerProcessHost::~WorkerProcessHost() {
152   // If we crashed, tell the RenderViewHosts.
153   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
154     if (!i->load_failed()) {
155       const WorkerDocumentSet::DocumentInfoSet& parents =
156           i->worker_document_set()->documents();
157       for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
158                parents.begin(); parent_iter != parents.end(); ++parent_iter) {
159         BrowserThread::PostTask(
160             BrowserThread::UI, FROM_HERE,
161             base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(),
162                        parent_iter->render_frame_id()));
163       }
164     }
165     WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
166         this, i->worker_route_id());
167   }
168
169   ChildProcessSecurityPolicyImpl::GetInstance()->Remove(
170       process_->GetData().id);
171 }
172
173 bool WorkerProcessHost::Send(IPC::Message* message) {
174   return process_->Send(message);
175 }
176
177 bool WorkerProcessHost::Init(int render_process_id, int render_frame_id) {
178   std::string channel_id = process_->GetHost()->CreateChannel();
179   if (channel_id.empty())
180     return false;
181
182 #if defined(OS_LINUX)
183   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
184 #else
185   int flags = ChildProcessHost::CHILD_NORMAL;
186 #endif
187
188   base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
189   if (exe_path.empty())
190     return false;
191
192   CommandLine* cmd_line = new CommandLine(exe_path);
193   cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess);
194   cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
195   std::string locale = GetContentClient()->browser()->GetApplicationLocale();
196   cmd_line->AppendSwitchASCII(switches::kLang, locale);
197
198   static const char* const kSwitchNames[] = {
199     switches::kDisableApplicationCache,
200     switches::kDisableDatabases,
201 #if defined(OS_WIN)
202     switches::kDisableDesktopNotifications,
203 #endif
204     switches::kDisableFileSystem,
205     switches::kDisableSeccompFilterSandbox,
206     switches::kEnableExperimentalWebPlatformFeatures,
207     switches::kEnablePreciseMemoryInfo,
208     switches::kEnableServiceWorker,
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 msg_is_ok = true;
377   bool handled = true;
378   IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
379     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
380                         OnWorkerContextClosed)
381     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed,
382                         OnWorkerContextDestroyed)
383     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded,
384                         OnWorkerScriptLoaded)
385     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed,
386                         OnWorkerScriptLoadFailed)
387     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected,
388                         OnWorkerConnected)
389     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
390     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem)
391     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB)
392     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker,
393                         OnForceKillWorkerProcess)
394     IPC_MESSAGE_UNHANDLED(handled = false)
395   IPC_END_MESSAGE_MAP_EX()
396
397   if (!msg_is_ok) {
398     NOTREACHED();
399     RecordAction(base::UserMetricsAction("BadMessageTerminate_WPH"));
400     base::KillProcess(
401         process_->GetData().handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
402   }
403
404   return handled;
405 }
406
407 // Sent to notify the browser process when a worker context invokes close(), so
408 // no new connections are sent to shared workers.
409 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) {
410   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
411     if (i->worker_route_id() == worker_route_id) {
412       // Set the closed flag - this will stop any further messages from
413       // being sent to the worker (messages can still be sent from the worker,
414       // for exception reporting, etc).
415       i->set_closed(true);
416       break;
417     }
418   }
419 }
420
421 void WorkerProcessHost::OnWorkerContextDestroyed(int worker_route_id) {
422   WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
423       this, worker_route_id);
424   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
425     if (i->worker_route_id() == worker_route_id) {
426       instances_.erase(i);
427       UpdateTitle();
428       return;
429     }
430   }
431 }
432
433 void WorkerProcessHost::OnWorkerScriptLoaded(int worker_route_id) {
434   WorkerDevToolsManager::GetInstance()->WorkerContextStarted(this,
435                                                              worker_route_id);
436 }
437
438 void WorkerProcessHost::OnWorkerScriptLoadFailed(int worker_route_id) {
439   bool shutdown = true;
440   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
441     if (i->worker_route_id() != worker_route_id) {
442       shutdown = false;
443       continue;
444     }
445     i->set_load_failed(true);
446     for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
447           j != i->filters().end(); ++j) {
448       j->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(j->route_id()));
449     }
450   }
451   if (shutdown) {
452     base::KillProcess(
453           process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
454   }
455 }
456
457 void WorkerProcessHost::OnWorkerConnected(int message_port_id,
458                                           int worker_route_id) {
459   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
460     if (i->worker_route_id() != worker_route_id)
461       continue;
462     for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
463           j != i->filters().end(); ++j) {
464       if (j->message_port_id() != message_port_id)
465         continue;
466       j->filter()->Send(new ViewMsg_WorkerConnected(j->route_id()));
467       return;
468     }
469   }
470 }
471
472 void WorkerProcessHost::OnAllowDatabase(int worker_route_id,
473                                         const GURL& url,
474                                         const base::string16& name,
475                                         const base::string16& display_name,
476                                         unsigned long estimated_size,
477                                         bool* result) {
478   *result = GetContentClient()->browser()->AllowWorkerDatabase(
479       url, name, display_name, estimated_size, resource_context_,
480       GetRenderFrameIDsForWorker(worker_route_id));
481 }
482
483 void WorkerProcessHost::OnAllowFileSystem(int worker_route_id,
484                                           const GURL& url,
485                                           bool* result) {
486   *result = GetContentClient()->browser()->AllowWorkerFileSystem(
487       url, resource_context_, GetRenderFrameIDsForWorker(worker_route_id));
488 }
489
490 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id,
491                                          const GURL& url,
492                                          const base::string16& name,
493                                          bool* result) {
494   *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
495       url, name, resource_context_,
496       GetRenderFrameIDsForWorker(worker_route_id));
497 }
498
499 void WorkerProcessHost::OnForceKillWorkerProcess() {
500   if (process_ && process_launched_)
501     base::KillProcess(
502           process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
503   else
504     RecordAction(base::UserMetricsAction("WorkerProcess_BadProcessToKill"));
505 }
506
507 void WorkerProcessHost::RelayMessage(
508     const IPC::Message& message,
509     WorkerMessageFilter* incoming_filter,
510     WorkerInstance* instance) {
511   if (message.type() == WorkerMsg_Connect::ID) {
512     // Crack the SharedWorker Connect message to setup routing for the port.
513     int sent_message_port_id;
514     int new_routing_id;
515     if (!WorkerMsg_Connect::Read(
516             &message, &sent_message_port_id, &new_routing_id)) {
517       return;
518     }
519     new_routing_id = worker_message_filter_->GetNextRoutingID();
520     MessagePortService::GetInstance()->UpdateMessagePort(
521         sent_message_port_id,
522         worker_message_filter_->message_port_message_filter(),
523         new_routing_id);
524
525     instance->SetMessagePortID(incoming_filter,
526                                message.routing_id(),
527                                sent_message_port_id);
528     // Resend the message with the new routing id.
529     worker_message_filter_->Send(new WorkerMsg_Connect(
530         instance->worker_route_id(), sent_message_port_id, new_routing_id));
531
532     // Send any queued messages for the sent port.
533     MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
534         sent_message_port_id);
535   } else {
536     IPC::Message* new_message = new IPC::Message(message);
537     new_message->set_routing_id(instance->worker_route_id());
538     worker_message_filter_->Send(new_message);
539     return;
540   }
541 }
542
543 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() {
544   if (!instances_.size() && socket_stream_dispatcher_host_.get()) {
545     // We can assume that this object is going to delete, because
546     // currently a WorkerInstance will never be added to a WorkerProcessHost
547     // once it is initialized.
548
549     // SocketStreamDispatcherHost should be notified now that the worker
550     // process will shutdown soon.
551     socket_stream_dispatcher_host_->Shutdown();
552     socket_stream_dispatcher_host_ = NULL;
553   }
554 }
555
556 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) {
557   for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
558     bool shutdown = false;
559     i->RemoveFilters(filter);
560
561     int render_frame_id = 0;
562     const WorkerDocumentSet::DocumentInfoSet& documents =
563         i->worker_document_set()->documents();
564     for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
565          documents.begin(); doc != documents.end(); ++doc) {
566       if (doc->filter() == filter) {
567         render_frame_id = doc->render_frame_id();
568         break;
569       }
570     }
571     i->worker_document_set()->RemoveAll(filter);
572     if (i->worker_document_set()->IsEmpty()) {
573       shutdown = true;
574     }
575     if (shutdown) {
576       BrowserThread::PostTask(
577           BrowserThread::UI, FROM_HERE,
578           base::Bind(&WorkerTerminatedCallback,
579                      filter->render_process_id(),
580                      render_frame_id,
581                      process_->GetData().id));
582       Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
583       i = instances_.erase(i);
584     } else {
585       ++i;
586     }
587   }
588   ShutdownSocketStreamDispatcherHostIfNecessary();
589 }
590
591 bool WorkerProcessHost::CanShutdown() {
592   return instances_.empty();
593 }
594
595 void WorkerProcessHost::UpdateTitle() {
596   std::set<std::string> titles;
597   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
598     // Allow the embedder first crack at special casing the title.
599     std::string title = GetContentClient()->browser()->
600         GetWorkerProcessTitle(i->url(), resource_context_);
601
602     if (title.empty()) {
603       title = net::registry_controlled_domains::GetDomainAndRegistry(
604           i->url(),
605           net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
606     }
607
608     // Use the host name if the domain is empty, i.e. localhost or IP address.
609     if (title.empty())
610       title = i->url().host();
611
612     // If the host name is empty, i.e. file url, use the path.
613     if (title.empty())
614       title = i->url().path();
615     titles.insert(title);
616   }
617
618   std::string display_title;
619   for (std::set<std::string>::iterator i = titles.begin();
620        i != titles.end(); ++i) {
621     if (!display_title.empty())
622       display_title += ", ";
623     display_title += *i;
624   }
625
626   process_->SetName(base::UTF8ToUTF16(display_title));
627 }
628
629 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter,
630                                          unsigned long long document_id) {
631   // Walk all instances and remove the document from their document set.
632   for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
633     int render_frame_id = 0;
634     const WorkerDocumentSet::DocumentInfoSet& documents =
635         i->worker_document_set()->documents();
636     for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
637          documents.begin(); doc != documents.end(); ++doc) {
638       if (doc->filter() == filter && doc->document_id() == document_id) {
639         render_frame_id = doc->render_frame_id();
640         break;
641       }
642     }
643     i->worker_document_set()->Remove(filter, document_id);
644     if (i->worker_document_set()->IsEmpty()) {
645       BrowserThread::PostTask(
646           BrowserThread::UI, FROM_HERE,
647           base::Bind(&WorkerTerminatedCallback,
648                      filter->render_process_id(),
649                      render_frame_id,
650                      process_->GetData().id));
651       // This worker has no more associated documents - shut it down.
652       Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
653       i = instances_.erase(i);
654     } else {
655       ++i;
656     }
657   }
658   ShutdownSocketStreamDispatcherHostIfNecessary();
659 }
660
661 void WorkerProcessHost::TerminateWorker(int worker_route_id) {
662   Send(new WorkerMsg_TerminateWorkerContext(worker_route_id));
663 }
664
665 void WorkerProcessHost::SetBackgrounded(bool backgrounded) {
666   process_->SetBackgrounded(backgrounded);
667 }
668
669 const ChildProcessData& WorkerProcessHost::GetData() {
670   return process_->GetData();
671 }
672
673 std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker(
674     int worker_route_id) {
675   std::vector<std::pair<int, int> > result;
676   WorkerProcessHost::Instances::const_iterator i;
677   for (i = instances_.begin(); i != instances_.end(); ++i) {
678     if (i->worker_route_id() != worker_route_id)
679       continue;
680     const WorkerDocumentSet::DocumentInfoSet& documents =
681         i->worker_document_set()->documents();
682     for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
683          documents.begin(); doc != documents.end(); ++doc) {
684       result.push_back(
685           std::make_pair(doc->render_process_id(), doc->render_frame_id()));
686     }
687     break;
688   }
689   return result;
690 }
691
692 void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request& request,
693                                     ResourceContext** resource_context,
694                                     net::URLRequestContext** request_context) {
695   *resource_context = resource_context_;
696   *request_context = partition_.url_request_context()->GetURLRequestContext();
697 }
698
699 net::URLRequestContext* WorkerProcessHost::GetRequestContext(
700     ResourceType::Type resource_type) {
701   return partition_.url_request_context()->GetURLRequestContext();
702 }
703
704 WorkerProcessHost::WorkerInstance::WorkerInstance(
705     const GURL& url,
706     const base::string16& name,
707     const base::string16& content_security_policy,
708     blink::WebContentSecurityPolicyType security_policy_type,
709     int worker_route_id,
710     int render_frame_id,
711     ResourceContext* resource_context,
712     const WorkerStoragePartition& partition)
713     : url_(url),
714       closed_(false),
715       name_(name),
716       content_security_policy_(content_security_policy),
717       security_policy_type_(security_policy_type),
718       worker_route_id_(worker_route_id),
719       render_frame_id_(render_frame_id),
720       worker_document_set_(new WorkerDocumentSet()),
721       resource_context_(resource_context),
722       partition_(partition),
723       load_failed_(false) {
724   DCHECK(resource_context_);
725 }
726
727 WorkerProcessHost::WorkerInstance::~WorkerInstance() {
728 }
729
730 void WorkerProcessHost::WorkerInstance::SetMessagePortID(
731     WorkerMessageFilter* filter,
732     int route_id,
733     int message_port_id) {
734   for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
735     if (i->filter() == filter && i->route_id() == route_id) {
736       i->set_message_port_id(message_port_id);
737       return;
738     }
739   }
740 }
741
742 // Compares an instance based on the algorithm in the WebWorkers spec - an
743 // instance matches if the origins of the URLs match, and:
744 // a) the names are non-empty and equal
745 // -or-
746 // b) the names are both empty, and the urls are equal
747 bool WorkerProcessHost::WorkerInstance::Matches(
748     const GURL& match_url,
749     const base::string16& match_name,
750     const WorkerStoragePartition& partition,
751     ResourceContext* resource_context) const {
752   // Only match open shared workers.
753   if (closed_)
754     return false;
755
756   // ResourceContext equivalence is being used as a proxy to ensure we only
757   // matched shared workers within the same BrowserContext.
758   if (resource_context_ != resource_context)
759     return false;
760
761   // We must be in the same storage partition otherwise sharing will violate
762   // isolation.
763   if (!partition_.Equals(partition))
764     return false;
765
766   if (url_.GetOrigin() != match_url.GetOrigin())
767     return false;
768
769   if (name_.empty() && match_name.empty())
770     return url_ == match_url;
771
772   return name_ == match_name;
773 }
774
775 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter,
776                                                   int route_id) {
777   CHECK(filter);
778   if (!HasFilter(filter, route_id)) {
779     FilterInfo info(filter, route_id);
780     filters_.push_back(info);
781   }
782 }
783
784 void WorkerProcessHost::WorkerInstance::RemoveFilter(
785     WorkerMessageFilter* filter, int route_id) {
786   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
787     if (i->filter() == filter && i->route_id() == route_id)
788       i = filters_.erase(i);
789     else
790       ++i;
791   }
792   // Should not be duplicate copies in the filter set.
793   DCHECK(!HasFilter(filter, route_id));
794 }
795
796 void WorkerProcessHost::WorkerInstance::RemoveFilters(
797     WorkerMessageFilter* filter) {
798   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
799     if (i->filter() == filter)
800       i = filters_.erase(i);
801     else
802       ++i;
803   }
804 }
805
806 bool WorkerProcessHost::WorkerInstance::HasFilter(
807     WorkerMessageFilter* filter, int route_id) const {
808   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
809        ++i) {
810     if (i->filter() == filter && i->route_id() == route_id)
811       return true;
812   }
813   return false;
814 }
815
816 bool WorkerProcessHost::WorkerInstance::FrameIsParent(
817     int render_process_id, int render_frame_id) const {
818   const WorkerDocumentSet::DocumentInfoSet& parents =
819       worker_document_set()->documents();
820   for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
821            parents.begin();
822        parent_iter != parents.end(); ++parent_iter) {
823     if (parent_iter->render_process_id() == render_process_id &&
824         parent_iter->render_frame_id() == render_frame_id) {
825       return true;
826     }
827   }
828   return false;
829 }
830
831 WorkerProcessHost::WorkerInstance::FilterInfo
832 WorkerProcessHost::WorkerInstance::GetFilter() const {
833   DCHECK(NumFilters() == 1);
834   return *filters_.begin();
835 }
836
837 }  // namespace content