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 NotifyWorkerReadyForInspection(int worker_process_id
,
35 int worker_route_id
) {
36 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
37 BrowserThread::PostTask(BrowserThread::UI
,
39 base::Bind(NotifyWorkerReadyForInspection
,
44 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
45 worker_process_id
, worker_route_id
);
48 void NotifyWorkerContextStarted(int worker_process_id
, int worker_route_id
) {
49 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
50 BrowserThread::PostTask(
54 NotifyWorkerContextStarted
, worker_process_id
, worker_route_id
));
57 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
58 worker_process_id
, worker_route_id
);
61 void NotifyWorkerDestroyed(int worker_process_id
, int worker_route_id
) {
62 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
63 BrowserThread::PostTask(
66 base::Bind(NotifyWorkerDestroyed
, worker_process_id
, worker_route_id
));
69 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
70 worker_process_id
, worker_route_id
);
75 SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance
* instance
,
76 SharedWorkerMessageFilter
* filter
,
78 : instance_(instance
),
79 worker_document_set_(new WorkerDocumentSet()),
81 container_render_filter_(filter
),
82 worker_process_id_(filter
->render_process_id()),
83 worker_route_id_(worker_route_id
),
86 creation_time_(base::TimeTicks::Now()) {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
90 SharedWorkerHost::~SharedWorkerHost() {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
92 UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
93 base::TimeTicks::Now() - creation_time_
);
94 // If we crashed, tell the RenderViewHosts.
95 if (instance_
&& !load_failed_
) {
96 const WorkerDocumentSet::DocumentInfoSet
& parents
=
97 worker_document_set_
->documents();
98 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter
=
100 parent_iter
!= parents
.end();
102 BrowserThread::PostTask(BrowserThread::UI
,
104 base::Bind(&WorkerCrashCallback
,
105 parent_iter
->render_process_id(),
106 parent_iter
->render_frame_id()));
110 NotifyWorkerDestroyed(worker_process_id_
, worker_route_id_
);
111 SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
112 worker_process_id_
, worker_route_id_
);
115 bool SharedWorkerHost::Send(IPC::Message
* message
) {
116 if (!container_render_filter_
) {
120 return container_render_filter_
->Send(message
);
123 void SharedWorkerHost::Start(bool pause_on_start
) {
124 WorkerProcessMsg_CreateWorker_Params params
;
125 params
.url
= instance_
->url();
126 params
.name
= instance_
->name();
127 params
.content_security_policy
= instance_
->content_security_policy();
128 params
.security_policy_type
= instance_
->security_policy_type();
129 params
.pause_on_start
= pause_on_start
;
130 params
.route_id
= worker_route_id_
;
131 Send(new WorkerProcessMsg_CreateWorker(params
));
133 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
135 i
->filter()->Send(new ViewMsg_WorkerCreated(i
->route_id()));
139 bool SharedWorkerHost::FilterMessage(const IPC::Message
& message
,
140 SharedWorkerMessageFilter
* filter
) {
144 if (!closed_
&& HasFilter(filter
, message
.routing_id())) {
145 RelayMessage(message
, filter
);
152 void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter
* filter
) {
155 RemoveFilters(filter
);
156 worker_document_set_
->RemoveAll(filter
);
157 if (worker_document_set_
->IsEmpty()) {
158 // This worker has no more associated documents - shut it down.
159 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_
));
163 void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter
* filter
,
164 unsigned long long document_id
) {
167 // Walk all instances and remove the document from their document set.
168 worker_document_set_
->Remove(filter
, document_id
);
169 if (worker_document_set_
->IsEmpty()) {
170 // This worker has no more associated documents - shut it down.
171 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_
));
175 void SharedWorkerHost::WorkerContextClosed() {
178 // Set the closed flag - this will stop any further messages from
179 // being sent to the worker (messages can still be sent from the worker,
180 // for exception reporting, etc).
182 NotifyWorkerDestroyed(worker_process_id_
, worker_route_id_
);
185 void SharedWorkerHost::WorkerContextDestroyed() {
189 worker_document_set_
= NULL
;
192 void SharedWorkerHost::WorkerReadyForInspection() {
193 NotifyWorkerReadyForInspection(worker_process_id_
, worker_route_id_
);
196 void SharedWorkerHost::WorkerScriptLoaded() {
197 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
198 base::TimeTicks::Now() - creation_time_
);
199 NotifyWorkerContextStarted(worker_process_id_
, worker_route_id_
);
202 void SharedWorkerHost::WorkerScriptLoadFailed() {
203 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
204 base::TimeTicks::Now() - creation_time_
);
208 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
210 i
->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i
->route_id()));
214 void SharedWorkerHost::WorkerConnected(int message_port_id
) {
217 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
219 if (i
->message_port_id() != message_port_id
)
221 i
->filter()->Send(new ViewMsg_WorkerConnected(i
->route_id()));
226 void SharedWorkerHost::AllowDatabase(const GURL
& url
,
227 const base::string16
& name
,
228 const base::string16
& display_name
,
229 unsigned long estimated_size
,
233 *result
= GetContentClient()->browser()->AllowWorkerDatabase(
238 instance_
->resource_context(),
239 GetRenderFrameIDsForWorker());
242 void SharedWorkerHost::AllowFileSystem(const GURL
& url
,
243 scoped_ptr
<IPC::Message
> reply_msg
) {
246 GetContentClient()->browser()->AllowWorkerFileSystem(
248 instance_
->resource_context(),
249 GetRenderFrameIDsForWorker(),
250 base::Bind(&SharedWorkerHost::AllowFileSystemResponse
,
251 weak_factory_
.GetWeakPtr(),
252 base::Passed(&reply_msg
)));
255 void SharedWorkerHost::AllowFileSystemResponse(
256 scoped_ptr
<IPC::Message
> reply_msg
,
258 WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
261 Send(reply_msg
.release());
264 void SharedWorkerHost::AllowIndexedDB(const GURL
& url
,
265 const base::string16
& name
,
269 *result
= GetContentClient()->browser()->AllowWorkerIndexedDB(
270 url
, name
, instance_
->resource_context(), GetRenderFrameIDsForWorker());
273 void SharedWorkerHost::RelayMessage(
274 const IPC::Message
& message
,
275 SharedWorkerMessageFilter
* incoming_filter
) {
278 if (message
.type() == WorkerMsg_Connect::ID
) {
279 // Crack the SharedWorker Connect message to setup routing for the port.
280 WorkerMsg_Connect::Param param
;
281 if (!WorkerMsg_Connect::Read(&message
, ¶m
))
283 int sent_message_port_id
= param
.a
;
284 int new_routing_id
= param
.b
;
286 DCHECK(container_render_filter_
);
287 new_routing_id
= container_render_filter_
->GetNextRoutingID();
288 MessagePortService::GetInstance()->UpdateMessagePort(
289 sent_message_port_id
,
290 container_render_filter_
->message_port_message_filter(),
293 incoming_filter
, message
.routing_id(), sent_message_port_id
);
294 // Resend the message with the new routing id.
295 Send(new WorkerMsg_Connect(
296 worker_route_id_
, sent_message_port_id
, new_routing_id
));
298 // Send any queued messages for the sent port.
299 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
300 sent_message_port_id
);
302 IPC::Message
* new_message
= new IPC::Message(message
);
303 new_message
->set_routing_id(worker_route_id_
);
309 void SharedWorkerHost::TerminateWorker() {
310 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_
));
313 std::vector
<std::pair
<int, int> >
314 SharedWorkerHost::GetRenderFrameIDsForWorker() {
315 std::vector
<std::pair
<int, int> > result
;
318 const WorkerDocumentSet::DocumentInfoSet
& documents
=
319 worker_document_set_
->documents();
320 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc
=
322 doc
!= documents
.end();
325 std::make_pair(doc
->render_process_id(), doc
->render_frame_id()));
330 void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter
* filter
,
333 if (!HasFilter(filter
, route_id
)) {
334 FilterInfo
info(filter
, route_id
);
335 filters_
.push_back(info
);
339 void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter
* filter
) {
340 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end();) {
341 if (i
->filter() == filter
)
342 i
= filters_
.erase(i
);
348 bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter
* filter
,
349 int route_id
) const {
350 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
352 if (i
->filter() == filter
&& i
->route_id() == route_id
)
358 void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter
* filter
,
360 int message_port_id
) {
361 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end(); ++i
) {
362 if (i
->filter() == filter
&& i
->route_id() == route_id
) {
363 i
->set_message_port_id(message_port_id
);
369 } // namespace content