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/shared_worker/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"
27 WorkerService* WorkerService::GetInstance() {
28 return SharedWorkerServiceImpl::GetInstance();
33 class ScopedWorkerDependencyChecker {
35 explicit ScopedWorkerDependencyChecker(SharedWorkerServiceImpl* service)
36 : service_(service) {}
37 ScopedWorkerDependencyChecker(SharedWorkerServiceImpl* service,
38 base::Closure done_closure)
39 : service_(service), done_closure_(done_closure) {}
40 ~ScopedWorkerDependencyChecker() {
41 service_->CheckWorkerDependency();
42 if (!done_closure_.is_null())
47 SharedWorkerServiceImpl* service_;
48 base::Closure done_closure_;
49 DISALLOW_COPY_AND_ASSIGN(ScopedWorkerDependencyChecker);
52 void UpdateWorkerDependencyOnUI(const std::vector<int>& added_ids,
53 const std::vector<int>& removed_ids) {
54 for (size_t i = 0; i < added_ids.size(); ++i) {
55 RenderProcessHostImpl* render_process_host_impl =
56 static_cast<RenderProcessHostImpl*>(
57 RenderProcessHost::FromID(added_ids[i]));
58 if (!render_process_host_impl)
60 render_process_host_impl->IncrementWorkerRefCount();
62 for (size_t i = 0; i < removed_ids.size(); ++i) {
63 RenderProcessHostImpl* render_process_host_impl =
64 static_cast<RenderProcessHostImpl*>(
65 RenderProcessHost::FromID(removed_ids[i]));
66 if (!render_process_host_impl)
68 render_process_host_impl->DecrementWorkerRefCount();
72 void UpdateWorkerDependency(const std::vector<int>& added_ids,
73 const std::vector<int>& removed_ids) {
74 BrowserThread::PostTask(
77 base::Bind(&UpdateWorkerDependencyOnUI, added_ids, removed_ids));
80 void DecrementWorkerRefCount(int process_id) {
81 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
82 BrowserThread::PostTask(BrowserThread::UI,
84 base::Bind(&DecrementWorkerRefCount, process_id));
87 RenderProcessHostImpl* render_process_host_impl =
88 static_cast<RenderProcessHostImpl*>(
89 RenderProcessHost::FromID(process_id));
90 if (render_process_host_impl)
91 render_process_host_impl->DecrementWorkerRefCount();
94 bool TryIncrementWorkerRefCount(int worker_process_id) {
95 RenderProcessHostImpl* render_process = static_cast<RenderProcessHostImpl*>(
96 RenderProcessHost::FromID(worker_process_id));
97 if (!render_process || render_process->FastShutdownStarted()) {
100 render_process->IncrementWorkerRefCount();
106 class SharedWorkerServiceImpl::SharedWorkerPendingInstance {
108 struct SharedWorkerPendingRequest {
109 SharedWorkerPendingRequest(SharedWorkerMessageFilter* filter,
111 unsigned long long document_id,
112 int render_process_id,
113 int render_frame_route_id)
116 document_id(document_id),
117 render_process_id(render_process_id),
118 render_frame_route_id(render_frame_route_id) {}
119 SharedWorkerMessageFilter* const filter;
121 const unsigned long long document_id;
122 const int render_process_id;
123 const int render_frame_route_id;
126 typedef ScopedVector<SharedWorkerPendingRequest> SharedWorkerPendingRequests;
128 explicit SharedWorkerPendingInstance(
129 scoped_ptr<SharedWorkerInstance> instance)
130 : instance_(instance.Pass()) {}
131 ~SharedWorkerPendingInstance() {}
132 SharedWorkerInstance* instance() { return instance_.get(); }
133 SharedWorkerInstance* release_instance() { return instance_.release(); }
134 SharedWorkerPendingRequests* requests() { return &requests_; }
135 SharedWorkerMessageFilter* FindFilter(int process_id) {
136 for (size_t i = 0; i < requests_.size(); ++i) {
137 if (requests_[i]->render_process_id == process_id)
138 return requests_[i]->filter;
142 void AddRequest(scoped_ptr<SharedWorkerPendingRequest> request_info) {
143 requests_.push_back(request_info.release());
145 void RemoveRequest(int process_id) {
146 for (SharedWorkerPendingRequests::iterator request_itr = requests_.begin();
147 request_itr != requests_.end();) {
148 if ((*request_itr)->render_process_id == process_id)
149 request_itr = requests_.erase(request_itr);
154 void RegisterToSharedWorkerHost(SharedWorkerHost* host) {
155 for (size_t i = 0; i < requests_.size(); ++i) {
156 SharedWorkerPendingRequest* request = requests_[i];
157 host->AddFilter(request->filter, request->route_id);
158 host->worker_document_set()->Add(request->filter,
159 request->document_id,
160 request->render_process_id,
161 request->render_frame_route_id);
164 void SendWorkerCreatedMessages() {
165 for (size_t i = 0; i < requests_.size(); ++i) {
166 SharedWorkerPendingRequest* request = requests_[i];
167 request->filter->Send(new ViewMsg_WorkerCreated(request->route_id));
172 scoped_ptr<SharedWorkerInstance> instance_;
173 SharedWorkerPendingRequests requests_;
174 DISALLOW_COPY_AND_ASSIGN(SharedWorkerPendingInstance);
177 class SharedWorkerServiceImpl::SharedWorkerReserver
178 : public base::RefCountedThreadSafe<SharedWorkerReserver> {
180 SharedWorkerReserver(int pending_instance_id,
181 int worker_process_id,
184 const SharedWorkerInstance& instance)
185 : worker_process_id_(worker_process_id),
186 worker_route_id_(worker_route_id),
187 is_new_worker_(is_new_worker),
188 instance_(instance) {}
190 void TryReserve(const base::Callback<void(bool)>& success_cb,
191 const base::Closure& failure_cb,
192 bool (*try_increment_worker_ref_count)(int)) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
194 if (!try_increment_worker_ref_count(worker_process_id_)) {
195 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_cb);
198 bool pause_on_start = false;
199 if (is_new_worker_) {
201 EmbeddedWorkerDevToolsManager::GetInstance()->SharedWorkerCreated(
202 worker_process_id_, worker_route_id_, instance_);
204 BrowserThread::PostTask(
205 BrowserThread::IO, FROM_HERE, base::Bind(success_cb, pause_on_start));
209 friend class base::RefCountedThreadSafe<SharedWorkerReserver>;
210 ~SharedWorkerReserver() {}
212 const int worker_process_id_;
213 const int worker_route_id_;
214 const bool is_new_worker_;
215 const SharedWorkerInstance instance_;
219 bool (*SharedWorkerServiceImpl::s_try_increment_worker_ref_count_)(int) =
220 TryIncrementWorkerRefCount;
222 SharedWorkerServiceImpl* SharedWorkerServiceImpl::GetInstance() {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224 return Singleton<SharedWorkerServiceImpl>::get();
227 SharedWorkerServiceImpl::SharedWorkerServiceImpl()
228 : update_worker_dependency_(UpdateWorkerDependency),
229 next_pending_instance_id_(0) {
232 SharedWorkerServiceImpl::~SharedWorkerServiceImpl() {}
234 void SharedWorkerServiceImpl::ResetForTesting() {
235 last_worker_depended_renderers_.clear();
236 worker_hosts_.clear();
238 update_worker_dependency_ = UpdateWorkerDependency;
239 s_try_increment_worker_ref_count_ = TryIncrementWorkerRefCount;
242 bool SharedWorkerServiceImpl::TerminateWorker(int process_id, int route_id) {
243 SharedWorkerHost* host =
244 worker_hosts_.get(std::make_pair(process_id, route_id));
245 if (!host || !host->instance())
247 host->TerminateWorker();
251 std::vector<WorkerService::WorkerInfo> SharedWorkerServiceImpl::GetWorkers() {
252 std::vector<WorkerService::WorkerInfo> results;
253 for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
254 iter != worker_hosts_.end();
256 SharedWorkerHost* host = iter->second;
257 const SharedWorkerInstance* instance = host->instance();
259 WorkerService::WorkerInfo info;
260 info.url = instance->url();
261 info.name = instance->name();
262 info.route_id = host->worker_route_id();
263 info.process_id = host->process_id();
264 info.handle = host->container_render_filter()->PeerHandle();
265 results.push_back(info);
271 void SharedWorkerServiceImpl::AddObserver(WorkerServiceObserver* observer) {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273 observers_.AddObserver(observer);
276 void SharedWorkerServiceImpl::RemoveObserver(WorkerServiceObserver* observer) {
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
278 observers_.RemoveObserver(observer);
281 void SharedWorkerServiceImpl::CreateWorker(
282 const ViewHostMsg_CreateWorker_Params& params,
284 SharedWorkerMessageFilter* filter,
285 ResourceContext* resource_context,
286 const WorkerStoragePartitionId& partition_id,
287 bool* url_mismatch) {
288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
289 *url_mismatch = false;
290 scoped_ptr<SharedWorkerInstance> instance(
291 new SharedWorkerInstance(params.url,
293 params.content_security_policy,
294 params.security_policy_type,
297 scoped_ptr<SharedWorkerPendingInstance::SharedWorkerPendingRequest> request(
298 new SharedWorkerPendingInstance::SharedWorkerPendingRequest(
302 filter->render_process_id(),
303 params.render_frame_route_id));
304 if (SharedWorkerPendingInstance* pending = FindPendingInstance(*instance)) {
305 if (params.url != pending->instance()->url()) {
306 *url_mismatch = true;
309 pending->AddRequest(request.Pass());
312 scoped_ptr<SharedWorkerPendingInstance> pending_instance(
313 new SharedWorkerPendingInstance(instance.Pass()));
314 pending_instance->AddRequest(request.Pass());
315 ReserveRenderProcessToCreateWorker(pending_instance.Pass(), url_mismatch);
318 void SharedWorkerServiceImpl::ForwardToWorker(
319 const IPC::Message& message,
320 SharedWorkerMessageFilter* filter) {
321 for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
322 iter != worker_hosts_.end();
324 if (iter->second->FilterMessage(message, filter))
329 void SharedWorkerServiceImpl::DocumentDetached(
330 unsigned long long document_id,
331 SharedWorkerMessageFilter* filter) {
332 ScopedWorkerDependencyChecker checker(this);
333 for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
334 iter != worker_hosts_.end();
336 iter->second->DocumentDetached(filter, document_id);
340 void SharedWorkerServiceImpl::WorkerContextClosed(
342 SharedWorkerMessageFilter* filter) {
343 ScopedWorkerDependencyChecker checker(this);
344 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
345 host->WorkerContextClosed();
348 void SharedWorkerServiceImpl::WorkerContextDestroyed(
350 SharedWorkerMessageFilter* filter) {
351 ScopedWorkerDependencyChecker checker(this);
352 scoped_ptr<SharedWorkerHost> host =
353 worker_hosts_.take_and_erase(std::make_pair(filter->render_process_id(),
357 host->WorkerContextDestroyed();
360 void SharedWorkerServiceImpl::WorkerScriptLoaded(
362 SharedWorkerMessageFilter* filter) {
363 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
364 host->WorkerScriptLoaded();
367 void SharedWorkerServiceImpl::WorkerScriptLoadFailed(
369 SharedWorkerMessageFilter* filter) {
370 ScopedWorkerDependencyChecker checker(this);
371 scoped_ptr<SharedWorkerHost> host =
372 worker_hosts_.take_and_erase(std::make_pair(filter->render_process_id(),
376 host->WorkerScriptLoadFailed();
379 void SharedWorkerServiceImpl::WorkerConnected(
382 SharedWorkerMessageFilter* filter) {
383 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
384 host->WorkerConnected(message_port_id);
387 void SharedWorkerServiceImpl::AllowDatabase(
390 const base::string16& name,
391 const base::string16& display_name,
392 unsigned long estimated_size,
394 SharedWorkerMessageFilter* filter) {
395 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
396 host->AllowDatabase(url, name, display_name, estimated_size, result);
399 void SharedWorkerServiceImpl::AllowFileSystem(
402 IPC::Message* reply_msg,
403 SharedWorkerMessageFilter* filter) {
404 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id)) {
405 host->AllowFileSystem(url, make_scoped_ptr(reply_msg));
407 filter->Send(reply_msg);
412 void SharedWorkerServiceImpl::AllowIndexedDB(
415 const base::string16& name,
417 SharedWorkerMessageFilter* filter) {
418 if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
419 host->AllowIndexedDB(url, name, result);
422 void SharedWorkerServiceImpl::OnSharedWorkerMessageFilterClosing(
423 SharedWorkerMessageFilter* filter) {
424 ScopedWorkerDependencyChecker checker(this);
425 std::vector<ProcessRouteIdPair> remove_list;
426 for (WorkerHostMap::iterator iter = worker_hosts_.begin();
427 iter != worker_hosts_.end();
429 iter->second->FilterShutdown(filter);
430 if (iter->first.first == filter->render_process_id())
431 remove_list.push_back(iter->first);
433 for (size_t i = 0; i < remove_list.size(); ++i) {
434 scoped_ptr<SharedWorkerHost> host =
435 worker_hosts_.take_and_erase(remove_list[i]);
438 std::vector<int> remove_pending_instance_list;
439 for (PendingInstaneMap::iterator iter = pending_instances_.begin();
440 iter != pending_instances_.end();
442 iter->second->RemoveRequest(filter->render_process_id());
443 if (!iter->second->requests()->size())
444 remove_pending_instance_list.push_back(iter->first);
446 for (size_t i = 0; i < remove_pending_instance_list.size(); ++i)
447 pending_instances_.take_and_erase(remove_pending_instance_list[i]);
450 void SharedWorkerServiceImpl::NotifyWorkerDestroyed(int worker_process_id,
451 int worker_route_id) {
452 FOR_EACH_OBSERVER(WorkerServiceObserver,
454 WorkerDestroyed(worker_process_id, worker_route_id));
457 void SharedWorkerServiceImpl::ReserveRenderProcessToCreateWorker(
458 scoped_ptr<SharedWorkerPendingInstance> pending_instance,
459 bool* url_mismatch) {
460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
461 DCHECK(!FindPendingInstance(*pending_instance->instance()));
463 *url_mismatch = false;
464 if (!pending_instance->requests()->size())
466 int worker_process_id = -1;
467 int worker_route_id = MSG_ROUTING_NONE;
468 bool is_new_worker = true;
469 SharedWorkerHost* host = FindSharedWorkerHost(*pending_instance->instance());
471 if (pending_instance->instance()->url() != host->instance()->url()) {
473 *url_mismatch = true;
476 worker_process_id = host->process_id();
477 worker_route_id = host->worker_route_id();
478 is_new_worker = false;
480 SharedWorkerMessageFilter* first_filter =
481 (*pending_instance->requests()->begin())->filter;
482 worker_process_id = first_filter->render_process_id();
483 worker_route_id = first_filter->GetNextRoutingID();
485 const int pending_instance_id = next_pending_instance_id_++;
486 scoped_refptr<SharedWorkerReserver> reserver(
487 new SharedWorkerReserver(pending_instance_id,
491 *pending_instance->instance()));
492 BrowserThread::PostTask(
496 &SharedWorkerReserver::TryReserve,
498 base::Bind(&SharedWorkerServiceImpl::RenderProcessReservedCallback,
499 base::Unretained(this),
505 &SharedWorkerServiceImpl::RenderProcessReserveFailedCallback,
506 base::Unretained(this),
511 s_try_increment_worker_ref_count_));
512 pending_instances_.set(pending_instance_id, pending_instance.Pass());
515 void SharedWorkerServiceImpl::RenderProcessReservedCallback(
516 int pending_instance_id,
517 int worker_process_id,
520 bool pause_on_start) {
521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
522 // To offset the TryIncrementWorkerRefCount called for the reservation,
523 // calls DecrementWorkerRefCount after CheckWorkerDependency in
524 // ScopeWorkerDependencyChecker's destructor.
525 ScopedWorkerDependencyChecker checker(
526 this, base::Bind(&DecrementWorkerRefCount, worker_process_id));
527 scoped_ptr<SharedWorkerPendingInstance> pending_instance =
528 pending_instances_.take_and_erase(pending_instance_id);
529 if (!pending_instance)
531 if (!is_new_worker) {
532 SharedWorkerHost* existing_host =
533 worker_hosts_.get(std::make_pair(worker_process_id, worker_route_id));
534 if (!existing_host) {
535 // Retry reserving a renderer process if the existed Shared Worker was
536 // destroyed on IO thread while reserving the renderer process on UI
538 ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
541 pending_instance->RegisterToSharedWorkerHost(existing_host);
542 pending_instance->SendWorkerCreatedMessages();
545 SharedWorkerMessageFilter* filter =
546 pending_instance->FindFilter(worker_process_id);
548 pending_instance->RemoveRequest(worker_process_id);
549 // Retry reserving a renderer process if the requested renderer process was
550 // destroyed on IO thread while reserving the renderer process on UI thread.
551 ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
554 scoped_ptr<SharedWorkerHost> host(new SharedWorkerHost(
555 pending_instance->release_instance(), filter, worker_route_id));
556 pending_instance->RegisterToSharedWorkerHost(host.get());
557 const GURL url = host->instance()->url();
558 const base::string16 name = host->instance()->name();
559 host->Start(pause_on_start);
560 worker_hosts_.set(std::make_pair(worker_process_id, worker_route_id),
563 WorkerServiceObserver,
565 WorkerCreated(url, name, worker_process_id, worker_route_id));
568 void SharedWorkerServiceImpl::RenderProcessReserveFailedCallback(
569 int pending_instance_id,
570 int worker_process_id,
572 bool is_new_worker) {
573 worker_hosts_.take_and_erase(
574 std::make_pair(worker_process_id, worker_route_id));
575 scoped_ptr<SharedWorkerPendingInstance> pending_instance =
576 pending_instances_.take_and_erase(pending_instance_id);
577 if (!pending_instance)
579 pending_instance->RemoveRequest(worker_process_id);
580 // Retry reserving a renderer process.
581 ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
584 SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
585 SharedWorkerMessageFilter* filter,
586 int worker_route_id) {
587 return worker_hosts_.get(std::make_pair(filter->render_process_id(),
591 SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
592 const SharedWorkerInstance& instance) {
593 for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
594 iter != worker_hosts_.end();
596 SharedWorkerHost* host = iter->second;
597 if (host->instance() && !host->closed() &&
598 host->instance()->Matches(instance)) {
605 SharedWorkerServiceImpl::SharedWorkerPendingInstance*
606 SharedWorkerServiceImpl::FindPendingInstance(
607 const SharedWorkerInstance& instance) {
608 for (PendingInstaneMap::iterator iter = pending_instances_.begin();
609 iter != pending_instances_.end();
611 if (iter->second->instance()->Matches(instance))
618 SharedWorkerServiceImpl::GetRenderersWithWorkerDependency() {
619 std::set<int> dependent_renderers;
620 for (WorkerHostMap::iterator host_iter = worker_hosts_.begin();
621 host_iter != worker_hosts_.end();
623 const int process_id = host_iter->first.first;
624 if (dependent_renderers.count(process_id))
626 if (host_iter->second->instance() &&
627 host_iter->second->worker_document_set()->ContainsExternalRenderer(
629 dependent_renderers.insert(process_id);
632 return dependent_renderers;
635 void SharedWorkerServiceImpl::CheckWorkerDependency() {
636 const std::set<int> current_worker_depended_renderers =
637 GetRenderersWithWorkerDependency();
638 std::vector<int> added_items = base::STLSetDifference<std::vector<int> >(
639 current_worker_depended_renderers, last_worker_depended_renderers_);
640 std::vector<int> removed_items = base::STLSetDifference<std::vector<int> >(
641 last_worker_depended_renderers_, current_worker_depended_renderers);
642 if (!added_items.empty() || !removed_items.empty()) {
643 last_worker_depended_renderers_ = current_worker_depended_renderers;
644 update_worker_dependency_(added_items, removed_items);
648 void SharedWorkerServiceImpl::ChangeUpdateWorkerDependencyFuncForTesting(
649 UpdateWorkerDependencyFunc new_func) {
650 update_worker_dependency_ = new_func;
653 void SharedWorkerServiceImpl::ChangeTryIncrementWorkerRefCountFuncForTesting(
654 bool (*new_func)(int)) {
655 s_try_increment_worker_ref_count_ = new_func;
658 } // namespace content