Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / service_worker / embedded_worker_instance.cc
blob439be29b8836d97d40a5e046380d2d4be60c921f
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"
7 #include <algorithm>
8 #include <utility>
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"
22 namespace content {
24 namespace {
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,
38 FROM_HERE,
39 base::Bind(NotifyWorkerReadyForInspection,
40 worker_process_id,
41 worker_route_id));
42 return;
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(
51 BrowserThread::UI,
52 FROM_HERE,
53 base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
54 return;
56 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
57 worker_process_id, worker_route_id);
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;
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));
95 BrowserThread::PostTask(
96 BrowserThread::IO,
97 FROM_HERE,
98 base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
101 } // namespace
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_);
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;
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));
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;
158 void EmbeddedWorkerInstance::ResumeAfterDownload() {
159 DCHECK_EQ(STARTING, status_);
160 registry_->Send(
161 process_id_,
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)
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) {
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;
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);
206 callback.Run(SERVICE_WORKER_ERROR_ABORT);
207 return;
209 instance->ProcessAllocated(params.Pass(), callback, process_id, status);
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;
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));
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;
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)
283 return;
284 DCHECK(status_ == STARTING);
285 status_ = RUNNING;
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_);
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());
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());
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;
316 return false;
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));
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));
343 MessagePortMessageFilter* EmbeddedWorkerInstance::message_port_message_filter()
344 const {
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