• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params),
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(&params),
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