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_service_impl.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/logging.h"
11#include "base/threading/thread.h"
12#include "content/browser/devtools/worker_devtools_manager.h"
13#include "content/browser/renderer_host/render_widget_host_impl.h"
14#include "content/browser/worker_host/worker_message_filter.h"
15#include "content/browser/worker_host/worker_process_host.h"
16#include "content/common/view_messages.h"
17#include "content/common/worker_messages.h"
18#include "content/public/browser/child_process_data.h"
19#include "content/public/browser/notification_service.h"
20#include "content/public/browser/notification_types.h"
21#include "content/public/browser/render_process_host.h"
22#include "content/public/browser/render_view_host.h"
23#include "content/public/browser/render_widget_host.h"
24#include "content/public/browser/render_widget_host_iterator.h"
25#include "content/public/browser/render_widget_host_view.h"
26#include "content/public/browser/resource_context.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/browser/worker_service_observer.h"
29#include "content/public/common/content_switches.h"
30#include "content/public/common/process_type.h"
31
32namespace content {
33
34const int WorkerServiceImpl::kMaxWorkersWhenSeparate = 64;
35const int WorkerServiceImpl::kMaxWorkersPerTabWhenSeparate = 16;
36
37class WorkerPrioritySetter
38    : public NotificationObserver,
39      public base::RefCountedThreadSafe<WorkerPrioritySetter,
40                                        BrowserThread::DeleteOnUIThread> {
41 public:
42  WorkerPrioritySetter();
43
44  // Posts a task to the UI thread to register to receive notifications.
45  void Initialize();
46
47  // Invoked by WorkerServiceImpl when a worker process is created.
48  void NotifyWorkerProcessCreated();
49
50 private:
51  friend class base::RefCountedThreadSafe<WorkerPrioritySetter>;
52  friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
53  friend class base::DeleteHelper<WorkerPrioritySetter>;
54  virtual ~WorkerPrioritySetter();
55
56  // Posts a task to perform a worker priority update.
57  void PostTaskToGatherAndUpdateWorkerPriorities();
58
59  // Gathers up a list of the visible tabs and then updates priorities for
60  // all the shared workers.
61  void GatherVisibleIDsAndUpdateWorkerPriorities();
62
63  // Registers as an observer to receive notifications about
64  // widgets being shown.
65  void RegisterObserver();
66
67  // Sets priorities for shared workers given a set of visible tabs (as a
68  // std::set of std::pair<render_process, render_view> ids.
69  void UpdateWorkerPrioritiesFromVisibleSet(
70      const std::set<std::pair<int, int> >* visible);
71
72  // Called to refresh worker priorities when focus changes between tabs.
73  void OnRenderWidgetVisibilityChanged(std::pair<int, int>);
74
75  // NotificationObserver implementation.
76  virtual void Observe(int type,
77                       const NotificationSource& source,
78                       const NotificationDetails& details) OVERRIDE;
79
80  NotificationRegistrar registrar_;
81};
82
83WorkerPrioritySetter::WorkerPrioritySetter() {
84}
85
86WorkerPrioritySetter::~WorkerPrioritySetter() {
87  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88}
89
90void WorkerPrioritySetter::Initialize() {
91  BrowserThread::PostTask(
92      BrowserThread::UI, FROM_HERE,
93      base::Bind(&WorkerPrioritySetter::RegisterObserver, this));
94}
95
96void WorkerPrioritySetter::NotifyWorkerProcessCreated() {
97  PostTaskToGatherAndUpdateWorkerPriorities();
98}
99
100void WorkerPrioritySetter::PostTaskToGatherAndUpdateWorkerPriorities() {
101  BrowserThread::PostTask(
102      BrowserThread::UI, FROM_HERE,
103      base::Bind(
104          &WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities,
105          this));
106}
107
108void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() {
109  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110  std::set<std::pair<int, int> >* visible_renderer_ids =
111      new std::set<std::pair<int, int> >();
112
113  // Gather up all the visible renderer process/view pairs
114  scoped_ptr<RenderWidgetHostIterator> widgets(
115      RenderWidgetHost::GetRenderWidgetHosts());
116  while (RenderWidgetHost* widget = widgets->GetNextHost()) {
117    if (widget->GetProcess()->VisibleWidgetCount() == 0)
118      continue;
119
120    RenderWidgetHostView* render_view = widget->GetView();
121    if (render_view && render_view->IsShowing()) {
122      visible_renderer_ids->insert(
123          std::pair<int, int>(widget->GetProcess()->GetID(),
124                              widget->GetRoutingID()));
125    }
126  }
127
128  BrowserThread::PostTask(
129      BrowserThread::IO, FROM_HERE,
130      base::Bind(&WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet,
131                 this, base::Owned(visible_renderer_ids)));
132}
133
134void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
135    const std::set<std::pair<int, int> >* visible_renderer_ids) {
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
137
138  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
139    if (!iter->process_launched())
140      continue;
141    bool throttle = true;
142
143    for (WorkerProcessHost::Instances::const_iterator instance =
144        iter->instances().begin(); instance != iter->instances().end();
145        ++instance) {
146
147      // This code assumes one worker per process
148      WorkerProcessHost::Instances::const_iterator first_instance =
149          iter->instances().begin();
150      if (first_instance == iter->instances().end())
151        continue;
152
153      WorkerDocumentSet::DocumentInfoSet::const_iterator info =
154          first_instance->worker_document_set()->documents().begin();
155
156      for (; info != first_instance->worker_document_set()->documents().end();
157          ++info) {
158        std::pair<int, int> id(
159            info->render_process_id(), info->render_view_id());
160        if (visible_renderer_ids->find(id) != visible_renderer_ids->end()) {
161          throttle = false;
162          break;
163        }
164      }
165
166      if (!throttle ) {
167        break;
168      }
169    }
170
171    iter->SetBackgrounded(throttle);
172  }
173}
174
175void WorkerPrioritySetter::OnRenderWidgetVisibilityChanged(
176    std::pair<int, int> id) {
177  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178  std::set<std::pair<int, int> > visible_renderer_ids;
179
180  visible_renderer_ids.insert(id);
181
182  UpdateWorkerPrioritiesFromVisibleSet(&visible_renderer_ids);
183}
184
185void WorkerPrioritySetter::RegisterObserver() {
186  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187  registrar_.Add(this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
188                 NotificationService::AllBrowserContextsAndSources());
189  registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CREATED,
190                 NotificationService::AllBrowserContextsAndSources());
191}
192
193void WorkerPrioritySetter::Observe(int type,
194    const NotificationSource& source, const NotificationDetails& details) {
195  if (type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
196    bool visible = *Details<bool>(details).ptr();
197
198    if (visible) {
199      int render_widget_id =
200          Source<RenderWidgetHost>(source).ptr()->GetRoutingID();
201      int render_process_pid =
202          Source<RenderWidgetHost>(source).ptr()->GetProcess()->GetID();
203
204      BrowserThread::PostTask(
205          BrowserThread::IO, FROM_HERE,
206          base::Bind(&WorkerPrioritySetter::OnRenderWidgetVisibilityChanged,
207              this, std::pair<int, int>(render_process_pid, render_widget_id)));
208    }
209  }
210  else if (type == NOTIFICATION_RENDERER_PROCESS_CREATED) {
211    PostTaskToGatherAndUpdateWorkerPriorities();
212  }
213}
214
215WorkerService* WorkerService::GetInstance() {
216  return WorkerServiceImpl::GetInstance();
217}
218
219WorkerServiceImpl* WorkerServiceImpl::GetInstance() {
220  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
221  return Singleton<WorkerServiceImpl>::get();
222}
223
224WorkerServiceImpl::WorkerServiceImpl()
225    : priority_setter_(new WorkerPrioritySetter()),
226      next_worker_route_id_(0) {
227  priority_setter_->Initialize();
228}
229
230WorkerServiceImpl::~WorkerServiceImpl() {
231  // The observers in observers_ can't be used here because they might be
232  // gone already.
233}
234
235void WorkerServiceImpl::PerformTeardownForTesting() {
236  priority_setter_ = NULL;
237}
238
239void WorkerServiceImpl::OnWorkerMessageFilterClosing(
240    WorkerMessageFilter* filter) {
241  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
242    iter->FilterShutdown(filter);
243  }
244
245  // See if that process had any queued workers.
246  for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
247       i != queued_workers_.end();) {
248    i->RemoveFilters(filter);
249    if (i->NumFilters() == 0) {
250      i = queued_workers_.erase(i);
251    } else {
252      ++i;
253    }
254  }
255
256  for (WorkerProcessHost::Instances::iterator i =
257           pending_shared_workers_.begin();
258       i != pending_shared_workers_.end(); ) {
259     i->RemoveFilters(filter);
260     if (i->NumFilters() == 0) {
261      i = pending_shared_workers_.erase(i);
262    } else {
263      ++i;
264    }
265  }
266
267  // Also, see if that process had any pending shared workers.
268  for (WorkerProcessHost::Instances::iterator iter =
269           pending_shared_workers_.begin();
270       iter != pending_shared_workers_.end(); ) {
271    iter->worker_document_set()->RemoveAll(filter);
272    if (iter->worker_document_set()->IsEmpty()) {
273      iter = pending_shared_workers_.erase(iter);
274    } else {
275      ++iter;
276    }
277  }
278
279  // Either a worker proceess has shut down, in which case we can start one of
280  // the queued workers, or a renderer has shut down, in which case it doesn't
281  // affect anything.  We call this function in both scenarios because then we
282  // don't have to keep track which filters are from worker processes.
283  TryStartingQueuedWorker();
284}
285
286void WorkerServiceImpl::CreateWorker(
287    const ViewHostMsg_CreateWorker_Params& params,
288    int route_id,
289    WorkerMessageFilter* filter,
290    ResourceContext* resource_context,
291    const WorkerStoragePartition& partition) {
292  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
293  // Generate a unique route id for the browser-worker communication that's
294  // unique among all worker processes.  That way when the worker process sends
295  // a wrapped IPC message through us, we know which WorkerProcessHost to give
296  // it to.
297  WorkerProcessHost::WorkerInstance instance(
298      params.url,
299      params.name,
300      next_worker_route_id(),
301      0,
302      params.script_resource_appcache_id,
303      resource_context,
304      partition);
305  instance.AddFilter(filter, route_id);
306  instance.worker_document_set()->Add(
307      filter, params.document_id, filter->render_process_id(),
308      params.render_view_route_id);
309
310  CreateWorkerFromInstance(instance);
311}
312
313void WorkerServiceImpl::LookupSharedWorker(
314    const ViewHostMsg_CreateWorker_Params& params,
315    int route_id,
316    WorkerMessageFilter* filter,
317    ResourceContext* resource_context,
318    const WorkerStoragePartition& partition,
319    bool* exists,
320    bool* url_mismatch) {
321  *exists = true;
322  WorkerProcessHost::WorkerInstance* instance = FindSharedWorkerInstance(
323      params.url, params.name, partition, resource_context);
324
325  if (!instance) {
326    // If no worker instance currently exists, we need to create a pending
327    // instance - this is to make sure that any subsequent lookups passing a
328    // mismatched URL get the appropriate url_mismatch error at lookup time.
329    // Having named shared workers was a Really Bad Idea due to details like
330    // this.
331    instance = CreatePendingInstance(params.url, params.name,
332                                     resource_context, partition);
333    *exists = false;
334  }
335
336  // Make sure the passed-in instance matches the URL - if not, return an
337  // error.
338  if (params.url != instance->url()) {
339    *url_mismatch = true;
340    *exists = false;
341  } else {
342    *url_mismatch = false;
343    // Add our route ID to the existing instance so we can send messages to it.
344    instance->AddFilter(filter, route_id);
345
346    // Add the passed filter/document_id to the worker instance.
347    // TODO(atwilson): This won't work if the message is from a worker process.
348    // We don't support that yet though (this message is only sent from
349    // renderers) but when we do, we'll need to add code to pass in the current
350    // worker's document set for nested workers.
351    instance->worker_document_set()->Add(
352        filter, params.document_id, filter->render_process_id(),
353        params.render_view_route_id);
354  }
355}
356
357void WorkerServiceImpl::ForwardToWorker(const IPC::Message& message,
358                                        WorkerMessageFilter* filter) {
359  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
360    if (iter->FilterMessage(message, filter))
361      return;
362  }
363
364  // TODO(jabdelmalek): tell filter that callee is gone
365}
366
367void WorkerServiceImpl::DocumentDetached(unsigned long long document_id,
368                                         WorkerMessageFilter* filter) {
369  // Any associated shared workers can be shut down.
370  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter)
371    iter->DocumentDetached(filter, document_id);
372
373  // Remove any queued shared workers for this document.
374  for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
375       iter != queued_workers_.end();) {
376
377    iter->worker_document_set()->Remove(filter, document_id);
378    if (iter->worker_document_set()->IsEmpty()) {
379      iter = queued_workers_.erase(iter);
380      continue;
381    }
382    ++iter;
383  }
384
385  // Remove the document from any pending shared workers.
386  for (WorkerProcessHost::Instances::iterator iter =
387           pending_shared_workers_.begin();
388       iter != pending_shared_workers_.end(); ) {
389    iter->worker_document_set()->Remove(filter, document_id);
390    if (iter->worker_document_set()->IsEmpty()) {
391      iter = pending_shared_workers_.erase(iter);
392    } else {
393      ++iter;
394    }
395  }
396}
397
398bool WorkerServiceImpl::CreateWorkerFromInstance(
399    WorkerProcessHost::WorkerInstance instance) {
400  if (!CanCreateWorkerProcess(instance)) {
401    queued_workers_.push_back(instance);
402    return true;
403  }
404
405  // Check to see if this shared worker is already running (two pages may have
406  // tried to start up the worker simultaneously).
407  // See if a worker with this name already exists.
408  WorkerProcessHost::WorkerInstance* existing_instance =
409      FindSharedWorkerInstance(
410          instance.url(), instance.name(), instance.partition(),
411          instance.resource_context());
412  WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
413      instance.GetFilter();
414  // If this worker is already running, no need to create a new copy. Just
415  // inform the caller that the worker has been created.
416  if (existing_instance) {
417    // Walk the worker's filter list to see if this client is listed. If not,
418    // then it means that the worker started by the client already exited so
419    // we should not attach to this new one (http://crbug.com/29243).
420    if (!existing_instance->HasFilter(filter_info.first, filter_info.second))
421      return false;
422    filter_info.first->Send(new ViewMsg_WorkerCreated(filter_info.second));
423    return true;
424  }
425
426  // Look to see if there's a pending instance.
427  WorkerProcessHost::WorkerInstance* pending = FindPendingInstance(
428      instance.url(), instance.name(), instance.partition(),
429      instance.resource_context());
430  // If there's no instance *and* no pending instance (or there is a pending
431  // instance but it does not contain our filter info), then it means the
432  // worker started up and exited already. Log a warning because this should
433  // be a very rare occurrence and is probably a bug, but it *can* happen so
434  // handle it gracefully.
435  if (!pending ||
436      !pending->HasFilter(filter_info.first, filter_info.second)) {
437    DLOG(WARNING) << "Pending worker already exited";
438    return false;
439  }
440
441  // Assign the accumulated document set and filter list for this pending
442  // worker to the new instance.
443  DCHECK(!pending->worker_document_set()->IsEmpty());
444  instance.ShareDocumentSet(*pending);
445  for (WorkerProcessHost::WorkerInstance::FilterList::const_iterator i =
446           pending->filters().begin();
447       i != pending->filters().end(); ++i) {
448    instance.AddFilter(i->first, i->second);
449  }
450  RemovePendingInstances(instance.url(), instance.name(),
451                         instance.partition(), instance.resource_context());
452
453  // Remove any queued instances of this worker and copy over the filter to
454  // this instance.
455  for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
456       iter != queued_workers_.end();) {
457    if (iter->Matches(instance.url(), instance.name(),
458                      instance.partition(), instance.resource_context())) {
459      DCHECK(iter->NumFilters() == 1);
460      WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
461          iter->GetFilter();
462      instance.AddFilter(filter_info.first, filter_info.second);
463      iter = queued_workers_.erase(iter);
464    } else {
465      ++iter;
466    }
467  }
468
469  WorkerMessageFilter* first_filter = instance.filters().begin()->first;
470  WorkerProcessHost* worker = new WorkerProcessHost(
471      instance.resource_context(), instance.partition());
472  // TODO(atwilson): This won't work if the message is from a worker process.
473  // We don't support that yet though (this message is only sent from
474  // renderers) but when we do, we'll need to add code to pass in the current
475  // worker's document set for nested workers.
476  if (!worker->Init(first_filter->render_process_id())) {
477    delete worker;
478    return false;
479  }
480
481  worker->CreateWorker(instance);
482  FOR_EACH_OBSERVER(
483      WorkerServiceObserver, observers_,
484      WorkerCreated(instance.url(), instance.name(), worker->GetData().id,
485                    instance.worker_route_id()));
486  WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance);
487  return true;
488}
489
490bool WorkerServiceImpl::CanCreateWorkerProcess(
491    const WorkerProcessHost::WorkerInstance& instance) {
492  // Worker can be fired off if *any* parent has room.
493  const WorkerDocumentSet::DocumentInfoSet& parents =
494        instance.worker_document_set()->documents();
495
496  for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
497           parents.begin();
498       parent_iter != parents.end(); ++parent_iter) {
499    bool hit_total_worker_limit = false;
500    if (TabCanCreateWorkerProcess(parent_iter->render_process_id(),
501                                  parent_iter->render_view_id(),
502                                  &hit_total_worker_limit)) {
503      return true;
504    }
505    // Return false if already at the global worker limit (no need to continue
506    // checking parent tabs).
507    if (hit_total_worker_limit)
508      return false;
509  }
510  // If we've reached here, none of the parent tabs is allowed to create an
511  // instance.
512  return false;
513}
514
515bool WorkerServiceImpl::TabCanCreateWorkerProcess(
516    int render_process_id,
517    int render_view_id,
518    bool* hit_total_worker_limit) {
519  int total_workers = 0;
520  int workers_per_tab = 0;
521  *hit_total_worker_limit = false;
522  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
523    for (WorkerProcessHost::Instances::const_iterator cur_instance =
524             iter->instances().begin();
525         cur_instance != iter->instances().end(); ++cur_instance) {
526      total_workers++;
527      if (total_workers >= kMaxWorkersWhenSeparate) {
528        *hit_total_worker_limit = true;
529        return false;
530      }
531      if (cur_instance->RendererIsParent(render_process_id, render_view_id)) {
532        workers_per_tab++;
533        if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate)
534          return false;
535      }
536    }
537  }
538
539  return true;
540}
541
542void WorkerServiceImpl::TryStartingQueuedWorker() {
543  if (queued_workers_.empty())
544    return;
545
546  for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
547       i != queued_workers_.end();) {
548    if (CanCreateWorkerProcess(*i)) {
549      WorkerProcessHost::WorkerInstance instance = *i;
550      queued_workers_.erase(i);
551      CreateWorkerFromInstance(instance);
552
553      // CreateWorkerFromInstance can modify the queued_workers_ list when it
554      // coalesces queued instances after starting a shared worker, so we
555      // have to rescan the list from the beginning (our iterator is now
556      // invalid). This is not a big deal as having any queued workers will be
557      // rare in practice so the list will be small.
558      i = queued_workers_.begin();
559    } else {
560      ++i;
561    }
562  }
563}
564
565bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id,
566                                             int* render_process_id,
567                                             int* render_view_id) const {
568  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
569    if (iter.GetData().id != worker_process_id)
570      continue;
571
572    // This code assumes one worker per process, see function comment in header!
573    WorkerProcessHost::Instances::const_iterator first_instance =
574        iter->instances().begin();
575    if (first_instance == iter->instances().end())
576      return false;
577
578    WorkerDocumentSet::DocumentInfoSet::const_iterator info =
579        first_instance->worker_document_set()->documents().begin();
580    *render_process_id = info->render_process_id();
581    *render_view_id = info->render_view_id();
582    return true;
583  }
584  return false;
585}
586
587const WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindWorkerInstance(
588      int worker_process_id) {
589  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
590    if (iter.GetData().id != worker_process_id)
591        continue;
592
593    WorkerProcessHost::Instances::const_iterator instance =
594        iter->instances().begin();
595    return instance == iter->instances().end() ? NULL : &*instance;
596  }
597  return NULL;
598}
599
600bool WorkerServiceImpl::TerminateWorker(int process_id, int route_id) {
601  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
602    if (iter.GetData().id == process_id) {
603      iter->TerminateWorker(route_id);
604      return true;
605    }
606  }
607  return false;
608}
609
610std::vector<WorkerService::WorkerInfo> WorkerServiceImpl::GetWorkers() {
611  std::vector<WorkerService::WorkerInfo> results;
612  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
613    const WorkerProcessHost::Instances& instances = (*iter)->instances();
614    for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
615         i != instances.end(); ++i) {
616      WorkerService::WorkerInfo info;
617      info.url = i->url();
618      info.name = i->name();
619      info.route_id = i->worker_route_id();
620      info.process_id = iter.GetData().id;
621      info.handle = iter.GetData().handle;
622      results.push_back(info);
623    }
624  }
625  return results;
626}
627
628void WorkerServiceImpl::AddObserver(WorkerServiceObserver* observer) {
629  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
630  observers_.AddObserver(observer);
631}
632
633void WorkerServiceImpl::RemoveObserver(WorkerServiceObserver* observer) {
634  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
635  observers_.RemoveObserver(observer);
636}
637
638void WorkerServiceImpl::NotifyWorkerDestroyed(
639    WorkerProcessHost* process,
640    int worker_route_id) {
641  WorkerDevToolsManager::GetInstance()->WorkerDestroyed(
642      process, worker_route_id);
643  FOR_EACH_OBSERVER(WorkerServiceObserver, observers_,
644                    WorkerDestroyed(process->GetData().id, worker_route_id));
645}
646
647void WorkerServiceImpl::NotifyWorkerProcessCreated() {
648  priority_setter_->NotifyWorkerProcessCreated();
649}
650
651WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindSharedWorkerInstance(
652    const GURL& url,
653    const base::string16& name,
654    const WorkerStoragePartition& partition,
655    ResourceContext* resource_context) {
656  for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
657    for (WorkerProcessHost::Instances::iterator instance_iter =
658             iter->mutable_instances().begin();
659         instance_iter != iter->mutable_instances().end();
660         ++instance_iter) {
661      if (instance_iter->Matches(url, name, partition, resource_context))
662        return &(*instance_iter);
663    }
664  }
665  return NULL;
666}
667
668WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindPendingInstance(
669    const GURL& url,
670    const base::string16& name,
671    const WorkerStoragePartition& partition,
672    ResourceContext* resource_context) {
673  // Walk the pending instances looking for a matching pending worker.
674  for (WorkerProcessHost::Instances::iterator iter =
675           pending_shared_workers_.begin();
676       iter != pending_shared_workers_.end();
677       ++iter) {
678    if (iter->Matches(url, name, partition, resource_context))
679      return &(*iter);
680  }
681  return NULL;
682}
683
684
685void WorkerServiceImpl::RemovePendingInstances(
686    const GURL& url,
687    const base::string16& name,
688    const WorkerStoragePartition& partition,
689    ResourceContext* resource_context) {
690  // Walk the pending instances looking for a matching pending worker.
691  for (WorkerProcessHost::Instances::iterator iter =
692           pending_shared_workers_.begin();
693       iter != pending_shared_workers_.end(); ) {
694    if (iter->Matches(url, name, partition, resource_context)) {
695      iter = pending_shared_workers_.erase(iter);
696    } else {
697      ++iter;
698    }
699  }
700}
701
702WorkerProcessHost::WorkerInstance* WorkerServiceImpl::CreatePendingInstance(
703    const GURL& url,
704    const base::string16& name,
705    ResourceContext* resource_context,
706    const WorkerStoragePartition& partition) {
707  // Look for an existing pending shared worker.
708  WorkerProcessHost::WorkerInstance* instance =
709      FindPendingInstance(url, name, partition, resource_context);
710  if (instance)
711    return instance;
712
713  // No existing pending worker - create a new one.
714  WorkerProcessHost::WorkerInstance pending(
715      url, true, name, resource_context, partition);
716  pending_shared_workers_.push_back(pending);
717  return &pending_shared_workers_.back();
718}
719
720}  // namespace content
721