ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / service_worker / embedded_worker_instance.cc
blob8d589eae349080d314afbfff5736af395f6c24b9
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/service_worker/embedded_worker_messages.h"
19 #include "content/common/service_worker/service_worker_types.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "ipc/ipc_message.h"
23 #include "url/gurl.h"
25 namespace content {
27 namespace {
29 // Functor to sort by the .second element of a struct.
30 struct SecondGreater {
31 template <typename Value>
32 bool operator()(const Value& lhs, const Value& rhs) {
33 return lhs.second > rhs.second;
37 void NotifyWorkerReadyForInspectionOnUI(int worker_process_id,
38 int worker_route_id) {
39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40 ServiceWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
41 worker_process_id, worker_route_id);
44 void NotifyWorkerDestroyedOnUI(int worker_process_id, int worker_route_id) {
45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
46 ServiceWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
47 worker_process_id, worker_route_id);
50 void NotifyWorkerStopIgnoredOnUI(int worker_process_id, int worker_route_id) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 ServiceWorkerDevToolsManager::GetInstance()->WorkerStopIgnored(
53 worker_process_id, worker_route_id);
56 void RegisterToWorkerDevToolsManagerOnUI(
57 int process_id,
58 const ServiceWorkerContextCore* service_worker_context,
59 const base::WeakPtr<ServiceWorkerContextCore>& service_worker_context_weak,
60 int64 service_worker_version_id,
61 const GURL& url,
62 const base::Callback<void(int worker_devtools_agent_route_id,
63 bool wait_for_debugger)>& callback) {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65 int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
66 bool wait_for_debugger = false;
67 if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
68 // |rph| may be NULL in unit tests.
69 worker_devtools_agent_route_id = rph->GetNextRoutingID();
70 wait_for_debugger =
71 ServiceWorkerDevToolsManager::GetInstance()->WorkerCreated(
72 process_id,
73 worker_devtools_agent_route_id,
74 ServiceWorkerDevToolsManager::ServiceWorkerIdentifier(
75 service_worker_context,
76 service_worker_context_weak,
77 service_worker_version_id,
78 url));
80 BrowserThread::PostTask(
81 BrowserThread::IO,
82 FROM_HERE,
83 base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
86 } // namespace
88 // Lives on IO thread, proxies notifications to DevToolsManager that lives on
89 // UI thread. Owned by EmbeddedWorkerInstance.
90 class EmbeddedWorkerInstance::DevToolsProxy : public base::NonThreadSafe {
91 public:
92 DevToolsProxy(int process_id, int agent_route_id)
93 : process_id_(process_id),
94 agent_route_id_(agent_route_id) {}
96 ~DevToolsProxy() {
97 BrowserThread::PostTask(
98 BrowserThread::UI,
99 FROM_HERE,
100 base::Bind(NotifyWorkerDestroyedOnUI,
101 process_id_, agent_route_id_));
104 void NotifyWorkerReadyForInspection() {
105 DCHECK(CalledOnValidThread());
106 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
107 base::Bind(NotifyWorkerReadyForInspectionOnUI,
108 process_id_, agent_route_id_));
111 void NotifyWorkerStopIgnored() {
112 DCHECK(CalledOnValidThread());
113 BrowserThread::PostTask(BrowserThread::UI,
114 FROM_HERE,
115 base::Bind(NotifyWorkerStopIgnoredOnUI,
116 process_id_, agent_route_id_));
119 int agent_route_id() const { return agent_route_id_; }
121 private:
122 const int process_id_;
123 const int agent_route_id_;
124 DISALLOW_COPY_AND_ASSIGN(DevToolsProxy);
127 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
128 if (status_ == STARTING || status_ == RUNNING)
129 Stop();
130 devtools_proxy_.reset();
131 if (context_ && process_id_ != -1)
132 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
133 registry_->RemoveWorker(process_id_, embedded_worker_id_);
136 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
137 const GURL& scope,
138 const GURL& script_url,
139 bool pause_after_download,
140 const StatusCallback& callback) {
141 if (!context_) {
142 callback.Run(SERVICE_WORKER_ERROR_ABORT);
143 return;
145 DCHECK(status_ == STOPPED);
146 start_timing_ = base::TimeTicks::Now();
147 status_ = STARTING;
148 network_accessed_for_script_ = false;
149 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
150 new EmbeddedWorkerMsg_StartWorker_Params());
151 TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
152 "EmbeddedWorkerInstance::ProcessAllocate",
153 params.get(),
154 "Scope", scope.spec(),
155 "Script URL", script_url.spec());
156 params->embedded_worker_id = embedded_worker_id_;
157 params->service_worker_version_id = service_worker_version_id;
158 params->scope = scope;
159 params->script_url = script_url;
160 params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
161 params->pause_after_download = pause_after_download;
162 params->wait_for_debugger = false;
163 params->v8_cache_options = GetV8CacheOptions();
164 context_->process_manager()->AllocateWorkerProcess(
165 embedded_worker_id_,
166 scope,
167 script_url,
168 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
169 weak_factory_.GetWeakPtr(),
170 context_,
171 base::Passed(&params),
172 callback));
175 ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
176 DCHECK(status_ == STARTING || status_ == RUNNING) << status_;
177 ServiceWorkerStatusCode status =
178 registry_->StopWorker(process_id_, embedded_worker_id_);
179 if (status == SERVICE_WORKER_OK)
180 status_ = STOPPING;
181 return status;
184 void EmbeddedWorkerInstance::StopIfIdle() {
185 if (devtools_attached_) {
186 if (devtools_proxy_)
187 devtools_proxy_->NotifyWorkerStopIgnored();
188 return;
190 Stop();
193 void EmbeddedWorkerInstance::ResumeAfterDownload() {
194 DCHECK_EQ(STARTING, status_);
195 registry_->Send(
196 process_id_,
197 new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_));
200 ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
201 const IPC::Message& message) {
202 DCHECK_NE(kInvalidEmbeddedWorkerThreadId, thread_id_);
203 if (status_ != RUNNING && status_ != STARTING)
204 return SERVICE_WORKER_ERROR_IPC_FAILED;
205 return registry_->Send(process_id_,
206 new EmbeddedWorkerContextMsg_MessageToWorker(
207 thread_id_, embedded_worker_id_, message));
210 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
211 base::WeakPtr<ServiceWorkerContextCore> context,
212 int embedded_worker_id)
213 : context_(context),
214 registry_(context->embedded_worker_registry()),
215 embedded_worker_id_(embedded_worker_id),
216 status_(STOPPED),
217 process_id_(-1),
218 thread_id_(kInvalidEmbeddedWorkerThreadId),
219 devtools_attached_(false),
220 network_accessed_for_script_(false),
221 weak_factory_(this) {
224 // static
225 void EmbeddedWorkerInstance::RunProcessAllocated(
226 base::WeakPtr<EmbeddedWorkerInstance> instance,
227 base::WeakPtr<ServiceWorkerContextCore> context,
228 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
229 const EmbeddedWorkerInstance::StatusCallback& callback,
230 ServiceWorkerStatusCode status,
231 int process_id) {
232 if (!context) {
233 callback.Run(SERVICE_WORKER_ERROR_ABORT);
234 return;
236 if (!instance) {
237 if (status == SERVICE_WORKER_OK) {
238 // We only have a process allocated if the status is OK.
239 context->process_manager()->ReleaseWorkerProcess(
240 params->embedded_worker_id);
242 callback.Run(SERVICE_WORKER_ERROR_ABORT);
243 return;
245 instance->ProcessAllocated(params.Pass(), callback, process_id, status);
248 void EmbeddedWorkerInstance::ProcessAllocated(
249 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
250 const StatusCallback& callback,
251 int process_id,
252 ServiceWorkerStatusCode status) {
253 DCHECK_EQ(process_id_, -1);
254 TRACE_EVENT_ASYNC_END1("ServiceWorker",
255 "EmbeddedWorkerInstance::ProcessAllocate",
256 params.get(),
257 "Status", status);
258 if (status != SERVICE_WORKER_OK) {
259 status_ = STOPPED;
260 callback.Run(status);
261 return;
263 const int64 service_worker_version_id = params->service_worker_version_id;
264 process_id_ = process_id;
265 GURL script_url(params->script_url);
267 // Register this worker to DevToolsManager on UI thread, then continue to
268 // call SendStartWorker on IO thread.
269 BrowserThread::PostTask(
270 BrowserThread::UI,
271 FROM_HERE,
272 base::Bind(RegisterToWorkerDevToolsManagerOnUI,
273 process_id_,
274 context_.get(),
275 context_,
276 service_worker_version_id,
277 script_url,
278 base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
279 weak_factory_.GetWeakPtr(),
280 base::Passed(&params),
281 callback)));
284 void EmbeddedWorkerInstance::SendStartWorker(
285 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
286 const StatusCallback& callback,
287 int worker_devtools_agent_route_id,
288 bool wait_for_debugger) {
289 if (worker_devtools_agent_route_id != MSG_ROUTING_NONE) {
290 DCHECK(!devtools_proxy_);
291 devtools_proxy_.reset(new DevToolsProxy(process_id_,
292 worker_devtools_agent_route_id));
294 params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
295 params->wait_for_debugger = wait_for_debugger;
296 if (params->pause_after_download || params->wait_for_debugger) {
297 // We don't measure the start time when pause_after_download or
298 // wait_for_debugger flag is set. So we set the NULL time here.
299 start_timing_ = base::TimeTicks();
300 } else {
301 DCHECK(!start_timing_.is_null());
302 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ProcessAllocation",
303 base::TimeTicks::Now() - start_timing_);
304 // Reset |start_timing_| to measure the time excluding the process
305 // allocation time.
306 start_timing_ = base::TimeTicks::Now();
308 ServiceWorkerStatusCode status =
309 registry_->SendStartWorker(params.Pass(), process_id_);
310 if (status != SERVICE_WORKER_OK) {
311 callback.Run(status);
312 return;
314 DCHECK(start_callback_.is_null());
315 start_callback_ = callback;
318 void EmbeddedWorkerInstance::OnReadyForInspection() {
319 if (devtools_proxy_)
320 devtools_proxy_->NotifyWorkerReadyForInspection();
323 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id) {
324 if (!start_timing_.is_null()) {
325 if (network_accessed_for_script_) {
326 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ScriptLoadWithNetworkAccess",
327 base::TimeTicks::Now() - start_timing_);
328 } else {
329 UMA_HISTOGRAM_TIMES(
330 "EmbeddedWorkerInstance.ScriptLoadWithoutNetworkAccess",
331 base::TimeTicks::Now() - start_timing_);
333 // Reset |start_timing_| to measure the time excluding the process
334 // allocation time and the script loading time.
335 start_timing_ = base::TimeTicks::Now();
337 thread_id_ = thread_id;
338 FOR_EACH_OBSERVER(Listener, listener_list_, OnScriptLoaded());
341 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
344 void EmbeddedWorkerInstance::OnScriptEvaluated(bool success) {
345 if (success && !start_timing_.is_null()) {
346 UMA_HISTOGRAM_TIMES("EmbeddedWorkerInstance.ScriptEvaluate",
347 base::TimeTicks::Now() - start_timing_);
349 DCHECK(!start_callback_.is_null());
350 start_callback_.Run(success ? SERVICE_WORKER_OK
351 : SERVICE_WORKER_ERROR_START_WORKER_FAILED);
352 start_callback_.Reset();
355 void EmbeddedWorkerInstance::OnStarted() {
356 // Stop is requested before OnStarted is sent back from the worker.
357 if (status_ == STOPPING)
358 return;
359 DCHECK(status_ == STARTING);
360 status_ = RUNNING;
361 FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
364 void EmbeddedWorkerInstance::OnStopped() {
365 devtools_proxy_.reset();
366 if (context_)
367 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
368 Status old_status = status_;
369 status_ = STOPPED;
370 process_id_ = -1;
371 thread_id_ = -1;
372 start_callback_.Reset();
373 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped(old_status));
376 void EmbeddedWorkerInstance::OnPausedAfterDownload() {
377 // Stop can be requested before getting this far.
378 if (status_ == STOPPING)
379 return;
380 DCHECK(status_ == STARTING);
381 FOR_EACH_OBSERVER(Listener, listener_list_, OnPausedAfterDownload());
384 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message& message) {
385 ListenerList::Iterator it(listener_list_);
386 while (Listener* listener = it.GetNext()) {
387 if (listener->OnMessageReceived(message))
388 return true;
390 return false;
393 void EmbeddedWorkerInstance::OnReportException(
394 const base::string16& error_message,
395 int line_number,
396 int column_number,
397 const GURL& source_url) {
398 FOR_EACH_OBSERVER(
399 Listener,
400 listener_list_,
401 OnReportException(error_message, line_number, column_number, source_url));
404 void EmbeddedWorkerInstance::OnReportConsoleMessage(
405 int source_identifier,
406 int message_level,
407 const base::string16& message,
408 int line_number,
409 const GURL& source_url) {
410 FOR_EACH_OBSERVER(
411 Listener,
412 listener_list_,
413 OnReportConsoleMessage(
414 source_identifier, message_level, message, line_number, source_url));
417 int EmbeddedWorkerInstance::worker_devtools_agent_route_id() const {
418 if (devtools_proxy_)
419 return devtools_proxy_->agent_route_id();
420 return MSG_ROUTING_NONE;
423 MessagePortMessageFilter* EmbeddedWorkerInstance::message_port_message_filter()
424 const {
425 return registry_->MessagePortMessageFilterForProcess(process_id_);
428 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
429 listener_list_.AddObserver(listener);
432 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
433 listener_list_.RemoveObserver(listener);
436 void EmbeddedWorkerInstance::OnNetworkAccessedForScriptLoad() {
437 network_accessed_for_script_ = true;
440 } // namespace content