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 "content/browser/devtools/embedded_worker_devtools_manager.h"
12 #include "content/browser/service_worker/embedded_worker_registry.h"
13 #include "content/browser/service_worker/service_worker_context_core.h"
14 #include "content/common/service_worker/embedded_worker_messages.h"
15 #include "content/common/service_worker/service_worker_types.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "ipc/ipc_message.h"
25 // Functor to sort by the .second element of a struct.
26 struct SecondGreater
{
27 template <typename Value
>
28 bool operator()(const Value
& lhs
, const Value
& rhs
) {
29 return lhs
.second
> rhs
.second
;
33 void NotifyWorkerReadyForInspection(int worker_process_id
,
34 int worker_route_id
) {
35 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
36 BrowserThread::PostTask(BrowserThread::UI
,
38 base::Bind(NotifyWorkerReadyForInspection
,
43 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
44 worker_process_id
, worker_route_id
);
47 void NotifyWorkerContextStarted(int worker_process_id
, int worker_route_id
) {
48 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
49 BrowserThread::PostTask(
53 NotifyWorkerContextStarted
, worker_process_id
, worker_route_id
));
56 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
57 worker_process_id
, worker_route_id
);
60 void NotifyWorkerDestroyed(int worker_process_id
, int worker_route_id
) {
61 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
62 BrowserThread::PostTask(
65 base::Bind(NotifyWorkerDestroyed
, worker_process_id
, worker_route_id
));
68 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
69 worker_process_id
, worker_route_id
);
72 void RegisterToWorkerDevToolsManager(
74 const ServiceWorkerContextCore
* service_worker_context
,
75 base::WeakPtr
<ServiceWorkerContextCore
> service_worker_context_weak
,
76 int64 service_worker_version_id
,
78 const base::Callback
<void(int worker_devtools_agent_route_id
,
79 bool wait_for_debugger
)>& callback
) {
80 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
81 BrowserThread::PostTask(BrowserThread::UI
,
83 base::Bind(RegisterToWorkerDevToolsManager
,
85 service_worker_context
,
86 service_worker_context_weak
,
87 service_worker_version_id
,
92 int worker_devtools_agent_route_id
= MSG_ROUTING_NONE
;
93 bool wait_for_debugger
= false;
94 if (RenderProcessHost
* rph
= RenderProcessHost::FromID(process_id
)) {
95 // |rph| may be NULL in unit tests.
96 worker_devtools_agent_route_id
= rph
->GetNextRoutingID();
98 EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
100 worker_devtools_agent_route_id
,
101 EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
102 service_worker_context
,
103 service_worker_context_weak
,
104 service_worker_version_id
,
107 BrowserThread::PostTask(
110 base::Bind(callback
, worker_devtools_agent_route_id
, wait_for_debugger
));
115 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
116 if (status_
== STARTING
|| status_
== RUNNING
)
118 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
119 NotifyWorkerDestroyed(process_id_
, worker_devtools_agent_route_id_
);
120 if (context_
&& process_id_
!= -1)
121 context_
->process_manager()->ReleaseWorkerProcess(embedded_worker_id_
);
122 registry_
->RemoveWorker(process_id_
, embedded_worker_id_
);
125 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id
,
127 const GURL
& script_url
,
128 bool pause_after_download
,
129 const std::vector
<int>& possible_process_ids
,
130 const StatusCallback
& callback
) {
132 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
135 DCHECK(status_
== STOPPED
);
137 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params(
138 new EmbeddedWorkerMsg_StartWorker_Params());
139 params
->embedded_worker_id
= embedded_worker_id_
;
140 params
->service_worker_version_id
= service_worker_version_id
;
141 params
->scope
= scope
;
142 params
->script_url
= script_url
;
143 params
->worker_devtools_agent_route_id
= MSG_ROUTING_NONE
;
144 params
->pause_after_download
= pause_after_download
;
145 params
->wait_for_debugger
= false;
146 context_
->process_manager()->AllocateWorkerProcess(
148 SortProcesses(possible_process_ids
),
150 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated
,
151 weak_factory_
.GetWeakPtr(),
153 base::Passed(¶ms
),
157 ServiceWorkerStatusCode
EmbeddedWorkerInstance::Stop() {
158 DCHECK(status_
== STARTING
|| status_
== RUNNING
);
159 ServiceWorkerStatusCode status
=
160 registry_
->StopWorker(process_id_
, embedded_worker_id_
);
161 if (status
== SERVICE_WORKER_OK
)
166 void EmbeddedWorkerInstance::ResumeAfterDownload() {
167 DCHECK_EQ(STARTING
, status_
);
170 new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_
));
173 ServiceWorkerStatusCode
EmbeddedWorkerInstance::SendMessage(
174 const IPC::Message
& message
) {
175 DCHECK_NE(kInvalidEmbeddedWorkerThreadId
, thread_id_
);
176 if (status_
!= RUNNING
&& status_
!= STARTING
)
177 return SERVICE_WORKER_ERROR_IPC_FAILED
;
178 return registry_
->Send(process_id_
,
179 new EmbeddedWorkerContextMsg_MessageToWorker(
180 thread_id_
, embedded_worker_id_
, message
));
183 void EmbeddedWorkerInstance::AddProcessReference(int process_id
) {
184 ProcessRefMap::iterator found
= process_refs_
.find(process_id
);
185 if (found
== process_refs_
.end())
186 found
= process_refs_
.insert(std::make_pair(process_id
, 0)).first
;
190 void EmbeddedWorkerInstance::ReleaseProcessReference(int process_id
) {
191 ProcessRefMap::iterator found
= process_refs_
.find(process_id
);
192 if (found
== process_refs_
.end()) {
193 NOTREACHED() << "Releasing unknown process ref " << process_id
;
196 if (--found
->second
== 0)
197 process_refs_
.erase(found
);
200 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
201 base::WeakPtr
<ServiceWorkerContextCore
> context
,
202 int embedded_worker_id
)
204 registry_(context
->embedded_worker_registry()),
205 embedded_worker_id_(embedded_worker_id
),
208 thread_id_(kInvalidEmbeddedWorkerThreadId
),
209 worker_devtools_agent_route_id_(MSG_ROUTING_NONE
),
210 weak_factory_(this) {
214 void EmbeddedWorkerInstance::RunProcessAllocated(
215 base::WeakPtr
<EmbeddedWorkerInstance
> instance
,
216 base::WeakPtr
<ServiceWorkerContextCore
> context
,
217 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
218 const EmbeddedWorkerInstance::StatusCallback
& callback
,
219 ServiceWorkerStatusCode status
,
222 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
226 if (status
== SERVICE_WORKER_OK
) {
227 // We only have a process allocated if the status is OK.
228 context
->process_manager()->ReleaseWorkerProcess(
229 params
->embedded_worker_id
);
231 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
234 instance
->ProcessAllocated(params
.Pass(), callback
, process_id
, status
);
237 void EmbeddedWorkerInstance::ProcessAllocated(
238 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
239 const StatusCallback
& callback
,
241 ServiceWorkerStatusCode status
) {
242 DCHECK_EQ(process_id_
, -1);
243 if (status
!= SERVICE_WORKER_OK
) {
245 callback
.Run(status
);
248 const int64 service_worker_version_id
= params
->service_worker_version_id
;
249 process_id_
= process_id
;
250 GURL
script_url(params
->script_url
);
251 RegisterToWorkerDevToolsManager(
255 service_worker_version_id
,
257 base::Bind(&EmbeddedWorkerInstance::SendStartWorker
,
258 weak_factory_
.GetWeakPtr(),
259 base::Passed(¶ms
),
263 void EmbeddedWorkerInstance::SendStartWorker(
264 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params
,
265 const StatusCallback
& callback
,
266 int worker_devtools_agent_route_id
,
267 bool wait_for_debugger
) {
268 worker_devtools_agent_route_id_
= worker_devtools_agent_route_id
;
269 params
->worker_devtools_agent_route_id
= worker_devtools_agent_route_id
;
270 params
->wait_for_debugger
= wait_for_debugger
;
271 registry_
->SendStartWorker(params
.Pass(), callback
, process_id_
);
274 void EmbeddedWorkerInstance::OnReadyForInspection() {
275 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
276 NotifyWorkerReadyForInspection(process_id_
,
277 worker_devtools_agent_route_id_
);
280 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id
) {
281 thread_id_
= thread_id
;
282 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
283 NotifyWorkerContextStarted(process_id_
, worker_devtools_agent_route_id_
);
286 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
289 void EmbeddedWorkerInstance::OnStarted() {
290 // Stop is requested before OnStarted is sent back from the worker.
291 if (status_
== STOPPING
)
293 DCHECK(status_
== STARTING
);
295 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnStarted());
298 void EmbeddedWorkerInstance::OnStopped() {
299 if (worker_devtools_agent_route_id_
!= MSG_ROUTING_NONE
)
300 NotifyWorkerDestroyed(process_id_
, worker_devtools_agent_route_id_
);
302 context_
->process_manager()->ReleaseWorkerProcess(embedded_worker_id_
);
306 worker_devtools_agent_route_id_
= MSG_ROUTING_NONE
;
307 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnStopped());
310 void EmbeddedWorkerInstance::OnPausedAfterDownload() {
311 // Stop can be requested before getting this far.
312 if (status_
== STOPPING
)
314 DCHECK(status_
== STARTING
);
315 FOR_EACH_OBSERVER(Listener
, listener_list_
, OnPausedAfterDownload());
318 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message
& message
) {
319 ListenerList::Iterator
it(listener_list_
);
320 while (Listener
* listener
= it
.GetNext()) {
321 if (listener
->OnMessageReceived(message
))
327 void EmbeddedWorkerInstance::OnReportException(
328 const base::string16
& error_message
,
331 const GURL
& source_url
) {
335 OnReportException(error_message
, line_number
, column_number
, source_url
));
338 void EmbeddedWorkerInstance::OnReportConsoleMessage(
339 int source_identifier
,
341 const base::string16
& message
,
343 const GURL
& source_url
) {
347 OnReportConsoleMessage(
348 source_identifier
, message_level
, message
, line_number
, source_url
));
351 void EmbeddedWorkerInstance::AddListener(Listener
* listener
) {
352 listener_list_
.AddObserver(listener
);
355 void EmbeddedWorkerInstance::RemoveListener(Listener
* listener
) {
356 listener_list_
.RemoveObserver(listener
);
359 std::vector
<int> EmbeddedWorkerInstance::SortProcesses(
360 const std::vector
<int>& possible_process_ids
) const {
361 // Add the |possible_process_ids| to the existing process_refs_ since each one
362 // is likely to take a reference once the SW starts up.
363 ProcessRefMap refs_with_new_ids
= process_refs_
;
364 for (std::vector
<int>::const_iterator it
= possible_process_ids
.begin();
365 it
!= possible_process_ids
.end();
367 refs_with_new_ids
[*it
]++;
370 std::vector
<std::pair
<int, int> > counted(refs_with_new_ids
.begin(),
371 refs_with_new_ids
.end());
372 // Sort descending by the reference count.
373 std::sort(counted
.begin(), counted
.end(), SecondGreater());
375 std::vector
<int> result(counted
.size());
376 for (size_t i
= 0; i
< counted
.size(); ++i
)
377 result
[i
] = counted
[i
].first
;
381 } // namespace content