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_host.h"
7 #include "base/metrics/histogram.h"
8 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
9 #include "content/browser/frame_host/render_frame_host_delegate.h"
10 #include "content/browser/frame_host/render_frame_host_impl.h"
11 #include "content/browser/message_port_service.h"
12 #include "content/browser/shared_worker/shared_worker_instance.h"
13 #include "content/browser/shared_worker/shared_worker_message_filter.h"
14 #include "content/browser/shared_worker/shared_worker_service_impl.h"
15 #include "content/browser/shared_worker/worker_document_set.h"
16 #include "content/common/view_messages.h"
17 #include "content/common/worker_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/common/content_client.h"
26 // Notifies RenderViewHost that one or more worker objects crashed.
27 void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
28 RenderFrameHostImpl* host =
29 RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
31 host->delegate()->WorkerCrashed(host);
34 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
35 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
36 BrowserThread::PostTask(
40 NotifyWorkerContextStarted, worker_process_id, worker_route_id));
43 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
44 worker_process_id, worker_route_id);
47 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
48 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
49 BrowserThread::PostTask(
52 base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
55 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
56 worker_process_id, worker_route_id);
61 SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
62 SharedWorkerMessageFilter* filter,
64 : instance_(instance),
65 worker_document_set_(new WorkerDocumentSet()),
67 container_render_filter_(filter),
68 worker_process_id_(filter->render_process_id()),
69 worker_route_id_(worker_route_id),
72 creation_time_(base::TimeTicks::Now()) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
76 SharedWorkerHost::~SharedWorkerHost() {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
78 UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
79 base::TimeTicks::Now() - creation_time_);
80 // If we crashed, tell the RenderViewHosts.
81 if (instance_ && !load_failed_) {
82 const WorkerDocumentSet::DocumentInfoSet& parents =
83 worker_document_set_->documents();
84 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
86 parent_iter != parents.end();
88 BrowserThread::PostTask(BrowserThread::UI,
90 base::Bind(&WorkerCrashCallback,
91 parent_iter->render_process_id(),
92 parent_iter->render_frame_id()));
96 NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
97 SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
98 worker_process_id_, worker_route_id_);
101 bool SharedWorkerHost::Send(IPC::Message* message) {
102 if (!container_render_filter_) {
106 return container_render_filter_->Send(message);
109 void SharedWorkerHost::Start(bool pause_on_start) {
110 WorkerProcessMsg_CreateWorker_Params params;
111 params.url = instance_->url();
112 params.name = instance_->name();
113 params.content_security_policy = instance_->content_security_policy();
114 params.security_policy_type = instance_->security_policy_type();
115 params.pause_on_start = pause_on_start;
116 params.route_id = worker_route_id_;
117 Send(new WorkerProcessMsg_CreateWorker(params));
119 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
121 i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
125 bool SharedWorkerHost::FilterMessage(const IPC::Message& message,
126 SharedWorkerMessageFilter* filter) {
130 if (!closed_ && HasFilter(filter, message.routing_id())) {
131 RelayMessage(message, filter);
138 void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) {
141 RemoveFilters(filter);
142 worker_document_set_->RemoveAll(filter);
143 if (worker_document_set_->IsEmpty()) {
144 // This worker has no more associated documents - shut it down.
145 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
149 void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter,
150 unsigned long long document_id) {
153 // Walk all instances and remove the document from their document set.
154 worker_document_set_->Remove(filter, document_id);
155 if (worker_document_set_->IsEmpty()) {
156 // This worker has no more associated documents - shut it down.
157 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
161 void SharedWorkerHost::WorkerContextClosed() {
164 // Set the closed flag - this will stop any further messages from
165 // being sent to the worker (messages can still be sent from the worker,
166 // for exception reporting, etc).
168 NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
171 void SharedWorkerHost::WorkerContextDestroyed() {
175 worker_document_set_ = NULL;
178 void SharedWorkerHost::WorkerScriptLoaded() {
179 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
180 base::TimeTicks::Now() - creation_time_);
181 NotifyWorkerContextStarted(worker_process_id_, worker_route_id_);
184 void SharedWorkerHost::WorkerScriptLoadFailed() {
185 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
186 base::TimeTicks::Now() - creation_time_);
190 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
192 i->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i->route_id()));
196 void SharedWorkerHost::WorkerConnected(int message_port_id) {
199 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
201 if (i->message_port_id() != message_port_id)
203 i->filter()->Send(new ViewMsg_WorkerConnected(i->route_id()));
208 void SharedWorkerHost::AllowDatabase(const GURL& url,
209 const base::string16& name,
210 const base::string16& display_name,
211 unsigned long estimated_size,
215 *result = GetContentClient()->browser()->AllowWorkerDatabase(
220 instance_->resource_context(),
221 GetRenderFrameIDsForWorker());
224 void SharedWorkerHost::AllowFileSystem(const GURL& url,
225 scoped_ptr<IPC::Message> reply_msg) {
228 GetContentClient()->browser()->AllowWorkerFileSystem(
230 instance_->resource_context(),
231 GetRenderFrameIDsForWorker(),
232 base::Bind(&SharedWorkerHost::AllowFileSystemResponse,
233 weak_factory_.GetWeakPtr(),
234 base::Passed(&reply_msg)));
237 void SharedWorkerHost::AllowFileSystemResponse(
238 scoped_ptr<IPC::Message> reply_msg,
240 WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
243 Send(reply_msg.release());
246 void SharedWorkerHost::AllowIndexedDB(const GURL& url,
247 const base::string16& name,
251 *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
252 url, name, instance_->resource_context(), GetRenderFrameIDsForWorker());
255 void SharedWorkerHost::RelayMessage(
256 const IPC::Message& message,
257 SharedWorkerMessageFilter* incoming_filter) {
260 if (message.type() == WorkerMsg_Connect::ID) {
261 // Crack the SharedWorker Connect message to setup routing for the port.
262 WorkerMsg_Connect::Param param;
263 if (!WorkerMsg_Connect::Read(&message, ¶m))
265 int sent_message_port_id = param.a;
266 int new_routing_id = param.b;
268 DCHECK(container_render_filter_);
269 new_routing_id = container_render_filter_->GetNextRoutingID();
270 MessagePortService::GetInstance()->UpdateMessagePort(
271 sent_message_port_id,
272 container_render_filter_->message_port_message_filter(),
275 incoming_filter, message.routing_id(), sent_message_port_id);
276 // Resend the message with the new routing id.
277 Send(new WorkerMsg_Connect(
278 worker_route_id_, sent_message_port_id, new_routing_id));
280 // Send any queued messages for the sent port.
281 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
282 sent_message_port_id);
284 IPC::Message* new_message = new IPC::Message(message);
285 new_message->set_routing_id(worker_route_id_);
291 void SharedWorkerHost::TerminateWorker() {
292 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
295 std::vector<std::pair<int, int> >
296 SharedWorkerHost::GetRenderFrameIDsForWorker() {
297 std::vector<std::pair<int, int> > result;
300 const WorkerDocumentSet::DocumentInfoSet& documents =
301 worker_document_set_->documents();
302 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
304 doc != documents.end();
307 std::make_pair(doc->render_process_id(), doc->render_frame_id()));
312 void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter,
315 if (!HasFilter(filter, route_id)) {
316 FilterInfo info(filter, route_id);
317 filters_.push_back(info);
321 void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) {
322 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
323 if (i->filter() == filter)
324 i = filters_.erase(i);
330 bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* filter,
331 int route_id) const {
332 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
334 if (i->filter() == filter && i->route_id() == route_id)
340 void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter* filter,
342 int message_port_id) {
343 for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
344 if (i->filter() == filter && i->route_id() == route_id) {
345 i->set_message_port_id(message_port_id);
351 } // namespace content