Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / embedded_worker_instance.cc
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>
29   bool operator()(const Value& lhs, const Value& rhs) {
30     return lhs.second > rhs.second;
31   }
32 };
33
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
48 void NotifyWorkerDestroyed(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(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
54     return;
55   }
56   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
57       worker_process_id, worker_route_id);
58 }
59
60 void RegisterToWorkerDevToolsManager(
61     int process_id,
62     const ServiceWorkerContextCore* service_worker_context,
63     base::WeakPtr<ServiceWorkerContextCore> service_worker_context_weak,
64     int64 service_worker_version_id,
65     const GURL& url,
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,
70                             FROM_HERE,
71                             base::Bind(RegisterToWorkerDevToolsManager,
72                                        process_id,
73                                        service_worker_context,
74                                        service_worker_context_weak,
75                                        service_worker_version_id,
76                                        url,
77                                        callback));
78     return;
79   }
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();
85     wait_for_debugger =
86         EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
87             process_id,
88             worker_devtools_agent_route_id,
89             EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
90                 service_worker_context,
91                 service_worker_context_weak,
92                 service_worker_version_id,
93                 url));
94   }
95   BrowserThread::PostTask(
96       BrowserThread::IO,
97       FROM_HERE,
98       base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
99 }
100
101 }  // namespace
102
103 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
104   if (status_ == STARTING || status_ == RUNNING)
105     Stop();
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_);
111 }
112
113 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
114                                    const GURL& scope,
115                                    const GURL& script_url,
116                                    bool pause_after_download,
117                                    const StatusCallback& callback) {
118   if (!context_) {
119     callback.Run(SERVICE_WORKER_ERROR_ABORT);
120     return;
121   }
122   DCHECK(status_ == STOPPED);
123   status_ = STARTING;
124   scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
125       new EmbeddedWorkerMsg_StartWorker_Params());
126   TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
127                            "EmbeddedWorkerInstance::ProcessAllocate",
128                            params.get(),
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(
139       embedded_worker_id_,
140       scope,
141       script_url,
142       base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
143                  weak_factory_.GetWeakPtr(),
144                  context_,
145                  base::Passed(&params),
146                  callback));
147 }
148
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)
154     status_ = STOPPING;
155   return status;
156 }
157
158 void EmbeddedWorkerInstance::ResumeAfterDownload() {
159   DCHECK_EQ(STARTING, status_);
160   registry_->Send(
161       process_id_,
162       new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_));
163 }
164
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));
173 }
174
175 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
176     base::WeakPtr<ServiceWorkerContextCore> context,
177     int embedded_worker_id)
178     : context_(context),
179       registry_(context->embedded_worker_registry()),
180       embedded_worker_id_(embedded_worker_id),
181       status_(STOPPED),
182       process_id_(-1),
183       thread_id_(kInvalidEmbeddedWorkerThreadId),
184       worker_devtools_agent_route_id_(MSG_ROUTING_NONE),
185       weak_factory_(this) {
186 }
187
188 // static
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,
195     int process_id) {
196   if (!context) {
197     callback.Run(SERVICE_WORKER_ERROR_ABORT);
198     return;
199   }
200   if (!instance) {
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);
205     }
206     callback.Run(SERVICE_WORKER_ERROR_ABORT);
207     return;
208   }
209   instance->ProcessAllocated(params.Pass(), callback, process_id, status);
210 }
211
212 void EmbeddedWorkerInstance::ProcessAllocated(
213     scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
214     const StatusCallback& callback,
215     int process_id,
216     ServiceWorkerStatusCode status) {
217   DCHECK_EQ(process_id_, -1);
218   TRACE_EVENT_ASYNC_END1("ServiceWorker",
219                          "EmbeddedWorkerInstance::ProcessAllocate",
220                          params.get(),
221                          "Status", status);
222   if (status != SERVICE_WORKER_OK) {
223     status_ = STOPPED;
224     callback.Run(status);
225     return;
226   }
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(
231       process_id,
232       context_.get(),
233       context_,
234       service_worker_version_id,
235       script_url,
236       base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
237                  weak_factory_.GetWeakPtr(),
238                  base::Passed(&params),
239                  callback));
240 }
241
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);
254     return;
255   }
256   DCHECK(start_callback_.is_null());
257   start_callback_ = callback;
258 }
259
260 void EmbeddedWorkerInstance::OnReadyForInspection() {
261   if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
262     NotifyWorkerReadyForInspection(process_id_,
263                                    worker_devtools_agent_route_id_);
264 }
265
266 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id) {
267   thread_id_ = thread_id;
268 }
269
270 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
271 }
272
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();
278 }
279
280 void EmbeddedWorkerInstance::OnStarted() {
281   // Stop is requested before OnStarted is sent back from the worker.
282   if (status_ == STOPPING)
283     return;
284   DCHECK(status_ == STARTING);
285   status_ = RUNNING;
286   FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
287 }
288
289 void EmbeddedWorkerInstance::OnStopped() {
290   if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
291     NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
292   if (context_)
293     context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
294   status_ = STOPPED;
295   process_id_ = -1;
296   thread_id_ = -1;
297   worker_devtools_agent_route_id_ = MSG_ROUTING_NONE;
298   start_callback_.Reset();
299   FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped());
300 }
301
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
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
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
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
343 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
344   listener_list_.AddObserver(listener);
345 }
346
347 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
348   listener_list_.RemoveObserver(listener);
349 }
350
351 }  // namespace content