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/shared_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_message_filter.h"
12 #include "content/browser/message_port_service.h"
13 #include "content/browser/shared_worker/shared_worker_instance.h"
14 #include "content/browser/shared_worker/shared_worker_message_filter.h"
15 #include "content/browser/shared_worker/shared_worker_service_impl.h"
16 #include "content/browser/shared_worker/worker_document_set.h"
17 #include "content/common/view_messages.h"
18 #include "content/common/worker_messages.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/common/content_client.h"
27 // Notifies RenderViewHost that one or more worker objects crashed.
28 void WorkerCrashCallback(int render_process_unique_id
, int render_frame_id
) {
29 RenderFrameHostImpl
* host
=
30 RenderFrameHostImpl::FromID(render_process_unique_id
, render_frame_id
);
32 host
->delegate()->WorkerCrashed(host
);
35 void NotifyWorkerReadyForInspection(int worker_process_id
,
36 int worker_route_id
) {
37 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
38 BrowserThread::PostTask(BrowserThread::UI
,
40 base::Bind(NotifyWorkerReadyForInspection
,
45 SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
46 worker_process_id
, worker_route_id
);
49 void NotifyWorkerDestroyed(int worker_process_id
, int worker_route_id
) {
50 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
51 BrowserThread::PostTask(
54 base::Bind(NotifyWorkerDestroyed
, worker_process_id
, worker_route_id
));
57 SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
58 worker_process_id
, worker_route_id
);
63 SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance
* instance
,
64 SharedWorkerMessageFilter
* filter
,
66 : instance_(instance
),
67 worker_document_set_(new WorkerDocumentSet()),
68 container_render_filter_(filter
),
69 worker_process_id_(filter
->render_process_id()),
70 worker_route_id_(worker_route_id
),
73 creation_time_(base::TimeTicks::Now()),
75 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
78 SharedWorkerHost::~SharedWorkerHost() {
79 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
80 UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
81 base::TimeTicks::Now() - creation_time_
);
82 // If we crashed, tell the RenderViewHosts.
83 if (instance_
&& !load_failed_
) {
84 const WorkerDocumentSet::DocumentInfoSet
& parents
=
85 worker_document_set_
->documents();
86 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter
=
88 parent_iter
!= parents
.end();
90 BrowserThread::PostTask(BrowserThread::UI
,
92 base::Bind(&WorkerCrashCallback
,
93 parent_iter
->render_process_id(),
94 parent_iter
->render_frame_id()));
98 NotifyWorkerDestroyed(worker_process_id_
, worker_route_id_
);
99 SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
100 worker_process_id_
, worker_route_id_
);
103 bool SharedWorkerHost::Send(IPC::Message
* message
) {
104 if (!container_render_filter_
) {
108 return container_render_filter_
->Send(message
);
111 void SharedWorkerHost::Start(bool pause_on_start
) {
112 WorkerProcessMsg_CreateWorker_Params params
;
113 params
.url
= instance_
->url();
114 params
.name
= instance_
->name();
115 params
.content_security_policy
= instance_
->content_security_policy();
116 params
.security_policy_type
= instance_
->security_policy_type();
117 params
.pause_on_start
= pause_on_start
;
118 params
.route_id
= worker_route_id_
;
119 Send(new WorkerProcessMsg_CreateWorker(params
));
121 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
123 i
->filter()->Send(new ViewMsg_WorkerCreated(i
->route_id()));
127 bool SharedWorkerHost::FilterMessage(const IPC::Message
& message
,
128 SharedWorkerMessageFilter
* filter
) {
132 if (!closed_
&& HasFilter(filter
, message
.routing_id())) {
133 RelayMessage(message
, filter
);
140 void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter
* filter
) {
143 RemoveFilters(filter
);
144 worker_document_set_
->RemoveAll(filter
);
145 if (worker_document_set_
->IsEmpty()) {
146 // This worker has no more associated documents - shut it down.
147 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_
));
151 void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter
* filter
,
152 unsigned long long document_id
) {
155 // Walk all instances and remove the document from their document set.
156 worker_document_set_
->Remove(filter
, document_id
);
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::WorkerContextClosed() {
166 // Set the closed flag - this will stop any further messages from
167 // being sent to the worker (messages can still be sent from the worker,
168 // for exception reporting, etc).
170 NotifyWorkerDestroyed(worker_process_id_
, worker_route_id_
);
173 void SharedWorkerHost::WorkerContextDestroyed() {
177 worker_document_set_
= NULL
;
180 void SharedWorkerHost::WorkerReadyForInspection() {
181 NotifyWorkerReadyForInspection(worker_process_id_
, worker_route_id_
);
184 void SharedWorkerHost::WorkerScriptLoaded() {
185 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
186 base::TimeTicks::Now() - creation_time_
);
189 void SharedWorkerHost::WorkerScriptLoadFailed() {
190 UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
191 base::TimeTicks::Now() - creation_time_
);
195 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
197 i
->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i
->route_id()));
201 void SharedWorkerHost::WorkerConnected(int message_port_id
) {
204 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
206 if (i
->message_port_id() != message_port_id
)
208 i
->filter()->Send(new ViewMsg_WorkerConnected(i
->route_id()));
213 void SharedWorkerHost::AllowDatabase(const GURL
& url
,
214 const base::string16
& name
,
215 const base::string16
& display_name
,
216 unsigned long estimated_size
,
220 *result
= GetContentClient()->browser()->AllowWorkerDatabase(
225 instance_
->resource_context(),
226 GetRenderFrameIDsForWorker());
229 void SharedWorkerHost::AllowFileSystem(const GURL
& url
,
230 scoped_ptr
<IPC::Message
> reply_msg
) {
233 GetContentClient()->browser()->AllowWorkerFileSystem(
235 instance_
->resource_context(),
236 GetRenderFrameIDsForWorker(),
237 base::Bind(&SharedWorkerHost::AllowFileSystemResponse
,
238 weak_factory_
.GetWeakPtr(),
239 base::Passed(&reply_msg
)));
242 void SharedWorkerHost::AllowFileSystemResponse(
243 scoped_ptr
<IPC::Message
> reply_msg
,
245 WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
248 Send(reply_msg
.release());
251 void SharedWorkerHost::AllowIndexedDB(const GURL
& url
,
252 const base::string16
& name
,
256 *result
= GetContentClient()->browser()->AllowWorkerIndexedDB(
257 url
, name
, instance_
->resource_context(), GetRenderFrameIDsForWorker());
260 void SharedWorkerHost::RelayMessage(
261 const IPC::Message
& message
,
262 SharedWorkerMessageFilter
* incoming_filter
) {
265 if (message
.type() == WorkerMsg_Connect::ID
) {
266 // Crack the SharedWorker Connect message to setup routing for the port.
267 WorkerMsg_Connect::Param param
;
268 if (!WorkerMsg_Connect::Read(&message
, ¶m
))
270 int sent_message_port_id
= base::get
<0>(param
);
271 int new_routing_id
= base::get
<1>(param
);
273 DCHECK(container_render_filter_
);
274 new_routing_id
= container_render_filter_
->GetNextRoutingID();
275 MessagePortService::GetInstance()->UpdateMessagePort(
276 sent_message_port_id
,
277 container_render_filter_
->message_port_message_filter(),
280 incoming_filter
, message
.routing_id(), sent_message_port_id
);
281 // Resend the message with the new routing id.
282 Send(new WorkerMsg_Connect(
283 worker_route_id_
, sent_message_port_id
, new_routing_id
));
285 // Send any queued messages for the sent port.
286 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
287 sent_message_port_id
);
289 IPC::Message
* new_message
= new IPC::Message(message
);
290 new_message
->set_routing_id(worker_route_id_
);
296 void SharedWorkerHost::TerminateWorker() {
297 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_
));
300 std::vector
<std::pair
<int, int> >
301 SharedWorkerHost::GetRenderFrameIDsForWorker() {
302 std::vector
<std::pair
<int, int> > result
;
305 const WorkerDocumentSet::DocumentInfoSet
& documents
=
306 worker_document_set_
->documents();
307 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc
=
309 doc
!= documents
.end();
312 std::make_pair(doc
->render_process_id(), doc
->render_frame_id()));
317 void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter
* filter
,
320 if (!HasFilter(filter
, route_id
)) {
321 FilterInfo
info(filter
, route_id
);
322 filters_
.push_back(info
);
326 void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter
* filter
) {
327 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end();) {
328 if (i
->filter() == filter
)
329 i
= filters_
.erase(i
);
335 bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter
* filter
,
336 int route_id
) const {
337 for (FilterList::const_iterator i
= filters_
.begin(); i
!= filters_
.end();
339 if (i
->filter() == filter
&& i
->route_id() == route_id
)
345 void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter
* filter
,
347 int message_port_id
) {
348 for (FilterList::iterator i
= filters_
.begin(); i
!= filters_
.end(); ++i
) {
349 if (i
->filter() == filter
&& i
->route_id() == route_id
) {
350 i
->set_message_port_id(message_port_id
);
356 } // namespace content