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