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 "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(&params),
    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(&params),
    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