1 // Copyright 2014 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/shared_worker/shared_worker_service_impl.h"
12 #include "base/callback.h"
13 #include "base/memory/ref_counted.h"
14 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
15 #include "content/browser/renderer_host/render_process_host_impl.h"
16 #include "content/browser/shared_worker/shared_worker_host.h"
17 #include "content/browser/shared_worker/shared_worker_instance.h"
18 #include "content/browser/shared_worker/shared_worker_message_filter.h"
19 #include "content/browser/worker_host/worker_document_set.h"
20 #include "content/common/view_messages.h"
21 #include "content/common/worker_messages.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/worker_service_observer.h"
28 class ScopedWorkerDependencyChecker
{
30 explicit ScopedWorkerDependencyChecker(SharedWorkerServiceImpl
* service
)
31 : service_(service
) {}
32 ScopedWorkerDependencyChecker(SharedWorkerServiceImpl
* service
,
33 base::Closure done_closure
)
34 : service_(service
), done_closure_(done_closure
) {}
35 ~ScopedWorkerDependencyChecker() {
36 service_
->CheckWorkerDependency();
37 if (!done_closure_
.is_null())
42 SharedWorkerServiceImpl
* service_
;
43 base::Closure done_closure_
;
44 DISALLOW_COPY_AND_ASSIGN(ScopedWorkerDependencyChecker
);
47 void UpdateWorkerDependencyOnUI(const std::vector
<int>& added_ids
,
48 const std::vector
<int>& removed_ids
) {
49 for (size_t i
= 0; i
< added_ids
.size(); ++i
) {
50 RenderProcessHostImpl
* render_process_host_impl
=
51 static_cast<RenderProcessHostImpl
*>(
52 RenderProcessHost::FromID(added_ids
[i
]));
53 if (!render_process_host_impl
)
55 render_process_host_impl
->IncrementWorkerRefCount();
57 for (size_t i
= 0; i
< removed_ids
.size(); ++i
) {
58 RenderProcessHostImpl
* render_process_host_impl
=
59 static_cast<RenderProcessHostImpl
*>(
60 RenderProcessHost::FromID(removed_ids
[i
]));
61 if (!render_process_host_impl
)
63 render_process_host_impl
->DecrementWorkerRefCount();
67 void UpdateWorkerDependency(const std::vector
<int>& added_ids
,
68 const std::vector
<int>& removed_ids
) {
69 BrowserThread::PostTask(
72 base::Bind(&UpdateWorkerDependencyOnUI
, added_ids
, removed_ids
));
75 void DecrementWorkerRefCount(int process_id
) {
76 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
77 BrowserThread::PostTask(BrowserThread::UI
,
79 base::Bind(&DecrementWorkerRefCount
, process_id
));
82 RenderProcessHostImpl
* render_process_host_impl
=
83 static_cast<RenderProcessHostImpl
*>(
84 RenderProcessHost::FromID(process_id
));
85 if (render_process_host_impl
)
86 render_process_host_impl
->DecrementWorkerRefCount();
89 bool TryIncrementWorkerRefCount(int worker_process_id
) {
90 RenderProcessHostImpl
* render_process
= static_cast<RenderProcessHostImpl
*>(
91 RenderProcessHost::FromID(worker_process_id
));
92 if (!render_process
|| render_process
->FastShutdownStarted()) {
95 render_process
->IncrementWorkerRefCount();
101 class SharedWorkerServiceImpl::SharedWorkerPendingInstance
{
103 struct SharedWorkerPendingRequest
{
104 SharedWorkerPendingRequest(SharedWorkerMessageFilter
* filter
,
106 unsigned long long document_id
,
107 int render_process_id
,
108 int render_frame_route_id
)
111 document_id(document_id
),
112 render_process_id(render_process_id
),
113 render_frame_route_id(render_frame_route_id
) {}
114 SharedWorkerMessageFilter
* const filter
;
116 const unsigned long long document_id
;
117 const int render_process_id
;
118 const int render_frame_route_id
;
121 typedef ScopedVector
<SharedWorkerPendingRequest
> SharedWorkerPendingRequests
;
123 explicit SharedWorkerPendingInstance(
124 scoped_ptr
<SharedWorkerInstance
> instance
)
125 : instance_(instance
.Pass()) {}
126 ~SharedWorkerPendingInstance() {}
127 SharedWorkerInstance
* instance() { return instance_
.get(); }
128 SharedWorkerInstance
* release_instance() { return instance_
.release(); }
129 SharedWorkerPendingRequests
* requests() { return &requests_
; }
130 SharedWorkerMessageFilter
* FindFilter(int process_id
) {
131 for (size_t i
= 0; i
< requests_
.size(); ++i
) {
132 if (requests_
[i
]->render_process_id
== process_id
)
133 return requests_
[i
]->filter
;
137 void AddRequest(scoped_ptr
<SharedWorkerPendingRequest
> request_info
) {
138 requests_
.push_back(request_info
.release());
140 void RemoveRequest(int process_id
) {
141 for (SharedWorkerPendingRequests::iterator request_itr
= requests_
.begin();
142 request_itr
!= requests_
.end();) {
143 if ((*request_itr
)->render_process_id
== process_id
)
144 request_itr
= requests_
.erase(request_itr
);
149 void RegisterToSharedWorkerHost(SharedWorkerHost
* host
) {
150 for (size_t i
= 0; i
< requests_
.size(); ++i
) {
151 SharedWorkerPendingRequest
* request
= requests_
[i
];
152 host
->AddFilter(request
->filter
, request
->route_id
);
153 host
->worker_document_set()->Add(request
->filter
,
154 request
->document_id
,
155 request
->render_process_id
,
156 request
->render_frame_route_id
);
159 void SendWorkerCreatedMessages() {
160 for (size_t i
= 0; i
< requests_
.size(); ++i
) {
161 SharedWorkerPendingRequest
* request
= requests_
[i
];
162 request
->filter
->Send(new ViewMsg_WorkerCreated(request
->route_id
));
167 scoped_ptr
<SharedWorkerInstance
> instance_
;
168 SharedWorkerPendingRequests requests_
;
169 DISALLOW_COPY_AND_ASSIGN(SharedWorkerPendingInstance
);
172 class SharedWorkerServiceImpl::SharedWorkerReserver
173 : public base::RefCountedThreadSafe
<SharedWorkerReserver
> {
175 SharedWorkerReserver(int pending_instance_id
,
176 int worker_process_id
,
179 const SharedWorkerInstance
& instance
)
180 : worker_process_id_(worker_process_id
),
181 worker_route_id_(worker_route_id
),
182 is_new_worker_(is_new_worker
),
183 instance_(instance
) {}
185 void TryReserve(const base::Callback
<void(bool)>& success_cb
,
186 const base::Closure
& failure_cb
,
187 bool (*try_increment_worker_ref_count
)(int)) {
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
189 if (!try_increment_worker_ref_count(worker_process_id_
)) {
190 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, failure_cb
);
193 bool pause_on_start
= false;
194 if (is_new_worker_
) {
196 EmbeddedWorkerDevToolsManager::GetInstance()->SharedWorkerCreated(
197 worker_process_id_
, worker_route_id_
, instance_
);
199 BrowserThread::PostTask(
200 BrowserThread::IO
, FROM_HERE
, base::Bind(success_cb
, pause_on_start
));
204 friend class base::RefCountedThreadSafe
<SharedWorkerReserver
>;
205 ~SharedWorkerReserver() {}
207 const int worker_process_id_
;
208 const int worker_route_id_
;
209 const bool is_new_worker_
;
210 const SharedWorkerInstance instance_
;
214 bool (*SharedWorkerServiceImpl::s_try_increment_worker_ref_count_
)(int) =
215 TryIncrementWorkerRefCount
;
217 SharedWorkerServiceImpl
* SharedWorkerServiceImpl::GetInstance() {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
219 return Singleton
<SharedWorkerServiceImpl
>::get();
222 SharedWorkerServiceImpl::SharedWorkerServiceImpl()
223 : update_worker_dependency_(UpdateWorkerDependency
),
224 next_pending_instance_id_(0) {
227 SharedWorkerServiceImpl::~SharedWorkerServiceImpl() {}
229 void SharedWorkerServiceImpl::ResetForTesting() {
230 last_worker_depended_renderers_
.clear();
231 worker_hosts_
.clear();
233 update_worker_dependency_
= UpdateWorkerDependency
;
234 s_try_increment_worker_ref_count_
= TryIncrementWorkerRefCount
;
237 bool SharedWorkerServiceImpl::TerminateWorker(int process_id
, int route_id
) {
238 SharedWorkerHost
* host
=
239 worker_hosts_
.get(std::make_pair(process_id
, route_id
));
240 if (!host
|| !host
->instance())
242 host
->TerminateWorker();
246 std::vector
<WorkerService::WorkerInfo
> SharedWorkerServiceImpl::GetWorkers() {
247 std::vector
<WorkerService::WorkerInfo
> results
;
248 for (WorkerHostMap::const_iterator iter
= worker_hosts_
.begin();
249 iter
!= worker_hosts_
.end();
251 SharedWorkerHost
* host
= iter
->second
;
252 const SharedWorkerInstance
* instance
= host
->instance();
254 WorkerService::WorkerInfo info
;
255 info
.url
= instance
->url();
256 info
.name
= instance
->name();
257 info
.route_id
= host
->worker_route_id();
258 info
.process_id
= host
->process_id();
259 info
.handle
= host
->container_render_filter()->PeerHandle();
260 results
.push_back(info
);
266 void SharedWorkerServiceImpl::AddObserver(WorkerServiceObserver
* observer
) {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
268 observers_
.AddObserver(observer
);
271 void SharedWorkerServiceImpl::RemoveObserver(WorkerServiceObserver
* observer
) {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
273 observers_
.RemoveObserver(observer
);
276 void SharedWorkerServiceImpl::CreateWorker(
277 const ViewHostMsg_CreateWorker_Params
& params
,
279 SharedWorkerMessageFilter
* filter
,
280 ResourceContext
* resource_context
,
281 const WorkerStoragePartitionId
& partition_id
,
282 bool* url_mismatch
) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
284 *url_mismatch
= false;
285 scoped_ptr
<SharedWorkerInstance
> instance(
286 new SharedWorkerInstance(params
.url
,
288 params
.content_security_policy
,
289 params
.security_policy_type
,
292 scoped_ptr
<SharedWorkerPendingInstance::SharedWorkerPendingRequest
> request(
293 new SharedWorkerPendingInstance::SharedWorkerPendingRequest(
297 filter
->render_process_id(),
298 params
.render_frame_route_id
));
299 if (SharedWorkerPendingInstance
* pending
= FindPendingInstance(*instance
)) {
300 if (params
.url
!= pending
->instance()->url()) {
301 *url_mismatch
= true;
304 pending
->AddRequest(request
.Pass());
307 scoped_ptr
<SharedWorkerPendingInstance
> pending_instance(
308 new SharedWorkerPendingInstance(instance
.Pass()));
309 pending_instance
->AddRequest(request
.Pass());
310 ReserveRenderProcessToCreateWorker(pending_instance
.Pass(), url_mismatch
);
313 void SharedWorkerServiceImpl::ForwardToWorker(
314 const IPC::Message
& message
,
315 SharedWorkerMessageFilter
* filter
) {
316 for (WorkerHostMap::const_iterator iter
= worker_hosts_
.begin();
317 iter
!= worker_hosts_
.end();
319 if (iter
->second
->FilterMessage(message
, filter
))
324 void SharedWorkerServiceImpl::DocumentDetached(
325 unsigned long long document_id
,
326 SharedWorkerMessageFilter
* filter
) {
327 ScopedWorkerDependencyChecker
checker(this);
328 for (WorkerHostMap::const_iterator iter
= worker_hosts_
.begin();
329 iter
!= worker_hosts_
.end();
331 iter
->second
->DocumentDetached(filter
, document_id
);
335 void SharedWorkerServiceImpl::WorkerContextClosed(
337 SharedWorkerMessageFilter
* filter
) {
338 ScopedWorkerDependencyChecker
checker(this);
339 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
340 host
->WorkerContextClosed();
343 void SharedWorkerServiceImpl::WorkerContextDestroyed(
345 SharedWorkerMessageFilter
* filter
) {
346 ScopedWorkerDependencyChecker
checker(this);
347 scoped_ptr
<SharedWorkerHost
> host
=
348 worker_hosts_
.take_and_erase(std::make_pair(filter
->render_process_id(),
352 host
->WorkerContextDestroyed();
355 void SharedWorkerServiceImpl::WorkerScriptLoaded(
357 SharedWorkerMessageFilter
* filter
) {
358 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
359 host
->WorkerScriptLoaded();
362 void SharedWorkerServiceImpl::WorkerScriptLoadFailed(
364 SharedWorkerMessageFilter
* filter
) {
365 ScopedWorkerDependencyChecker
checker(this);
366 scoped_ptr
<SharedWorkerHost
> host
=
367 worker_hosts_
.take_and_erase(std::make_pair(filter
->render_process_id(),
371 host
->WorkerScriptLoadFailed();
374 void SharedWorkerServiceImpl::WorkerConnected(
377 SharedWorkerMessageFilter
* filter
) {
378 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
379 host
->WorkerConnected(message_port_id
);
382 void SharedWorkerServiceImpl::AllowDatabase(
385 const base::string16
& name
,
386 const base::string16
& display_name
,
387 unsigned long estimated_size
,
389 SharedWorkerMessageFilter
* filter
) {
390 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
391 host
->AllowDatabase(url
, name
, display_name
, estimated_size
, result
);
394 void SharedWorkerServiceImpl::AllowFileSystem(
398 SharedWorkerMessageFilter
* filter
) {
399 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
400 host
->AllowFileSystem(url
, result
);
403 void SharedWorkerServiceImpl::AllowIndexedDB(
406 const base::string16
& name
,
408 SharedWorkerMessageFilter
* filter
) {
409 if (SharedWorkerHost
* host
= FindSharedWorkerHost(filter
, worker_route_id
))
410 host
->AllowIndexedDB(url
, name
, result
);
413 void SharedWorkerServiceImpl::OnSharedWorkerMessageFilterClosing(
414 SharedWorkerMessageFilter
* filter
) {
415 ScopedWorkerDependencyChecker
checker(this);
416 std::vector
<ProcessRouteIdPair
> remove_list
;
417 for (WorkerHostMap::iterator iter
= worker_hosts_
.begin();
418 iter
!= worker_hosts_
.end();
420 iter
->second
->FilterShutdown(filter
);
421 if (iter
->first
.first
== filter
->render_process_id())
422 remove_list
.push_back(iter
->first
);
424 for (size_t i
= 0; i
< remove_list
.size(); ++i
) {
425 scoped_ptr
<SharedWorkerHost
> host
=
426 worker_hosts_
.take_and_erase(remove_list
[i
]);
429 std::vector
<int> remove_pending_instance_list
;
430 for (PendingInstaneMap::iterator iter
= pending_instances_
.begin();
431 iter
!= pending_instances_
.end();
433 iter
->second
->RemoveRequest(filter
->render_process_id());
434 if (!iter
->second
->requests()->size())
435 remove_pending_instance_list
.push_back(iter
->first
);
437 for (size_t i
= 0; i
< remove_pending_instance_list
.size(); ++i
)
438 pending_instances_
.take_and_erase(remove_pending_instance_list
[i
]);
441 void SharedWorkerServiceImpl::NotifyWorkerDestroyed(int worker_process_id
,
442 int worker_route_id
) {
443 FOR_EACH_OBSERVER(WorkerServiceObserver
,
445 WorkerDestroyed(worker_process_id
, worker_route_id
));
448 void SharedWorkerServiceImpl::ReserveRenderProcessToCreateWorker(
449 scoped_ptr
<SharedWorkerPendingInstance
> pending_instance
,
450 bool* url_mismatch
) {
451 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
452 DCHECK(!FindPendingInstance(*pending_instance
->instance()));
454 *url_mismatch
= false;
455 if (!pending_instance
->requests()->size())
457 int worker_process_id
= -1;
458 int worker_route_id
= MSG_ROUTING_NONE
;
459 bool is_new_worker
= true;
460 SharedWorkerHost
* host
= FindSharedWorkerHost(*pending_instance
->instance());
462 if (pending_instance
->instance()->url() != host
->instance()->url()) {
464 *url_mismatch
= true;
467 worker_process_id
= host
->process_id();
468 worker_route_id
= host
->worker_route_id();
469 is_new_worker
= false;
471 SharedWorkerMessageFilter
* first_filter
=
472 (*pending_instance
->requests()->begin())->filter
;
473 worker_process_id
= first_filter
->render_process_id();
474 worker_route_id
= first_filter
->GetNextRoutingID();
476 const int pending_instance_id
= next_pending_instance_id_
++;
477 scoped_refptr
<SharedWorkerReserver
> reserver(
478 new SharedWorkerReserver(pending_instance_id
,
482 *pending_instance
->instance()));
483 BrowserThread::PostTask(
487 &SharedWorkerReserver::TryReserve
,
489 base::Bind(&SharedWorkerServiceImpl::RenderProcessReservedCallback
,
490 base::Unretained(this),
496 &SharedWorkerServiceImpl::RenderProcessReserveFailedCallback
,
497 base::Unretained(this),
502 s_try_increment_worker_ref_count_
));
503 pending_instances_
.set(pending_instance_id
, pending_instance
.Pass());
506 void SharedWorkerServiceImpl::RenderProcessReservedCallback(
507 int pending_instance_id
,
508 int worker_process_id
,
511 bool pause_on_start
) {
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
513 // To offset the TryIncrementWorkerRefCount called for the reservation,
514 // calls DecrementWorkerRefCount after CheckWorkerDependency in
515 // ScopeWorkerDependencyChecker's destructor.
516 ScopedWorkerDependencyChecker
checker(
517 this, base::Bind(&DecrementWorkerRefCount
, worker_process_id
));
518 scoped_ptr
<SharedWorkerPendingInstance
> pending_instance
=
519 pending_instances_
.take_and_erase(pending_instance_id
);
520 if (!pending_instance
)
522 if (!is_new_worker
) {
523 SharedWorkerHost
* existing_host
=
524 worker_hosts_
.get(std::make_pair(worker_process_id
, worker_route_id
));
525 if (!existing_host
) {
526 // Retry reserving a renderer process if the existed Shared Worker was
527 // destroyed on IO thread while reserving the renderer process on UI
529 ReserveRenderProcessToCreateWorker(pending_instance
.Pass(), NULL
);
532 pending_instance
->RegisterToSharedWorkerHost(existing_host
);
533 pending_instance
->SendWorkerCreatedMessages();
536 SharedWorkerMessageFilter
* filter
=
537 pending_instance
->FindFilter(worker_process_id
);
539 pending_instance
->RemoveRequest(worker_process_id
);
540 // Retry reserving a renderer process if the requested renderer process was
541 // destroyed on IO thread while reserving the renderer process on UI thread.
542 ReserveRenderProcessToCreateWorker(pending_instance
.Pass(), NULL
);
545 scoped_ptr
<SharedWorkerHost
> host(new SharedWorkerHost(
546 pending_instance
->release_instance(), filter
, worker_route_id
));
547 pending_instance
->RegisterToSharedWorkerHost(host
.get());
548 const GURL url
= host
->instance()->url();
549 const base::string16 name
= host
->instance()->name();
550 host
->Start(pause_on_start
);
551 worker_hosts_
.set(std::make_pair(worker_process_id
, worker_route_id
),
554 WorkerServiceObserver
,
556 WorkerCreated(url
, name
, worker_process_id
, worker_route_id
));
559 void SharedWorkerServiceImpl::RenderProcessReserveFailedCallback(
560 int pending_instance_id
,
561 int worker_process_id
,
563 bool is_new_worker
) {
564 worker_hosts_
.take_and_erase(
565 std::make_pair(worker_process_id
, worker_route_id
));
566 scoped_ptr
<SharedWorkerPendingInstance
> pending_instance
=
567 pending_instances_
.take_and_erase(pending_instance_id
);
568 if (!pending_instance
)
570 pending_instance
->RemoveRequest(worker_process_id
);
571 // Retry reserving a renderer process.
572 ReserveRenderProcessToCreateWorker(pending_instance
.Pass(), NULL
);
575 SharedWorkerHost
* SharedWorkerServiceImpl::FindSharedWorkerHost(
576 SharedWorkerMessageFilter
* filter
,
577 int worker_route_id
) {
578 return worker_hosts_
.get(std::make_pair(filter
->render_process_id(),
582 SharedWorkerHost
* SharedWorkerServiceImpl::FindSharedWorkerHost(
583 const SharedWorkerInstance
& instance
) {
584 for (WorkerHostMap::const_iterator iter
= worker_hosts_
.begin();
585 iter
!= worker_hosts_
.end();
587 SharedWorkerHost
* host
= iter
->second
;
588 if (host
->instance() && !host
->closed() &&
589 host
->instance()->Matches(instance
)) {
596 SharedWorkerServiceImpl::SharedWorkerPendingInstance
*
597 SharedWorkerServiceImpl::FindPendingInstance(
598 const SharedWorkerInstance
& instance
) {
599 for (PendingInstaneMap::iterator iter
= pending_instances_
.begin();
600 iter
!= pending_instances_
.end();
602 if (iter
->second
->instance()->Matches(instance
))
609 SharedWorkerServiceImpl::GetRenderersWithWorkerDependency() {
610 std::set
<int> dependent_renderers
;
611 for (WorkerHostMap::iterator host_iter
= worker_hosts_
.begin();
612 host_iter
!= worker_hosts_
.end();
614 const int process_id
= host_iter
->first
.first
;
615 if (dependent_renderers
.count(process_id
))
617 if (host_iter
->second
->instance() &&
618 host_iter
->second
->worker_document_set()->ContainsExternalRenderer(
620 dependent_renderers
.insert(process_id
);
623 return dependent_renderers
;
626 void SharedWorkerServiceImpl::CheckWorkerDependency() {
627 const std::set
<int> current_worker_depended_renderers
=
628 GetRenderersWithWorkerDependency();
629 std::vector
<int> added_items
;
630 std::vector
<int> removed_items
;
631 std::set_difference(current_worker_depended_renderers
.begin(),
632 current_worker_depended_renderers
.end(),
633 last_worker_depended_renderers_
.begin(),
634 last_worker_depended_renderers_
.end(),
635 std::back_inserter(added_items
));
636 std::set_difference(last_worker_depended_renderers_
.begin(),
637 last_worker_depended_renderers_
.end(),
638 current_worker_depended_renderers
.begin(),
639 current_worker_depended_renderers
.end(),
640 std::back_inserter(removed_items
));
641 if (!added_items
.empty() || !removed_items
.empty()) {
642 last_worker_depended_renderers_
= current_worker_depended_renderers
;
643 update_worker_dependency_(added_items
, removed_items
);
647 void SharedWorkerServiceImpl::ChangeUpdateWorkerDependencyFuncForTesting(
648 UpdateWorkerDependencyFunc new_func
) {
649 update_worker_dependency_
= new_func
;
652 void SharedWorkerServiceImpl::ChangeTryIncrementWorkerRefCountFuncForTesting(
653 bool (*new_func
)(int)) {
654 s_try_increment_worker_ref_count_
= new_func
;
657 } // namespace content