Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / service_worker / embedded_worker_instance.cc
blobc79fdfe34b6b83ff5e314c5a60e8684c1375c417
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/metrics/histogram_macros.h"
12 #include "base/threading/non_thread_safe.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/browser/devtools/service_worker_devtools_manager.h"
15 #include "content/browser/service_worker/embedded_worker_registry.h"
16 #include "content/browser/service_worker/service_worker_context_core.h"
17 #include "content/common/content_switches_internal.h"
18 #include "content/common/mojo/service_registry_impl.h"
19 #include "content/common/service_worker/embedded_worker_messages.h"
20 #include "content/common/service_worker/embedded_worker_setup.mojom.h"
21 #include "content/common/service_worker/service_worker_types.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "ipc/ipc_message.h"
25 #include "url/gurl.h"
27 namespace content {
29 namespace {
31 // Functor to sort by the .second element of a struct.
32 struct SecondGreater {
33 template <typename Value>
34 bool operator()(const Value& lhs, const Value& rhs) {
35 return lhs.second > rhs.second;
39 void NotifyWorkerReadyForInspectionOnUI(int worker_process_id,
40 int worker_route_id) {
41 DCHECK_CURRENTLY_ON(BrowserThread::UI);
42 ServiceWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
43 worker_process_id, worker_route_id);
46 void NotifyWorkerDestroyedOnUI(int worker_process_id, int worker_route_id) {
47 DCHECK_CURRENTLY_ON(BrowserThread::UI);
48 ServiceWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
49 worker_process_id, worker_route_id);
52 void NotifyWorkerStopIgnoredOnUI(int worker_process_id, int worker_route_id) {
53 DCHECK_CURRENTLY_ON(BrowserThread::UI);
54 ServiceWorkerDevToolsManager::GetInstance()->WorkerStopIgnored(
55 worker_process_id, worker_route_id);
58 void RegisterToWorkerDevToolsManagerOnUI(
59 int process_id,
60 const ServiceWorkerContextCore* service_worker_context,
61 const base::WeakPtr<ServiceWorkerContextCore>& service_worker_context_weak,
62 int64 service_worker_version_id,
63 const GURL& url,
64 const base::Callback<void(int worker_devtools_agent_route_id,
65 bool wait_for_debugger)>& callback) {
66 DCHECK_CURRENTLY_ON(BrowserThread::UI);
67 int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
68 bool wait_for_debugger = false;
69 if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
70 // |rph| may be NULL in unit tests.
71 worker_devtools_agent_route_id = rph->GetNextRoutingID();
72 wait_for_debugger =
73 ServiceWorkerDevToolsManager::GetInstance()->WorkerCreated(
74 process_id,
75 worker_devtools_agent_route_id,
76 ServiceWorkerDevToolsManager::ServiceWorkerIdentifier(
77 service_worker_context,
78 service_worker_context_weak,
79 service_worker_version_id,
80 url));
82 BrowserThread::PostTask(
83 BrowserThread::IO,
84 FROM_HERE,
85 base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
88 void SetupMojoOnUIThread(int process_id,
89 int thread_id,
90 mojo::InterfaceRequest<mojo::ServiceProvider> services,
91 mojo::ServiceProviderPtr exposed_services) {
92 RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
93 // |rph| may be NULL in unit tests.
94 if (!rph)
95 return;
96 EmbeddedWorkerSetupPtr setup;
97 rph->GetServiceRegistry()->ConnectToRemoteService(mojo::GetProxy(&setup));
98 setup->ExchangeServiceProviders(thread_id, services.Pass(),
99 exposed_services.Pass());
102 } // namespace
104 // Lives on IO thread, proxies notifications to DevToolsManager that lives on
105 // UI thread. Owned by EmbeddedWorkerInstance.
106 class EmbeddedWorkerInstance::DevToolsProxy : public base::NonThreadSafe {
107 public:
108 DevToolsProxy(int process_id, int agent_route_id)
109 : process_id_(process_id),
110 agent_route_id_(agent_route_id) {}
112 ~DevToolsProxy() {
113 BrowserThread::PostTask(
114 BrowserThread::UI,
115 FROM_HERE,
116 base::Bind(NotifyWorkerDestroyedOnUI,
117 process_id_, agent_route_id_));
120 void NotifyWorkerReadyForInspection() {
121 DCHECK(CalledOnValidThread());
122 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
123 base::Bind(NotifyWorkerReadyForInspectionOnUI,
124 process_id_, agent_route_id_));
127 void NotifyWorkerStopIgnored() {
128 DCHECK(CalledOnValidThread());
129 BrowserThread::PostTask(BrowserThread::UI,
130 FROM_HERE,
131 base::Bind(NotifyWorkerStopIgnoredOnUI,
132 process_id_, agent_route_id_));
135 int agent_route_id() const { return agent_route_id_; }
137 private:
138 const int process_id_;
139 const int agent_route_id_;
140 DISALLOW_COPY_AND_ASSIGN(DevToolsProxy);
143 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
144 DCHECK(status_ == STOPPING || status_ == STOPPED);
145 devtools_proxy_.reset();
146 if (context_ && process_id_ != -1)
147 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
148 registry_->RemoveWorker(process_id_, embedded_worker_id_);
151 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
152 const GURL& scope,
153 const GURL& script_url,
154 const StatusCallback& callback) {
155 if (!context_) {
156 callback.Run(SERVICE_WORKER_ERROR_ABORT);
157 return;
159 DCHECK(status_ == STOPPED);
160 start_timing_ = base::TimeTicks::Now();
161 status_ = STARTING;
162 starting_phase_ = ALLOCATING_PROCESS;
163 network_accessed_for_script_ = false;
164 service_registry_.reset(new ServiceRegistryImpl());
165 FOR_EACH_OBSERVER(Listener, listener_list_, OnStarting());
166 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
167 new EmbeddedWorkerMsg_StartWorker_Params());
168 TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
169 "EmbeddedWorkerInstance::ProcessAllocate",
170 params.get(),
171 "Scope", scope.spec(),
172 "Script URL", script_url.spec());
173 params->embedded_worker_id = embedded_worker_id_;
174 params->service_worker_version_id = service_worker_version_id;
175 params->scope = scope;
176 params->script_url = script_url;
177 params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
178 params->wait_for_debugger = false;
179 params->v8_cache_options = GetV8CacheOptions();
180 context_->process_manager()->AllocateWorkerProcess(
181 embedded_worker_id_,
182 scope,
183 script_url,
184 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
185 weak_factory_.GetWeakPtr(),
186 context_,
187 base::Passed(&params),
188 callback));
191 ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
192 DCHECK(status_ == STARTING || status_ == RUNNING) << status_;
193 ServiceWorkerStatusCode status =
194 registry_->StopWorker(process_id_, embedded_worker_id_);
195 // StopWorker could fail if we can't talk to the worker, which should
196 // basically means it's being terminated, so unconditionally change
197 // the status to STOPPING.
198 status_ = STOPPING;
199 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopping());
200 return status;
203 void EmbeddedWorkerInstance::StopIfIdle() {
204 if (devtools_attached_) {
205 if (devtools_proxy_)
206 devtools_proxy_->NotifyWorkerStopIgnored();
207 return;
209 Stop();
212 ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
213 const IPC::Message& message) {
214 DCHECK_NE(kInvalidEmbeddedWorkerThreadId, thread_id_);
215 if (status_ != RUNNING && status_ != STARTING)
216 return SERVICE_WORKER_ERROR_IPC_FAILED;
217 return registry_->Send(process_id_,
218 new EmbeddedWorkerContextMsg_MessageToWorker(
219 thread_id_, embedded_worker_id_, message));
222 ServiceRegistry* EmbeddedWorkerInstance::GetServiceRegistry() {
223 DCHECK(status_ == STARTING || status_ == RUNNING) << status_;
224 return service_registry_.get();
227 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
228 base::WeakPtr<ServiceWorkerContextCore> context,
229 int embedded_worker_id)
230 : context_(context),
231 registry_(context->embedded_worker_registry()),
232 embedded_worker_id_(embedded_worker_id),
233 status_(STOPPED),
234 starting_phase_(NOT_STARTING),
235 process_id_(-1),
236 thread_id_(kInvalidEmbeddedWorkerThreadId),
237 devtools_attached_(false),
238 network_accessed_for_script_(false),
239 weak_factory_(this) {
242 // static
243 void EmbeddedWorkerInstance::RunProcessAllocated(
244 base::WeakPtr<EmbeddedWorkerInstance> instance,
245 base::WeakPtr<ServiceWorkerContextCore> context,
246 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
247 const EmbeddedWorkerInstance::StatusCallback& callback,
248 ServiceWorkerStatusCode status,
249 int process_id,
250 bool is_new_process) {
251 if (!context) {
252 callback.Run(SERVICE_WORKER_ERROR_ABORT);
253 return;
255 if (!instance) {
256 if (status == SERVICE_WORKER_OK) {
257 // We only have a process allocated if the status is OK.
258 context->process_manager()->ReleaseWorkerProcess(
259 params->embedded_worker_id);
261 callback.Run(SERVICE_WORKER_ERROR_ABORT);
262 return;
264 instance->ProcessAllocated(params.Pass(), callback, process_id,
265 is_new_process, status);
268 void EmbeddedWorkerInstance::ProcessAllocated(
269 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
270 const StatusCallback& callback,
271 int process_id,
272 bool is_new_process,
273 ServiceWorkerStatusCode status) {
274 DCHECK_EQ(process_id_, -1);
275 TRACE_EVENT_ASYNC_END1("ServiceWorker",
276 "EmbeddedWorkerInstance::ProcessAllocate",
277 params.get(),
278 "Status", status);
279 if (status != SERVICE_WORKER_OK) {
280 Status old_status = status_;
281 status_ = STOPPED;
282 service_registry_.reset();
283 callback.Run(status);
284 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped(old_status));
285 return;
287 const int64 service_worker_version_id = params->service_worker_version_id;
288 process_id_ = process_id;
289 GURL script_url(params->script_url);
291 // Register this worker to DevToolsManager on UI thread, then continue to
292 // call SendStartWorker on IO thread.
293 starting_phase_ = REGISTERING_TO_DEVTOOLS;
294 BrowserThread::PostTask(
295 BrowserThread::UI, FROM_HERE,
296 base::Bind(RegisterToWorkerDevToolsManagerOnUI, process_id_,
297 context_.get(), context_, service_worker_version_id,
298 script_url,
299 base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
300 weak_factory_.GetWeakPtr(), base::Passed(&params),
301 callback, is_new_process)));
304 void EmbeddedWorkerInstance::SendStartWorker(
305 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
306 const StatusCallback& callback,
307 bool is_new_process,
308 int worker_devtools_agent_route_id,
309 bool wait_for_debugger) {
310 if (worker_devtools_agent_route_id != MSG_ROUTING_NONE) {
311 DCHECK(!devtools_proxy_);
312 devtools_proxy_.reset(new DevToolsProxy(process_id_,
313 worker_devtools_agent_route_id));
315 params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
316 params->wait_for_debugger = wait_for_debugger;
317 if (params->wait_for_debugger) {
318 // We don't measure the start time when wait_for_debugger flag is set. So we
319 // set the NULL time here.
320 start_timing_ = base::TimeTicks();
321 } else {
322 DCHECK(!start_timing_.is_null());
323 if (is_new_process) {
324 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.NewProcessAllocation",
325 base::TimeTicks::Now() - start_timing_);
326 } else {
327 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ExistingProcessAllocation",
328 base::TimeTicks::Now() - start_timing_);
330 UMA_HISTOGRAM_BOOLEAN("EmbeddedWorkerInstance.ProcessCreated",
331 is_new_process);
332 // Reset |start_timing_| to measure the time excluding the process
333 // allocation time.
334 start_timing_ = base::TimeTicks::Now();
337 starting_phase_ = SENT_START_WORKER;
338 ServiceWorkerStatusCode status =
339 registry_->SendStartWorker(params.Pass(), process_id_);
340 if (status != SERVICE_WORKER_OK) {
341 callback.Run(status);
342 return;
344 DCHECK(start_callback_.is_null());
345 start_callback_ = callback;
348 void EmbeddedWorkerInstance::OnReadyForInspection() {
349 if (devtools_proxy_)
350 devtools_proxy_->NotifyWorkerReadyForInspection();
353 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id) {
354 starting_phase_ = SCRIPT_LOADED;
355 if (!start_timing_.is_null()) {
356 if (network_accessed_for_script_) {
357 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ScriptLoadWithNetworkAccess",
358 base::TimeTicks::Now() - start_timing_);
359 } else {
360 UMA_HISTOGRAM_TIMES(
361 "EmbeddedWorkerInstance.ScriptLoadWithoutNetworkAccess",
362 base::TimeTicks::Now() - start_timing_);
364 // Reset |start_timing_| to measure the time excluding the process
365 // allocation time and the script loading time.
366 start_timing_ = base::TimeTicks::Now();
368 thread_id_ = thread_id;
369 FOR_EACH_OBSERVER(Listener, listener_list_, OnScriptLoaded());
371 mojo::ServiceProviderPtr exposed_services;
372 service_registry_->Bind(GetProxy(&exposed_services));
373 mojo::ServiceProviderPtr services;
374 mojo::InterfaceRequest<mojo::ServiceProvider> services_request =
375 GetProxy(&services);
376 BrowserThread::PostTask(
377 BrowserThread::UI, FROM_HERE,
378 base::Bind(SetupMojoOnUIThread, process_id_, thread_id_,
379 base::Passed(&services_request),
380 base::Passed(&exposed_services)));
381 service_registry_->BindRemoteServiceProvider(services.Pass());
384 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
387 void EmbeddedWorkerInstance::OnScriptEvaluated(bool success) {
388 starting_phase_ = SCRIPT_EVALUATED;
389 if (start_callback_.is_null()) {
390 DVLOG(1) << "Received unexpected OnScriptEvaluated message.";
391 return;
393 if (success && !start_timing_.is_null()) {
394 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ScriptEvaluate",
395 base::TimeTicks::Now() - start_timing_);
397 start_callback_.Run(success ? SERVICE_WORKER_OK
398 : SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED);
399 start_callback_.Reset();
402 void EmbeddedWorkerInstance::OnStarted() {
403 // Stop is requested before OnStarted is sent back from the worker.
404 if (status_ == STOPPING)
405 return;
406 DCHECK(status_ == STARTING);
407 status_ = RUNNING;
408 FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
411 void EmbeddedWorkerInstance::OnStopped() {
412 Status old_status = status_;
413 ReleaseProcess();
414 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped(old_status));
417 void EmbeddedWorkerInstance::OnDetached() {
418 Status old_status = status_;
419 ReleaseProcess();
420 FOR_EACH_OBSERVER(Listener, listener_list_, OnDetached(old_status));
423 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message& message) {
424 ListenerList::Iterator it(&listener_list_);
425 while (Listener* listener = it.GetNext()) {
426 if (listener->OnMessageReceived(message))
427 return true;
429 return false;
432 void EmbeddedWorkerInstance::OnReportException(
433 const base::string16& error_message,
434 int line_number,
435 int column_number,
436 const GURL& source_url) {
437 FOR_EACH_OBSERVER(
438 Listener,
439 listener_list_,
440 OnReportException(error_message, line_number, column_number, source_url));
443 void EmbeddedWorkerInstance::OnReportConsoleMessage(
444 int source_identifier,
445 int message_level,
446 const base::string16& message,
447 int line_number,
448 const GURL& source_url) {
449 FOR_EACH_OBSERVER(
450 Listener,
451 listener_list_,
452 OnReportConsoleMessage(
453 source_identifier, message_level, message, line_number, source_url));
456 int EmbeddedWorkerInstance::worker_devtools_agent_route_id() const {
457 if (devtools_proxy_)
458 return devtools_proxy_->agent_route_id();
459 return MSG_ROUTING_NONE;
462 MessagePortMessageFilter* EmbeddedWorkerInstance::message_port_message_filter()
463 const {
464 return registry_->MessagePortMessageFilterForProcess(process_id_);
467 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
468 listener_list_.AddObserver(listener);
471 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
472 listener_list_.RemoveObserver(listener);
475 void EmbeddedWorkerInstance::OnNetworkAccessedForScriptLoad() {
476 starting_phase_ = SCRIPT_DOWNLOADING;
477 network_accessed_for_script_ = true;
480 void EmbeddedWorkerInstance::ReleaseProcess() {
481 devtools_proxy_.reset();
482 if (context_)
483 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
484 status_ = STOPPED;
485 process_id_ = -1;
486 thread_id_ = -1;
487 service_registry_.reset();
488 start_callback_.Reset();
491 // static
492 std::string EmbeddedWorkerInstance::StatusToString(Status status) {
493 switch (status) {
494 case STOPPED:
495 return "STOPPED";
496 case STARTING:
497 return "STARTING";
498 case RUNNING:
499 return "RUNNING";
500 case STOPPING:
501 return "STOPPING";
503 NOTREACHED() << status;
504 return std::string();
507 // static
508 std::string EmbeddedWorkerInstance::StartingPhaseToString(StartingPhase phase) {
509 switch (phase) {
510 case NOT_STARTING:
511 return "Not in STARTING status";
512 case ALLOCATING_PROCESS:
513 return "Allocating process";
514 case REGISTERING_TO_DEVTOOLS:
515 return "Registering to DevTools";
516 case SENT_START_WORKER:
517 return "Sent StartWorker message to renderer";
518 case SCRIPT_DOWNLOADING:
519 return "Script downloading";
520 case SCRIPT_LOADED:
521 return "Script loaded";
522 case SCRIPT_EVALUATED:
523 return "Script evaluated";
524 case STARTING_PHASE_MAX_VALUE:
525 NOTREACHED();
527 NOTREACHED() << phase;
528 return std::string();
531 } // namespace content