Home | History | Annotate | Download | only in service_worker
      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(&params),
    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(&params),
    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