Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / service_worker / embedded_worker_instance.cc
blob1e7145816162630197450297b7d2d7b85ff39241
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 "content/browser/devtools/embedded_worker_devtools_manager.h"
12 #include "content/browser/service_worker/embedded_worker_registry.h"
13 #include "content/browser/service_worker/service_worker_context_core.h"
14 #include "content/common/service_worker/embedded_worker_messages.h"
15 #include "content/common/service_worker/service_worker_types.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "ipc/ipc_message.h"
19 #include "url/gurl.h"
21 namespace content {
23 namespace {
25 // Functor to sort by the .second element of a struct.
26 struct SecondGreater {
27 template <typename Value>
28 bool operator()(const Value& lhs, const Value& rhs) {
29 return lhs.second > rhs.second;
33 void NotifyWorkerReadyForInspection(int worker_process_id,
34 int worker_route_id) {
35 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
36 BrowserThread::PostTask(BrowserThread::UI,
37 FROM_HERE,
38 base::Bind(NotifyWorkerReadyForInspection,
39 worker_process_id,
40 worker_route_id));
41 return;
43 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
44 worker_process_id, worker_route_id);
47 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
48 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
49 BrowserThread::PostTask(
50 BrowserThread::UI,
51 FROM_HERE,
52 base::Bind(
53 NotifyWorkerContextStarted, worker_process_id, worker_route_id));
54 return;
56 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
57 worker_process_id, worker_route_id);
60 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
61 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
62 BrowserThread::PostTask(
63 BrowserThread::UI,
64 FROM_HERE,
65 base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
66 return;
68 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
69 worker_process_id, worker_route_id);
72 void RegisterToWorkerDevToolsManager(
73 int process_id,
74 const ServiceWorkerContextCore* service_worker_context,
75 base::WeakPtr<ServiceWorkerContextCore> service_worker_context_weak,
76 int64 service_worker_version_id,
77 const GURL& url,
78 const base::Callback<void(int worker_devtools_agent_route_id,
79 bool wait_for_debugger)>& callback) {
80 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
81 BrowserThread::PostTask(BrowserThread::UI,
82 FROM_HERE,
83 base::Bind(RegisterToWorkerDevToolsManager,
84 process_id,
85 service_worker_context,
86 service_worker_context_weak,
87 service_worker_version_id,
88 url,
89 callback));
90 return;
92 int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
93 bool wait_for_debugger = false;
94 if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
95 // |rph| may be NULL in unit tests.
96 worker_devtools_agent_route_id = rph->GetNextRoutingID();
97 wait_for_debugger =
98 EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
99 process_id,
100 worker_devtools_agent_route_id,
101 EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
102 service_worker_context,
103 service_worker_context_weak,
104 service_worker_version_id,
105 url));
107 BrowserThread::PostTask(
108 BrowserThread::IO,
109 FROM_HERE,
110 base::Bind(callback, worker_devtools_agent_route_id, wait_for_debugger));
113 } // namespace
115 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
116 if (status_ == STARTING || status_ == RUNNING)
117 Stop();
118 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
119 NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
120 if (context_ && process_id_ != -1)
121 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
122 registry_->RemoveWorker(process_id_, embedded_worker_id_);
125 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
126 const GURL& scope,
127 const GURL& script_url,
128 bool pause_after_download,
129 const std::vector<int>& possible_process_ids,
130 const StatusCallback& callback) {
131 if (!context_) {
132 callback.Run(SERVICE_WORKER_ERROR_ABORT);
133 return;
135 DCHECK(status_ == STOPPED);
136 status_ = STARTING;
137 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
138 new EmbeddedWorkerMsg_StartWorker_Params());
139 params->embedded_worker_id = embedded_worker_id_;
140 params->service_worker_version_id = service_worker_version_id;
141 params->scope = scope;
142 params->script_url = script_url;
143 params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
144 params->pause_after_download = pause_after_download;
145 params->wait_for_debugger = false;
146 context_->process_manager()->AllocateWorkerProcess(
147 embedded_worker_id_,
148 SortProcesses(possible_process_ids),
149 script_url,
150 base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
151 weak_factory_.GetWeakPtr(),
152 context_,
153 base::Passed(&params),
154 callback));
157 ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
158 DCHECK(status_ == STARTING || status_ == RUNNING);
159 ServiceWorkerStatusCode status =
160 registry_->StopWorker(process_id_, embedded_worker_id_);
161 if (status == SERVICE_WORKER_OK)
162 status_ = STOPPING;
163 return status;
166 void EmbeddedWorkerInstance::ResumeAfterDownload() {
167 DCHECK_EQ(STARTING, status_);
168 registry_->Send(
169 process_id_,
170 new EmbeddedWorkerMsg_ResumeAfterDownload(embedded_worker_id_));
173 ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
174 const IPC::Message& message) {
175 DCHECK_NE(kInvalidEmbeddedWorkerThreadId, thread_id_);
176 if (status_ != RUNNING && status_ != STARTING)
177 return SERVICE_WORKER_ERROR_IPC_FAILED;
178 return registry_->Send(process_id_,
179 new EmbeddedWorkerContextMsg_MessageToWorker(
180 thread_id_, embedded_worker_id_, message));
183 void EmbeddedWorkerInstance::AddProcessReference(int process_id) {
184 ProcessRefMap::iterator found = process_refs_.find(process_id);
185 if (found == process_refs_.end())
186 found = process_refs_.insert(std::make_pair(process_id, 0)).first;
187 ++found->second;
190 void EmbeddedWorkerInstance::ReleaseProcessReference(int process_id) {
191 ProcessRefMap::iterator found = process_refs_.find(process_id);
192 if (found == process_refs_.end()) {
193 NOTREACHED() << "Releasing unknown process ref " << process_id;
194 return;
196 if (--found->second == 0)
197 process_refs_.erase(found);
200 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
201 base::WeakPtr<ServiceWorkerContextCore> context,
202 int embedded_worker_id)
203 : context_(context),
204 registry_(context->embedded_worker_registry()),
205 embedded_worker_id_(embedded_worker_id),
206 status_(STOPPED),
207 process_id_(-1),
208 thread_id_(kInvalidEmbeddedWorkerThreadId),
209 worker_devtools_agent_route_id_(MSG_ROUTING_NONE),
210 weak_factory_(this) {
213 // static
214 void EmbeddedWorkerInstance::RunProcessAllocated(
215 base::WeakPtr<EmbeddedWorkerInstance> instance,
216 base::WeakPtr<ServiceWorkerContextCore> context,
217 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
218 const EmbeddedWorkerInstance::StatusCallback& callback,
219 ServiceWorkerStatusCode status,
220 int process_id) {
221 if (!context) {
222 callback.Run(SERVICE_WORKER_ERROR_ABORT);
223 return;
225 if (!instance) {
226 if (status == SERVICE_WORKER_OK) {
227 // We only have a process allocated if the status is OK.
228 context->process_manager()->ReleaseWorkerProcess(
229 params->embedded_worker_id);
231 callback.Run(SERVICE_WORKER_ERROR_ABORT);
232 return;
234 instance->ProcessAllocated(params.Pass(), callback, process_id, status);
237 void EmbeddedWorkerInstance::ProcessAllocated(
238 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
239 const StatusCallback& callback,
240 int process_id,
241 ServiceWorkerStatusCode status) {
242 DCHECK_EQ(process_id_, -1);
243 if (status != SERVICE_WORKER_OK) {
244 status_ = STOPPED;
245 callback.Run(status);
246 return;
248 const int64 service_worker_version_id = params->service_worker_version_id;
249 process_id_ = process_id;
250 GURL script_url(params->script_url);
251 RegisterToWorkerDevToolsManager(
252 process_id,
253 context_.get(),
254 context_,
255 service_worker_version_id,
256 script_url,
257 base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
258 weak_factory_.GetWeakPtr(),
259 base::Passed(&params),
260 callback));
263 void EmbeddedWorkerInstance::SendStartWorker(
264 scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
265 const StatusCallback& callback,
266 int worker_devtools_agent_route_id,
267 bool wait_for_debugger) {
268 worker_devtools_agent_route_id_ = worker_devtools_agent_route_id;
269 params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
270 params->wait_for_debugger = wait_for_debugger;
271 registry_->SendStartWorker(params.Pass(), callback, process_id_);
274 void EmbeddedWorkerInstance::OnReadyForInspection() {
275 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
276 NotifyWorkerReadyForInspection(process_id_,
277 worker_devtools_agent_route_id_);
280 void EmbeddedWorkerInstance::OnScriptLoaded(int thread_id) {
281 thread_id_ = thread_id;
282 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
283 NotifyWorkerContextStarted(process_id_, worker_devtools_agent_route_id_);
286 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
289 void EmbeddedWorkerInstance::OnStarted() {
290 // Stop is requested before OnStarted is sent back from the worker.
291 if (status_ == STOPPING)
292 return;
293 DCHECK(status_ == STARTING);
294 status_ = RUNNING;
295 FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
298 void EmbeddedWorkerInstance::OnStopped() {
299 if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
300 NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
301 if (context_)
302 context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
303 status_ = STOPPED;
304 process_id_ = -1;
305 thread_id_ = -1;
306 worker_devtools_agent_route_id_ = MSG_ROUTING_NONE;
307 FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped());
310 void EmbeddedWorkerInstance::OnPausedAfterDownload() {
311 // Stop can be requested before getting this far.
312 if (status_ == STOPPING)
313 return;
314 DCHECK(status_ == STARTING);
315 FOR_EACH_OBSERVER(Listener, listener_list_, OnPausedAfterDownload());
318 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message& message) {
319 ListenerList::Iterator it(listener_list_);
320 while (Listener* listener = it.GetNext()) {
321 if (listener->OnMessageReceived(message))
322 return true;
324 return false;
327 void EmbeddedWorkerInstance::OnReportException(
328 const base::string16& error_message,
329 int line_number,
330 int column_number,
331 const GURL& source_url) {
332 FOR_EACH_OBSERVER(
333 Listener,
334 listener_list_,
335 OnReportException(error_message, line_number, column_number, source_url));
338 void EmbeddedWorkerInstance::OnReportConsoleMessage(
339 int source_identifier,
340 int message_level,
341 const base::string16& message,
342 int line_number,
343 const GURL& source_url) {
344 FOR_EACH_OBSERVER(
345 Listener,
346 listener_list_,
347 OnReportConsoleMessage(
348 source_identifier, message_level, message, line_number, source_url));
351 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
352 listener_list_.AddObserver(listener);
355 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
356 listener_list_.RemoveObserver(listener);
359 std::vector<int> EmbeddedWorkerInstance::SortProcesses(
360 const std::vector<int>& possible_process_ids) const {
361 // Add the |possible_process_ids| to the existing process_refs_ since each one
362 // is likely to take a reference once the SW starts up.
363 ProcessRefMap refs_with_new_ids = process_refs_;
364 for (std::vector<int>::const_iterator it = possible_process_ids.begin();
365 it != possible_process_ids.end();
366 ++it) {
367 refs_with_new_ids[*it]++;
370 std::vector<std::pair<int, int> > counted(refs_with_new_ids.begin(),
371 refs_with_new_ids.end());
372 // Sort descending by the reference count.
373 std::sort(counted.begin(), counted.end(), SecondGreater());
375 std::vector<int> result(counted.size());
376 for (size_t i = 0; i < counted.size(); ++i)
377 result[i] = counted[i].first;
378 return result;
381 } // namespace content