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> 24 bool operator()(const Value& lhs, const Value& rhs) { 25 return lhs.second > rhs.second; 26 } 27 }; 28 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 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 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 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 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(¶ms), 127 callback)); 128 } 129 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 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 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 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 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 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 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(¶ms), 221 callback)); 222 } 223 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 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 240 void EmbeddedWorkerInstance::OnScriptLoadFailed() { 241 } 242 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 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 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 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 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 298 void EmbeddedWorkerInstance::AddListener(Listener* listener) { 299 listener_list_.AddObserver(listener); 300 } 301 302 void EmbeddedWorkerInstance::RemoveListener(Listener* listener) { 303 listener_list_.RemoveObserver(listener); 304 } 305 306 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