| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/shared_worker/shared_worker_host.h" |
| |
| #include <utility> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "content/browser/devtools/shared_worker_devtools_manager.h" |
| #include "content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h" |
| #include "content/browser/shared_worker/shared_worker_instance.h" |
| #include "content/browser/shared_worker/shared_worker_message_filter.h" |
| #include "content/browser/shared_worker/shared_worker_service_impl.h" |
| #include "content/common/view_messages.h" |
| #include "content/common/worker_messages.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/content_client.h" |
| #include "third_party/WebKit/public/web/worker_content_settings_proxy.mojom.h" |
| |
| namespace content { |
| namespace { |
| |
| void NotifyWorkerReadyForInspection(int worker_process_id, |
| int worker_route_id) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::BindOnce(NotifyWorkerReadyForInspection, |
| worker_process_id, worker_route_id)); |
| return; |
| } |
| SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection( |
| worker_process_id, worker_route_id); |
| } |
| |
| void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::BindOnce(NotifyWorkerDestroyed, |
| worker_process_id, worker_route_id)); |
| return; |
| } |
| SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed( |
| worker_process_id, worker_route_id); |
| } |
| |
| } // namespace |
| |
| SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance, |
| SharedWorkerMessageFilter* filter, |
| int worker_route_id) |
| : instance_(instance), |
| worker_document_set_(new WorkerDocumentSet()), |
| worker_render_filter_(filter), |
| worker_process_id_(filter->render_process_id()), |
| worker_route_id_(worker_route_id), |
| next_connection_request_id_(1), |
| creation_time_(base::TimeTicks::Now()), |
| weak_factory_(this) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(instance_); |
| DCHECK(worker_render_filter_); |
| } |
| |
| SharedWorkerHost::~SharedWorkerHost() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted", |
| base::TimeTicks::Now() - creation_time_); |
| if (!closed_ && !termination_message_sent_) |
| NotifyWorkerDestroyed(worker_process_id_, worker_route_id_); |
| SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( |
| worker_process_id_, worker_route_id_); |
| } |
| |
| void SharedWorkerHost::Start(bool pause_on_start) { |
| blink::mojom::WorkerContentSettingsProxyPtrInfo content_settings; |
| content_settings_ = base::MakeUnique<SharedWorkerContentSettingsProxyImpl>( |
| instance_->url(), this, mojo::MakeRequest(&content_settings)); |
| |
| WorkerProcessMsg_CreateWorker_Params params; |
| params.url = instance_->url(); |
| params.name = instance_->name(); |
| params.content_security_policy = instance_->content_security_policy(); |
| params.security_policy_type = instance_->security_policy_type(); |
| params.creation_address_space = instance_->creation_address_space(); |
| params.pause_on_start = pause_on_start; |
| params.route_id = worker_route_id_; |
| params.data_saver_enabled = instance_->data_saver_enabled(); |
| params.content_settings_handle = content_settings.PassHandle().release(); |
| Send(new WorkerProcessMsg_CreateWorker(params)); |
| |
| for (const FilterInfo& info : filters_) |
| info.filter()->Send(new ViewMsg_WorkerCreated(info.route_id())); |
| } |
| |
| bool SharedWorkerHost::SendConnectToWorker(int worker_route_id, |
| const MessagePort& port, |
| SharedWorkerMessageFilter* filter) { |
| if (!IsAvailable() || !HasFilter(filter, worker_route_id)) |
| return false; |
| |
| int connection_request_id = next_connection_request_id_++; |
| |
| SetConnectionRequestID(filter, worker_route_id, connection_request_id); |
| |
| // Send the connect message with the new connection_request_id. |
| Send(new WorkerMsg_Connect(worker_route_id_, connection_request_id, port)); |
| return true; |
| } |
| |
| void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) { |
| RemoveFilters(filter); |
| worker_document_set_->RemoveAll(filter); |
| if (worker_document_set_->IsEmpty()) { |
| // This worker has no more associated documents - shut it down. |
| TerminateWorker(); |
| } |
| } |
| |
| void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter, |
| unsigned long long document_id) { |
| // Walk all instances and remove the document from their document set. |
| worker_document_set_->Remove(filter, document_id); |
| if (worker_document_set_->IsEmpty()) { |
| // This worker has no more associated documents - shut it down. |
| TerminateWorker(); |
| } |
| } |
| |
| void SharedWorkerHost::RenderFrameDetached(int render_process_id, |
| int render_frame_id) { |
| // Walk all instances and remove all the documents in the frame from their |
| // document set. |
| worker_document_set_->RemoveRenderFrame(render_process_id, render_frame_id); |
| if (worker_document_set_->IsEmpty()) { |
| // This worker has no more associated documents - shut it down. |
| TerminateWorker(); |
| } |
| } |
| |
| void SharedWorkerHost::CountFeature(uint32_t feature) { |
| if (!used_features_.insert(feature).second) |
| return; |
| for (const auto& filter_info : filters_) { |
| filter_info.filter()->Send(new ViewMsg_CountFeatureOnSharedWorker( |
| filter_info.route_id(), feature)); |
| } |
| } |
| |
| void SharedWorkerHost::WorkerContextClosed() { |
| // Set the closed flag - this will stop any further messages from |
| // being sent to the worker (messages can still be sent from the worker, |
| // for exception reporting, etc). |
| closed_ = true; |
| if (!termination_message_sent_) |
| NotifyWorkerDestroyed(worker_process_id_, worker_route_id_); |
| } |
| |
| void SharedWorkerHost::WorkerContextDestroyed() { |
| for (const auto& filter_info : filters_) { |
| filter_info.filter()->Send( |
| new ViewMsg_WorkerDestroyed(filter_info.route_id())); |
| } |
| } |
| |
| void SharedWorkerHost::WorkerReadyForInspection() { |
| NotifyWorkerReadyForInspection(worker_process_id_, worker_route_id_); |
| } |
| |
| void SharedWorkerHost::WorkerScriptLoaded() { |
| UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded", |
| base::TimeTicks::Now() - creation_time_); |
| } |
| |
| void SharedWorkerHost::WorkerScriptLoadFailed() { |
| UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed", |
| base::TimeTicks::Now() - creation_time_); |
| for (const FilterInfo& info : filters_) |
| info.filter()->Send(new ViewMsg_WorkerScriptLoadFailed(info.route_id())); |
| } |
| |
| void SharedWorkerHost::WorkerConnected(int connection_request_id) { |
| if (!instance_) |
| return; |
| for (const FilterInfo& info : filters_) { |
| if (info.connection_request_id() != connection_request_id) |
| continue; |
| info.filter()->Send( |
| new ViewMsg_WorkerConnected(info.route_id(), used_features_)); |
| return; |
| } |
| } |
| |
| void SharedWorkerHost::AllowFileSystem( |
| const GURL& url, |
| base::OnceCallback<void(bool)> callback) { |
| GetContentClient()->browser()->AllowWorkerFileSystem( |
| url, instance_->resource_context(), GetRenderFrameIDsForWorker(), |
| base::Bind(&SharedWorkerHost::AllowFileSystemResponse, |
| weak_factory_.GetWeakPtr(), base::Passed(&callback))); |
| } |
| |
| void SharedWorkerHost::AllowFileSystemResponse( |
| base::OnceCallback<void(bool)> callback, |
| bool allowed) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::move(callback).Run(allowed); |
| } |
| |
| bool SharedWorkerHost::AllowIndexedDB(const GURL& url, |
| const base::string16& name) { |
| return GetContentClient()->browser()->AllowWorkerIndexedDB( |
| url, name, instance_->resource_context(), GetRenderFrameIDsForWorker()); |
| } |
| |
| void SharedWorkerHost::TerminateWorker() { |
| termination_message_sent_ = true; |
| if (!closed_) |
| NotifyWorkerDestroyed(worker_process_id_, worker_route_id_); |
| Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_)); |
| } |
| |
| std::vector<std::pair<int, int> > |
| SharedWorkerHost::GetRenderFrameIDsForWorker() { |
| std::vector<std::pair<int, int> > result; |
| const WorkerDocumentSet::DocumentInfoSet& documents = |
| worker_document_set_->documents(); |
| for (const WorkerDocumentSet::DocumentInfo& doc : documents) { |
| result.push_back( |
| std::make_pair(doc.render_process_id(), doc.render_frame_id())); |
| } |
| return result; |
| } |
| |
| bool SharedWorkerHost::IsAvailable() const { |
| return !termination_message_sent_ && !closed_; |
| } |
| |
| void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter, |
| int route_id) { |
| CHECK(filter); |
| if (!HasFilter(filter, route_id)) { |
| FilterInfo info(filter, route_id); |
| filters_.push_back(info); |
| } |
| } |
| |
| void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) { |
| for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { |
| if (i->filter() == filter) |
| i = filters_.erase(i); |
| else |
| ++i; |
| } |
| } |
| |
| bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* filter, |
| int route_id) const { |
| for (const FilterInfo& info : filters_) { |
| if (info.filter() == filter && info.route_id() == route_id) |
| return true; |
| } |
| return false; |
| } |
| |
| void SharedWorkerHost::SetConnectionRequestID(SharedWorkerMessageFilter* filter, |
| int route_id, |
| int connection_request_id) { |
| for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) { |
| if (i->filter() == filter && i->route_id() == route_id) { |
| i->set_connection_request_id(connection_request_id); |
| return; |
| } |
| } |
| } |
| |
| bool SharedWorkerHost::Send(IPC::Message* message) { |
| return worker_render_filter_->Send(message); |
| } |
| |
| } // namespace content |