• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/bind_helpers.h"
8 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
9 #include "content/browser/service_worker/embedded_worker_registry.h"
10 #include "content/browser/service_worker/service_worker_context_core.h"
11 #include "content/common/service_worker/embedded_worker_messages.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "ipc/ipc_message.h"
15 #include "url/gurl.h"
16 
17 namespace content {
18 
19 namespace {
20 
21 // Functor to sort by the .second element of a struct.
22 struct SecondGreater {
23   template <typename Value>
operator ()content::__anon2935c3700111::SecondGreater24   bool operator()(const Value& lhs, const Value& rhs) {
25     return lhs.second > rhs.second;
26   }
27 };
28 
NotifyWorkerContextStarted(int worker_process_id,int worker_route_id)29 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
30   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
31     BrowserThread::PostTask(
32         BrowserThread::UI,
33         FROM_HERE,
34         base::Bind(
35             NotifyWorkerContextStarted, worker_process_id, worker_route_id));
36     return;
37   }
38   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
39       worker_process_id, worker_route_id);
40 }
41 
NotifyWorkerDestroyed(int worker_process_id,int worker_route_id)42 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
43   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
44     BrowserThread::PostTask(
45         BrowserThread::UI,
46         FROM_HERE,
47         base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
48     return;
49   }
50   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
51       worker_process_id, worker_route_id);
52 }
53 
RegisterToWorkerDevToolsManager(int process_id,const ServiceWorkerContextCore * const service_worker_context,int64 service_worker_version_id,const base::Callback<void (int worker_devtools_agent_route_id,bool pause_on_start)> & callback)54 void RegisterToWorkerDevToolsManager(
55     int process_id,
56     const ServiceWorkerContextCore* const service_worker_context,
57     int64 service_worker_version_id,
58     const base::Callback<void(int worker_devtools_agent_route_id,
59                               bool pause_on_start)>& callback) {
60   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
61     BrowserThread::PostTask(BrowserThread::UI,
62                             FROM_HERE,
63                             base::Bind(RegisterToWorkerDevToolsManager,
64                                        process_id,
65                                        service_worker_context,
66                                        service_worker_version_id,
67                                        callback));
68     return;
69   }
70   int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
71   bool pause_on_start = false;
72   if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
73     // |rph| may be NULL in unit tests.
74     worker_devtools_agent_route_id = rph->GetNextRoutingID();
75     pause_on_start =
76         EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
77             process_id,
78             worker_devtools_agent_route_id,
79             EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
80                 service_worker_context, service_worker_version_id));
81   }
82   BrowserThread::PostTask(
83       BrowserThread::IO,
84       FROM_HERE,
85       base::Bind(callback, worker_devtools_agent_route_id, pause_on_start));
86 }
87 
88 }  // namespace
89 
~EmbeddedWorkerInstance()90 EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
91   if (status_ == STARTING || status_ == RUNNING)
92     Stop();
93   if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
94     NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
95   if (context_ && process_id_ != -1)
96     context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
97   registry_->RemoveWorker(process_id_, embedded_worker_id_);
98 }
99 
Start(int64 service_worker_version_id,const GURL & scope,const GURL & script_url,const std::vector<int> & possible_process_ids,const StatusCallback & callback)100 void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
101                                    const GURL& scope,
102                                    const GURL& script_url,
103                                    const std::vector<int>& possible_process_ids,
104                                    const StatusCallback& callback) {
105   if (!context_) {
106     callback.Run(SERVICE_WORKER_ERROR_ABORT);
107     return;
108   }
109   DCHECK(status_ == STOPPED);
110   status_ = STARTING;
111   scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
112       new EmbeddedWorkerMsg_StartWorker_Params());
113   params->embedded_worker_id = embedded_worker_id_;
114   params->service_worker_version_id = service_worker_version_id;
115   params->scope = scope;
116   params->script_url = script_url;
117   params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
118   params->pause_on_start = false;
119   context_->process_manager()->AllocateWorkerProcess(
120       embedded_worker_id_,
121       SortProcesses(possible_process_ids),
122       script_url,
123       base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
124                  weak_factory_.GetWeakPtr(),
125                  context_,
126                  base::Passed(&params),
127                  callback));
128 }
129 
Stop()130 ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
131   DCHECK(status_ == STARTING || status_ == RUNNING);
132   ServiceWorkerStatusCode status =
133       registry_->StopWorker(process_id_, embedded_worker_id_);
134   if (status == SERVICE_WORKER_OK)
135     status_ = STOPPING;
136   return status;
137 }
138 
SendMessage(const IPC::Message & message)139 ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
140     const IPC::Message& message) {
141   DCHECK(status_ == RUNNING);
142   return registry_->Send(process_id_,
143                          new EmbeddedWorkerContextMsg_MessageToWorker(
144                              thread_id_, embedded_worker_id_, message));
145 }
146 
AddProcessReference(int process_id)147 void EmbeddedWorkerInstance::AddProcessReference(int process_id) {
148   ProcessRefMap::iterator found = process_refs_.find(process_id);
149   if (found == process_refs_.end())
150     found = process_refs_.insert(std::make_pair(process_id, 0)).first;
151   ++found->second;
152 }
153 
ReleaseProcessReference(int process_id)154 void EmbeddedWorkerInstance::ReleaseProcessReference(int process_id) {
155   ProcessRefMap::iterator found = process_refs_.find(process_id);
156   if (found == process_refs_.end()) {
157     NOTREACHED() << "Releasing unknown process ref " << process_id;
158     return;
159   }
160   if (--found->second == 0)
161     process_refs_.erase(found);
162 }
163 
EmbeddedWorkerInstance(base::WeakPtr<ServiceWorkerContextCore> context,int embedded_worker_id)164 EmbeddedWorkerInstance::EmbeddedWorkerInstance(
165     base::WeakPtr<ServiceWorkerContextCore> context,
166     int embedded_worker_id)
167     : context_(context),
168       registry_(context->embedded_worker_registry()),
169       embedded_worker_id_(embedded_worker_id),
170       status_(STOPPED),
171       process_id_(-1),
172       thread_id_(-1),
173       worker_devtools_agent_route_id_(MSG_ROUTING_NONE),
174       weak_factory_(this) {
175 }
176 
177 // static
RunProcessAllocated(base::WeakPtr<EmbeddedWorkerInstance> instance,base::WeakPtr<ServiceWorkerContextCore> context,scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const EmbeddedWorkerInstance::StatusCallback & callback,ServiceWorkerStatusCode status,int process_id)178 void EmbeddedWorkerInstance::RunProcessAllocated(
179     base::WeakPtr<EmbeddedWorkerInstance> instance,
180     base::WeakPtr<ServiceWorkerContextCore> context,
181     scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
182     const EmbeddedWorkerInstance::StatusCallback& callback,
183     ServiceWorkerStatusCode status,
184     int process_id) {
185   if (!context) {
186     callback.Run(SERVICE_WORKER_ERROR_ABORT);
187     return;
188   }
189   if (!instance) {
190     if (status == SERVICE_WORKER_OK) {
191       // We only have a process allocated if the status is OK.
192       context->process_manager()->ReleaseWorkerProcess(
193           params->embedded_worker_id);
194     }
195     callback.Run(SERVICE_WORKER_ERROR_ABORT);
196     return;
197   }
198   instance->ProcessAllocated(params.Pass(), callback, process_id, status);
199 }
200 
ProcessAllocated(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const StatusCallback & callback,int process_id,ServiceWorkerStatusCode status)201 void EmbeddedWorkerInstance::ProcessAllocated(
202     scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
203     const StatusCallback& callback,
204     int process_id,
205     ServiceWorkerStatusCode status) {
206   DCHECK_EQ(process_id_, -1);
207   if (status != SERVICE_WORKER_OK) {
208     status_ = STOPPED;
209     callback.Run(status);
210     return;
211   }
212   const int64 service_worker_version_id = params->service_worker_version_id;
213   process_id_ = process_id;
214   RegisterToWorkerDevToolsManager(
215       process_id,
216       context_.get(),
217       service_worker_version_id,
218       base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
219                  weak_factory_.GetWeakPtr(),
220                  base::Passed(&params),
221                  callback));
222 }
223 
SendStartWorker(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,const StatusCallback & callback,int worker_devtools_agent_route_id,bool pause_on_start)224 void EmbeddedWorkerInstance::SendStartWorker(
225     scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
226     const StatusCallback& callback,
227     int worker_devtools_agent_route_id,
228     bool pause_on_start) {
229   worker_devtools_agent_route_id_ = worker_devtools_agent_route_id;
230   params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
231   params->pause_on_start = pause_on_start;
232   registry_->SendStartWorker(params.Pass(), callback, process_id_);
233 }
234 
OnScriptLoaded()235 void EmbeddedWorkerInstance::OnScriptLoaded() {
236   if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
237     NotifyWorkerContextStarted(process_id_, worker_devtools_agent_route_id_);
238 }
239 
OnScriptLoadFailed()240 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
241 }
242 
OnStarted(int thread_id)243 void EmbeddedWorkerInstance::OnStarted(int thread_id) {
244   // Stop is requested before OnStarted is sent back from the worker.
245   if (status_ == STOPPING)
246     return;
247   DCHECK(status_ == STARTING);
248   status_ = RUNNING;
249   thread_id_ = thread_id;
250   FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
251 }
252 
OnStopped()253 void EmbeddedWorkerInstance::OnStopped() {
254   if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
255     NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
256   if (context_)
257     context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
258   status_ = STOPPED;
259   process_id_ = -1;
260   thread_id_ = -1;
261   worker_devtools_agent_route_id_ = MSG_ROUTING_NONE;
262   FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped());
263 }
264 
OnMessageReceived(const IPC::Message & message)265 bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message& message) {
266   ListenerList::Iterator it(listener_list_);
267   while (Listener* listener = it.GetNext()) {
268     if (listener->OnMessageReceived(message))
269       return true;
270   }
271   return false;
272 }
273 
OnReportException(const base::string16 & error_message,int line_number,int column_number,const GURL & source_url)274 void EmbeddedWorkerInstance::OnReportException(
275     const base::string16& error_message,
276     int line_number,
277     int column_number,
278     const GURL& source_url) {
279   FOR_EACH_OBSERVER(
280       Listener,
281       listener_list_,
282       OnReportException(error_message, line_number, column_number, source_url));
283 }
284 
OnReportConsoleMessage(int source_identifier,int message_level,const base::string16 & message,int line_number,const GURL & source_url)285 void EmbeddedWorkerInstance::OnReportConsoleMessage(
286     int source_identifier,
287     int message_level,
288     const base::string16& message,
289     int line_number,
290     const GURL& source_url) {
291   FOR_EACH_OBSERVER(
292       Listener,
293       listener_list_,
294       OnReportConsoleMessage(
295           source_identifier, message_level, message, line_number, source_url));
296 }
297 
AddListener(Listener * listener)298 void EmbeddedWorkerInstance::AddListener(Listener* listener) {
299   listener_list_.AddObserver(listener);
300 }
301 
RemoveListener(Listener * listener)302 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
303   listener_list_.RemoveObserver(listener);
304 }
305 
SortProcesses(const std::vector<int> & possible_process_ids) const306 std::vector<int> EmbeddedWorkerInstance::SortProcesses(
307     const std::vector<int>& possible_process_ids) const {
308   // Add the |possible_process_ids| to the existing process_refs_ since each one
309   // is likely to take a reference once the SW starts up.
310   ProcessRefMap refs_with_new_ids = process_refs_;
311   for (std::vector<int>::const_iterator it = possible_process_ids.begin();
312        it != possible_process_ids.end();
313        ++it) {
314     refs_with_new_ids[*it]++;
315   }
316 
317   std::vector<std::pair<int, int> > counted(refs_with_new_ids.begin(),
318                                             refs_with_new_ids.end());
319   // Sort descending by the reference count.
320   std::sort(counted.begin(), counted.end(), SecondGreater());
321 
322   std::vector<int> result(counted.size());
323   for (size_t i = 0; i < counted.size(); ++i)
324     result[i] = counted[i].first;
325   return result;
326 }
327 
328 }  // namespace content
329