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