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.
4
5 #include "content/browser/service_worker/embedded_worker_instance.h"
6
7 #include <algorithm>
8 #include <utility>
9
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"
20 #include "url/gurl.h"
21
22 namespace content {
23
24 namespace {
25
26 // Functor to sort by the .second element of a struct.
27 struct SecondGreater {
28 template <typename Value>
operator ()content::__anon24ec7f830111::SecondGreater29 bool operator()(const Value& lhs, const Value& rhs) {
30 return lhs.second > rhs.second;
31 }
32 };
33
NotifyWorkerReadyForInspection(int worker_process_id,int worker_route_id)34 void NotifyWorkerReadyForInspection(int worker_process_id,
35 int worker_route_id) {
36 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
37 BrowserThread::PostTask(BrowserThread::UI,
38 FROM_HERE,
39 base::Bind(NotifyWorkerReadyForInspection,
40 worker_process_id,
41 worker_route_id));
42 return;
43 }
44 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
45 worker_process_id, worker_route_id);
46 }
47
NotifyWorkerContextStarted(int worker_process_id,int worker_route_id)48 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
49 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
50 BrowserThread::PostTask(
51 BrowserThread::UI,
52 FROM_HERE,
53 base::Bind(
54 NotifyWorkerContextStarted, worker_process_id, worker_route_id));
55 return;
56 }
57 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
58 worker_process_id, worker_route_id);
59 }
60
NotifyWorkerDestroyed(int worker_process_id,int worker_route_id)61 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
62 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
63 BrowserThread::PostTask(
64 BrowserThread::UI,
65 FROM_HERE,
66 base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
67 return;
68 }
69 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
70 worker_process_id, worker_route_id);
71 }
72
RegisterToWorkerDevToolsManager(int process_id,const ServiceWorkerContextCore * service_worker_context,base::WeakPtr<ServiceWorkerContextCore> service_worker_context_weak,int64 service_worker_version_id,const GURL & url,const base::Callback<void (int worker_devtools_agent_route_id,bool wait_for_debugger)> & callback)73 void RegisterToWorkerDevToolsManager(
74 int process_id,
75 const ServiceWorkerContextCore* service_worker_context,
76 base::WeakPtr<ServiceWorkerContextCore> service_worker_context_weak,
77 int64 service_worker_version_id,
78 const GURL& url,
79 const base::Callback<void(int worker_devtools_agent_route_id,
80 bool wait_for_debugger)>& callback) {
81 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
82 BrowserThread::PostTask(BrowserThread::UI,
83 FROM_HERE,
84 base::Bind(RegisterToWorkerDevToolsManager,
85 process_id,
86 service_worker_context,
87 service_worker_context_weak,
88 service_worker_version_id,
89 url,
90 callback));
91 return;
92 }
93 int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
94 bool wait_for_debugger = false;
95 if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
96 // |rph| may be NULL in unit tests.
97 worker_devtools_agent_route_id = rph->GetNextRoutingID();
98 wait_for_debugger =
99 EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
100 process_id,
101 worker_devtools_agent_route_id,
102 EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
103 service_worker_context,
104 service_worker_context_weak,
105 service_worker_version_id,
106 url));
107 }
108 BrowserThread::PostTask(
109 BrowserThread::IO,
110 FROM_HERE,
111 base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
112 }
113
114 } // namespace
115
~EmbeddedWorkerInstance()116 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
117 if (status_ == STARTING || status_ == RUNNING)
118 Stop();
119 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
120 NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
121 if (context_ && process_id_ != -1)
122 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
123 registry_->RemoveWorker(process_id_, embedded_worker_id_);
124 }
125
Start(int64 service_worker_version_id,const GURL & scope,const GURL & script_url,bool pause_after_download,const StatusCallback & callback)126 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
127 const GURL& scope,
128 const GURL& script_url,
129 bool pause_after_download,
130 const StatusCallback& callback) {
131 if (!context_) {
132 callback.Run(SERVICE_WORKER_ERROR_ABORT);
133 return;
134 }
135 DCHECK(status_ == STOPPED);
136 status_ = STARTING;
137 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
138 new EmbeddedWorkerMsg_StartWorker_Params());
139 TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
140 "EmbeddedWorkerInstance::ProcessAllocate",
141 params.get(),
142 "Scope", scope.spec(),
143 "Script URL", script_url.spec());
144 params->embedded_worker_id = embedded_worker_id_;
145 params->service_worker_version_id = service_worker_version_id;
146 params->scope = scope;
147 params->script_url = script_url;
148 params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
149 params->pause_after_download = pause_after_download;
150 params->wait_for_debugger = false;
151 context_->process_manager()->AllocateWorkerProcess(
152 embedded_worker_id_,
153 scope,
154 script_url,
155 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
156 weak_factory_.GetWeakPtr(),
157 context_,
158 base::Passed(¶ms),
159 callback));
160 }
161
Stop()162 ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
163 DCHECK(status_ == STARTING || status_ == RUNNING);
164 ServiceWorkerStatusCode status =
165 registry_->StopWorker(process_id_, embedded_worker_id_);
166 if (status == SERVICE_WORKER_OK)
167 status_ = STOPPING;
168 return status;
169 }
170
ResumeAfterDownload()171 void EmbeddedWorkerInstance::ResumeAfterDownload() {
172 DCHECK_EQ(STARTING, status_);
173 registry_->Send(
174 process_id_,
175 new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_));
176 }
177
SendMessage(const IPC::Message & message)178 ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
179 const IPC::Message& message) {
180 DCHECK_NE(kInvalidEmbeddedWorkerThreadId, thread_id_);
181 if (status_ != RUNNING && status_ != STARTING)
182 return SERVICE_WORKER_ERROR_IPC_FAILED;
183 return registry_->Send(process_id_,
184 new EmbeddedWorkerContextMsg_MessageToWorker(
185 thread_id_, embedded_worker_id_, message));
186 }
187
EmbeddedWorkerInstance(base::WeakPtr<ServiceWorkerContextCore> context,int embedded_worker_id)188 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
189 base::WeakPtr<ServiceWorkerContextCore> context,
190 int embedded_worker_id)
191 : context_(context),
192 registry_(context->embedded_worker_registry()),
193 embedded_worker_id_(embedded_worker_id),
194 status_(STOPPED),
195 process_id_(-1),
196 thread_id_(kInvalidEmbeddedWorkerThreadId),
197 worker_devtools_agent_route_id_(MSG_ROUTING_NONE),
198 weak_factory_(this) {
199 }
200
201 // static
RunProcessAllocated(base::WeakPtr<EmbeddedWorkerInstance> instance,base::WeakPtr<ServiceWorkerContextCore> context,scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const EmbeddedWorkerInstance::StatusCallback & callback,ServiceWorkerStatusCode status,int process_id)202 void EmbeddedWorkerInstance::RunProcessAllocated(
203 base::WeakPtr<EmbeddedWorkerInstance> instance,
204 base::WeakPtr<ServiceWorkerContextCore> context,
205 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
206 const EmbeddedWorkerInstance::StatusCallback& callback,
207 ServiceWorkerStatusCode status,
208 int process_id) {
209 if (!context) {
210 callback.Run(SERVICE_WORKER_ERROR_ABORT);
211 return;
212 }
213 if (!instance) {
214 if (status == SERVICE_WORKER_OK) {
215 // We only have a process allocated if the status is OK.
216 context->process_manager()->ReleaseWorkerProcess(
217 params->embedded_worker_id);
218 }
219 callback.Run(SERVICE_WORKER_ERROR_ABORT);
220 return;
221 }
222 instance->ProcessAllocated(params.Pass(), callback, process_id, status);
223 }
224
ProcessAllocated(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const StatusCallback & callback,int process_id,ServiceWorkerStatusCode status)225 void EmbeddedWorkerInstance::ProcessAllocated(
226 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
227 const StatusCallback& callback,
228 int process_id,
229 ServiceWorkerStatusCode status) {
230 DCHECK_EQ(process_id_, -1);
231 TRACE_EVENT_ASYNC_END1("ServiceWorker",
232 "EmbeddedWorkerInstance::ProcessAllocate",
233 params.get(),
234 "Status", status);
235 if (status != SERVICE_WORKER_OK) {
236 status_ = STOPPED;
237 callback.Run(status);
238 return;
239 }
240 const int64 service_worker_version_id = params->service_worker_version_id;
241 process_id_ = process_id;
242 GURL script_url(params->script_url);
243 RegisterToWorkerDevToolsManager(
244 process_id,
245 context_.get(),
246 context_,
247 service_worker_version_id,
248 script_url,
249 base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
250 weak_factory_.GetWeakPtr(),
251 base::Passed(¶ms),
252 callback));
253 }
254
SendStartWorker(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const StatusCallback & callback,int worker_devtools_agent_route_id,bool wait_for_debugger)255 void EmbeddedWorkerInstance::SendStartWorker(
256 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
257 const StatusCallback& callback,
258 int worker_devtools_agent_route_id,
259 bool wait_for_debugger) {
260 worker_devtools_agent_route_id_ = worker_devtools_agent_route_id;
261 params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
262 params->wait_for_debugger = wait_for_debugger;
263 registry_->SendStartWorker(params.Pass(), callback, process_id_);
264 }
265
OnReadyForInspection()266 void EmbeddedWorkerInstance::OnReadyForInspection() {
267 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
268 NotifyWorkerReadyForInspection(process_id_,
269 worker_devtools_agent_route_id_);
270 }
271
OnScriptLoaded(int thread_id)272 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id) {
273 thread_id_ = thread_id;
274 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
275 NotifyWorkerContextStarted(process_id_, worker_devtools_agent_route_id_);
276 }
277
OnScriptLoadFailed()278 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
279 }
280
OnStarted()281 void EmbeddedWorkerInstance::OnStarted() {
282 // Stop is requested before OnStarted is sent back from the worker.
283 if (status_ == STOPPING)
284 return;
285 DCHECK(status_ == STARTING);
286 status_ = RUNNING;
287 FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
288 }
289
OnStopped()290 void EmbeddedWorkerInstance::OnStopped() {
291 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
292 NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
293 if (context_)
294 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
295 status_ = STOPPED;
296 process_id_ = -1;
297 thread_id_ = -1;
298 worker_devtools_agent_route_id_ = MSG_ROUTING_NONE;
299 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped());
300 }
301
OnPausedAfterDownload()302 void EmbeddedWorkerInstance::OnPausedAfterDownload() {
303 // Stop can be requested before getting this far.
304 if (status_ == STOPPING)
305 return;
306 DCHECK(status_ == STARTING);
307 FOR_EACH_OBSERVER(Listener, listener_list_, OnPausedAfterDownload());
308 }
309
OnMessageReceived(const IPC::Message & message)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))
314 return true;
315 }
316 return false;
317 }
318
OnReportException(const base::string16 & error_message,int line_number,int column_number,const GURL & source_url)319 void EmbeddedWorkerInstance::OnReportException(
320 const base::string16& error_message,
321 int line_number,
322 int column_number,
323 const GURL& source_url) {
324 FOR_EACH_OBSERVER(
325 Listener,
326 listener_list_,
327 OnReportException(error_message, line_number, column_number, source_url));
328 }
329
OnReportConsoleMessage(int source_identifier,int message_level,const base::string16 & message,int line_number,const GURL & source_url)330 void EmbeddedWorkerInstance::OnReportConsoleMessage(
331 int source_identifier,
332 int message_level,
333 const base::string16& message,
334 int line_number,
335 const GURL& source_url) {
336 FOR_EACH_OBSERVER(
337 Listener,
338 listener_list_,
339 OnReportConsoleMessage(
340 source_identifier, message_level, message, line_number, source_url));
341 }
342
AddListener(Listener * listener)343 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
344 listener_list_.AddObserver(listener);
345 }
346
RemoveListener(Listener * listener)347 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
348 listener_list_.RemoveObserver(listener);
349 }
350
351 } // namespace content
352