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.
5 #include "content/browser/worker_host/worker_service_impl.h"
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"
34 const int WorkerServiceImpl::kMaxWorkersWhenSeparate
= 64;
35 const int WorkerServiceImpl::kMaxWorkersPerTabWhenSeparate
= 16;
37 class WorkerPrioritySetter
38 : public NotificationObserver
,
39 public base::RefCountedThreadSafe
<WorkerPrioritySetter
,
40 BrowserThread::DeleteOnUIThread
> {
42 WorkerPrioritySetter();
44 // Posts a task to the UI thread to register to receive notifications.
47 // Invoked by WorkerServiceImpl when a worker process is created.
48 void NotifyWorkerProcessCreated();
51 friend class base::RefCountedThreadSafe
<WorkerPrioritySetter
>;
52 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
53 friend class base::DeleteHelper
<WorkerPrioritySetter
>;
54 virtual ~WorkerPrioritySetter();
56 // Posts a task to perform a worker priority update.
57 void PostTaskToGatherAndUpdateWorkerPriorities();
59 // Gathers up a list of the visible tabs and then updates priorities for
60 // all the shared workers.
61 void GatherVisibleIDsAndUpdateWorkerPriorities();
63 // Registers as an observer to receive notifications about
64 // widgets being shown.
65 void RegisterObserver();
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
);
72 // Called to refresh worker priorities when focus changes between tabs.
73 void OnRenderWidgetVisibilityChanged(std::pair
<int, int>);
75 // NotificationObserver implementation.
76 virtual void Observe(int type
,
77 const NotificationSource
& source
,
78 const NotificationDetails
& details
) OVERRIDE
;
80 NotificationRegistrar registrar_
;
83 WorkerPrioritySetter::WorkerPrioritySetter() {
86 WorkerPrioritySetter::~WorkerPrioritySetter() {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
90 void WorkerPrioritySetter::Initialize() {
91 BrowserThread::PostTask(
92 BrowserThread::UI
, FROM_HERE
,
93 base::Bind(&WorkerPrioritySetter::RegisterObserver
, this));
96 void WorkerPrioritySetter::NotifyWorkerProcessCreated() {
97 PostTaskToGatherAndUpdateWorkerPriorities();
100 void WorkerPrioritySetter::PostTaskToGatherAndUpdateWorkerPriorities() {
101 BrowserThread::PostTask(
102 BrowserThread::UI
, FROM_HERE
,
104 &WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities
,
108 void 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> >();
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)
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()));
128 BrowserThread::PostTask(
129 BrowserThread::IO
, FROM_HERE
,
130 base::Bind(&WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet
,
131 this, base::Owned(visible_renderer_ids
)));
134 void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
135 const std::set
<std::pair
<int, int> >* visible_renderer_ids
) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
138 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
139 if (!iter
->process_launched())
141 bool throttle
= true;
143 for (WorkerProcessHost::Instances::const_iterator instance
=
144 iter
->instances().begin(); instance
!= iter
->instances().end();
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())
153 WorkerDocumentSet::DocumentInfoSet::const_iterator info
=
154 first_instance
->worker_document_set()->documents().begin();
156 for (; info
!= first_instance
->worker_document_set()->documents().end();
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()) {
171 iter
->SetBackgrounded(throttle
);
175 void WorkerPrioritySetter::OnRenderWidgetVisibilityChanged(
176 std::pair
<int, int> id
) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
178 std::set
<std::pair
<int, int> > visible_renderer_ids
;
180 visible_renderer_ids
.insert(id
);
182 UpdateWorkerPrioritiesFromVisibleSet(&visible_renderer_ids
);
185 void 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());
193 void 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();
199 int render_widget_id
=
200 Source
<RenderWidgetHost
>(source
).ptr()->GetRoutingID();
201 int render_process_pid
=
202 Source
<RenderWidgetHost
>(source
).ptr()->GetProcess()->GetID();
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
)));
210 else if (type
== NOTIFICATION_RENDERER_PROCESS_CREATED
) {
211 PostTaskToGatherAndUpdateWorkerPriorities();
215 WorkerService
* WorkerService::GetInstance() {
216 return WorkerServiceImpl::GetInstance();
219 WorkerServiceImpl
* WorkerServiceImpl::GetInstance() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
221 return Singleton
<WorkerServiceImpl
>::get();
224 WorkerServiceImpl::WorkerServiceImpl()
225 : priority_setter_(new WorkerPrioritySetter()),
226 next_worker_route_id_(0) {
227 priority_setter_
->Initialize();
230 WorkerServiceImpl::~WorkerServiceImpl() {
231 // The observers in observers_ can't be used here because they might be
235 void WorkerServiceImpl::PerformTeardownForTesting() {
236 priority_setter_
= NULL
;
239 void WorkerServiceImpl::OnWorkerMessageFilterClosing(
240 WorkerMessageFilter
* filter
) {
241 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
242 iter
->FilterShutdown(filter
);
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
);
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
);
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
);
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();
286 void WorkerServiceImpl::CreateWorker(
287 const ViewHostMsg_CreateWorker_Params
& params
,
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
297 WorkerProcessHost::WorkerInstance
instance(
300 next_worker_route_id(),
302 params
.script_resource_appcache_id
,
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
);
310 CreateWorkerFromInstance(instance
);
313 void WorkerServiceImpl::LookupSharedWorker(
314 const ViewHostMsg_CreateWorker_Params
& params
,
316 WorkerMessageFilter
* filter
,
317 ResourceContext
* resource_context
,
318 const WorkerStoragePartition
& partition
,
320 bool* url_mismatch
) {
322 WorkerProcessHost::WorkerInstance
* instance
= FindSharedWorkerInstance(
323 params
.url
, params
.name
, partition
, resource_context
);
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
331 instance
= CreatePendingInstance(params
.url
, params
.name
,
332 resource_context
, partition
);
336 // Make sure the passed-in instance matches the URL - if not, return an
338 if (params
.url
!= instance
->url()) {
339 *url_mismatch
= true;
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
);
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
);
357 void WorkerServiceImpl::ForwardToWorker(const IPC::Message
& message
,
358 WorkerMessageFilter
* filter
) {
359 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
360 if (iter
->FilterMessage(message
, filter
))
364 // TODO(jabdelmalek): tell filter that callee is gone
367 void 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
);
373 // Remove any queued shared workers for this document.
374 for (WorkerProcessHost::Instances::iterator iter
= queued_workers_
.begin();
375 iter
!= queued_workers_
.end();) {
377 iter
->worker_document_set()->Remove(filter
, document_id
);
378 if (iter
->worker_document_set()->IsEmpty()) {
379 iter
= queued_workers_
.erase(iter
);
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
);
398 bool WorkerServiceImpl::CreateWorkerFromInstance(
399 WorkerProcessHost::WorkerInstance instance
) {
400 if (!CanCreateWorkerProcess(instance
)) {
401 queued_workers_
.push_back(instance
);
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
))
422 filter_info
.first
->Send(new ViewMsg_WorkerCreated(filter_info
.second
));
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.
436 !pending
->HasFilter(filter_info
.first
, filter_info
.second
)) {
437 DLOG(WARNING
) << "Pending worker already exited";
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
);
450 RemovePendingInstances(instance
.url(), instance
.name(),
451 instance
.partition(), instance
.resource_context());
453 // Remove any queued instances of this worker and copy over the filter to
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
=
462 instance
.AddFilter(filter_info
.first
, filter_info
.second
);
463 iter
= queued_workers_
.erase(iter
);
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())) {
481 worker
->CreateWorker(instance
);
483 WorkerServiceObserver
, observers_
,
484 WorkerCreated(instance
.url(), instance
.name(), worker
->GetData().id
,
485 instance
.worker_route_id()));
486 WorkerDevToolsManager::GetInstance()->WorkerCreated(worker
, instance
);
490 bool 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();
496 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter
=
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
)) {
505 // Return false if already at the global worker limit (no need to continue
506 // checking parent tabs).
507 if (hit_total_worker_limit
)
510 // If we've reached here, none of the parent tabs is allowed to create an
515 bool WorkerServiceImpl::TabCanCreateWorkerProcess(
516 int render_process_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
) {
527 if (total_workers
>= kMaxWorkersWhenSeparate
) {
528 *hit_total_worker_limit
= true;
531 if (cur_instance
->RendererIsParent(render_process_id
, render_view_id
)) {
533 if (workers_per_tab
>= kMaxWorkersPerTabWhenSeparate
)
542 void WorkerServiceImpl::TryStartingQueuedWorker() {
543 if (queued_workers_
.empty())
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
);
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();
565 bool 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
)
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())
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();
587 const WorkerProcessHost::WorkerInstance
* WorkerServiceImpl::FindWorkerInstance(
588 int worker_process_id
) {
589 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
590 if (iter
.GetData().id
!= worker_process_id
)
593 WorkerProcessHost::Instances::const_iterator instance
=
594 iter
->instances().begin();
595 return instance
== iter
->instances().end() ? NULL
: &*instance
;
600 bool 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
);
610 std::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
;
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
);
628 void WorkerServiceImpl::AddObserver(WorkerServiceObserver
* observer
) {
629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
630 observers_
.AddObserver(observer
);
633 void WorkerServiceImpl::RemoveObserver(WorkerServiceObserver
* observer
) {
634 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
635 observers_
.RemoveObserver(observer
);
638 void 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
));
647 void WorkerServiceImpl::NotifyWorkerProcessCreated() {
648 priority_setter_
->NotifyWorkerProcessCreated();
651 WorkerProcessHost::WorkerInstance
* WorkerServiceImpl::FindSharedWorkerInstance(
653 const 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();
661 if (instance_iter
->Matches(url
, name
, partition
, resource_context
))
662 return &(*instance_iter
);
668 WorkerProcessHost::WorkerInstance
* WorkerServiceImpl::FindPendingInstance(
670 const 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();
678 if (iter
->Matches(url
, name
, partition
, resource_context
))
685 void WorkerServiceImpl::RemovePendingInstances(
687 const 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
);
702 WorkerProcessHost::WorkerInstance
* WorkerServiceImpl::CreatePendingInstance(
704 const 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
);
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();
720 } // namespace content