1 // Copyright 2013 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/service_worker/embedded_worker_instance.h"
10 #include "base/bind_helpers.h"
11 #include "base/debug/trace_event.h"
12 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
13 #include "content/browser/service_worker/embedded_worker_registry.h"
14 #include "content/browser/service_worker/service_worker_context_core.h"
15 #include "content/common/service_worker/embedded_worker_messages.h"
16 #include "content/common/service_worker/service_worker_types.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "ipc/ipc_message.h"
26 // Functor to sort by the .second element of a struct.
27 struct SecondGreater
{
28 template <typename Value
>
29 bool operator()(const Value
& lhs
, const Value
& rhs
) {
30 return lhs
.second
> rhs
.second
;
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 NotifyWorkerDestroyed(int worker_process_id
, int worker_route_id
) {
49 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
50 BrowserThread::PostTask(
53 base::Bind(NotifyWorkerDestroyed
, worker_process_id
, worker_route_id
));
56 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
57 worker_process_id
, worker_route_id
);
60 void RegisterToWorkerDevToolsManager(
62 const ServiceWorkerContextCore
* service_worker_context
,
63 base::WeakPtr
<ServiceWorkerContextCore
> service_worker_context_weak
,
64 int64 service_worker_version_id
,
66 const base::Callback
<void(int worker_devtools_agent_route_id
,
67 bool wait_for_debugger
)>& callback
) {
68 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
69 BrowserThread::PostTask(BrowserThread::UI
,
71 base::Bind(RegisterToWorkerDevToolsManager
,
73 service_worker_context
,
74 service_worker_context_weak
,
75 service_worker_version_id
,
80 int worker_devtools_agent_route_id
= MSG_ROUTING_NONE
;
81 bool wait_for_debugger
= false;
82 if (RenderProcessHost
* rph
= RenderProcessHost::FromID(process_id
)) {
83 // |rph| may be NULL in unit tests.
84 worker_devtools_agent_route_id
= rph
->GetNextRoutingID();
86 EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
88 worker_devtools_agent_route_id
,
89 EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
90 service_worker_context
,
91 service_worker_context_weak
,
92 service_worker_version_id
,
95 BrowserThread::PostTask(
98 base::Bind(callback
, worker_devtools_agent_route_id
, wait_for_debugger
));
103 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
104 if (status_
== STARTING
|| status_
== RUNNING
)
106 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
107 NotifyWorkerDestroyed(process_id_
, worker_devtools_agent_route_id_
);
108 if (context_
&& process_id_
!= -1)
109 context_
->process_manager()->ReleaseWorkerProcess(embedded_worker_id_
);
110 registry_
->RemoveWorker(process_id_
, embedded_worker_id_
);
113 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id
,
115 const GURL
& script_url
,
116 bool pause_after_download
,
117 const StatusCallback
& callback
) {
119 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
122 DCHECK(status_
== STOPPED
);
124 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params(
125 new EmbeddedWorkerMsg_StartWorker_Params());
126 TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
127 "EmbeddedWorkerInstance::ProcessAllocate",
129 "Scope", scope
.spec(),
130 "Script URL", script_url
.spec());
131 params
->embedded_worker_id
= embedded_worker_id_
;
132 params
->service_worker_version_id
= service_worker_version_id
;
133 params
->scope
= scope
;
134 params
->script_url
= script_url
;
135 params
->worker_devtools_agent_route_id
= MSG_ROUTING_NONE
;
136 params
->pause_after_download
= pause_after_download
;
137 params
->wait_for_debugger
= false;
138 context_
->process_manager()->AllocateWorkerProcess(
142 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated
,
143 weak_factory_
.GetWeakPtr(),
145 base::Passed(¶ms
),
149 ServiceWorkerStatusCode
EmbeddedWorkerInstance::Stop() {
150 DCHECK(status_
== STARTING
|| status_
== RUNNING
);
151 ServiceWorkerStatusCode status
=
152 registry_
->StopWorker(process_id_
, embedded_worker_id_
);
153 if (status
== SERVICE_WORKER_OK
)
158 void EmbeddedWorkerInstance::ResumeAfterDownload() {
159 DCHECK_EQ(STARTING
, status_
);
162 new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_
));
165 ServiceWorkerStatusCode
EmbeddedWorkerInstance::SendMessage(
166 const IPC::Message
& message
) {
167 DCHECK_NE(kInvalidEmbeddedWorkerThreadId
, thread_id_
);
168 if (status_
!= RUNNING
&& status_
!= STARTING
)
169 return SERVICE_WORKER_ERROR_IPC_FAILED
;
170 return registry_
->Send(process_id_
,
171 new EmbeddedWorkerContextMsg_MessageToWorker(
172 thread_id_
, embedded_worker_id_
, message
));
175 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
176 base::WeakPtr
<ServiceWorkerContextCore
> context
,
177 int embedded_worker_id
)
179 registry_(context
->embedded_worker_registry()),
180 embedded_worker_id_(embedded_worker_id
),
183 thread_id_(kInvalidEmbeddedWorkerThreadId
),
184 worker_devtools_agent_route_id_(MSG_ROUTING_NONE
),
185 weak_factory_(this) {
189 void EmbeddedWorkerInstance::RunProcessAllocated(
190 base::WeakPtr
<EmbeddedWorkerInstance
> instance
,
191 base::WeakPtr
<ServiceWorkerContextCore
> context
,
192 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
193 const EmbeddedWorkerInstance::StatusCallback
& callback
,
194 ServiceWorkerStatusCode status
,
197 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
201 if (status
== SERVICE_WORKER_OK
) {
202 // We only have a process allocated if the status is OK.
203 context
->process_manager()->ReleaseWorkerProcess(
204 params
->embedded_worker_id
);
206 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
209 instance
->ProcessAllocated(params
.Pass(), callback
, process_id
, status
);
212 void EmbeddedWorkerInstance::ProcessAllocated(
213 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
214 const StatusCallback
& callback
,
216 ServiceWorkerStatusCode status
) {
217 DCHECK_EQ(process_id_
, -1);
218 TRACE_EVENT_ASYNC_END1("ServiceWorker",
219 "EmbeddedWorkerInstance::ProcessAllocate",
222 if (status
!= SERVICE_WORKER_OK
) {
224 callback
.Run(status
);
227 const int64 service_worker_version_id
= params
->service_worker_version_id
;
228 process_id_
= process_id
;
229 GURL
script_url(params
->script_url
);
230 RegisterToWorkerDevToolsManager(
234 service_worker_version_id
,
236 base::Bind(&EmbeddedWorkerInstance::SendStartWorker
,
237 weak_factory_
.GetWeakPtr(),
238 base::Passed(¶ms
),
242 void EmbeddedWorkerInstance::SendStartWorker(
243 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
244 const StatusCallback
& callback
,
245 int worker_devtools_agent_route_id
,
246 bool wait_for_debugger
) {
247 worker_devtools_agent_route_id_
= worker_devtools_agent_route_id
;
248 params
->worker_devtools_agent_route_id
= worker_devtools_agent_route_id
;
249 params
->wait_for_debugger
= wait_for_debugger
;
250 ServiceWorkerStatusCode status
=
251 registry_
->SendStartWorker(params
.Pass(), process_id_
);
252 if (status
!= SERVICE_WORKER_OK
) {
253 callback
.Run(status
);
256 DCHECK(start_callback_
.is_null());
257 start_callback_
= callback
;
260 void EmbeddedWorkerInstance::OnReadyForInspection() {
261 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
262 NotifyWorkerReadyForInspection(process_id_
,
263 worker_devtools_agent_route_id_
);
266 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id
) {
267 thread_id_
= thread_id
;
270 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
273 void EmbeddedWorkerInstance::OnScriptEvaluated(bool success
) {
274 DCHECK(!start_callback_
.is_null());
275 start_callback_
.Run(success
? SERVICE_WORKER_OK
276 : SERVICE_WORKER_ERROR_START_WORKER_FAILED
);
277 start_callback_
.Reset();
280 void EmbeddedWorkerInstance::OnStarted() {
281 // Stop is requested before OnStarted is sent back from the worker.
282 if (status_
== STOPPING
)
284 DCHECK(status_
== STARTING
);
286 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnStarted());
289 void EmbeddedWorkerInstance::OnStopped() {
290 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
291 NotifyWorkerDestroyed(process_id_
, worker_devtools_agent_route_id_
);
293 context_
->process_manager()->ReleaseWorkerProcess(embedded_worker_id_
);
297 worker_devtools_agent_route_id_
= MSG_ROUTING_NONE
;
298 start_callback_
.Reset();
299 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnStopped());
302 void EmbeddedWorkerInstance::OnPausedAfterDownload() {
303 // Stop can be requested before getting this far.
304 if (status_
== STOPPING
)
306 DCHECK(status_
== STARTING
);
307 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnPausedAfterDownload());
310 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message
& message
) {
311 ListenerList::Iterator
it(listener_list_
);
312 while (Listener
* listener
= it
.GetNext()) {
313 if (listener
->OnMessageReceived(message
))
319 void EmbeddedWorkerInstance::OnReportException(
320 const base::string16
& error_message
,
323 const GURL
& source_url
) {
327 OnReportException(error_message
, line_number
, column_number
, source_url
));
330 void EmbeddedWorkerInstance::OnReportConsoleMessage(
331 int source_identifier
,
333 const base::string16
& message
,
335 const GURL
& source_url
) {
339 OnReportConsoleMessage(
340 source_identifier
, message_level
, message
, line_number
, source_url
));
343 MessagePortMessageFilter
* EmbeddedWorkerInstance::message_port_message_filter()
345 return registry_
->MessagePortMessageFilterForProcess(process_id_
);
348 void EmbeddedWorkerInstance::AddListener(Listener
* listener
) {
349 listener_list_
.AddObserver(listener
);
352 void EmbeddedWorkerInstance::RemoveListener(Listener
* listener
) {
353 listener_list_
.RemoveObserver(listener
);
356 } // namespace content