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