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/service_worker_registration.h"
      6 
      7 #include "content/browser/service_worker/service_worker_context_core.h"
      8 #include "content/browser/service_worker/service_worker_info.h"
      9 #include "content/browser/service_worker/service_worker_register_job.h"
     10 #include "content/browser/service_worker/service_worker_utils.h"
     11 #include "content/public/browser/browser_thread.h"
     12 
     13 namespace content {
     14 
     15 namespace {
     16 
     17 ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) {
     18   if (!version)
     19     return ServiceWorkerVersionInfo();
     20   return version->GetInfo();
     21 }
     22 
     23 }  // namespace
     24 
     25 ServiceWorkerRegistration::ServiceWorkerRegistration(
     26     const GURL& pattern,
     27     int64 registration_id,
     28     base::WeakPtr<ServiceWorkerContextCore> context)
     29     : pattern_(pattern),
     30       registration_id_(registration_id),
     31       is_deleted_(false),
     32       is_uninstalling_(false),
     33       is_uninstalled_(false),
     34       should_activate_when_ready_(false),
     35       context_(context) {
     36   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     37   DCHECK(context_);
     38   context_->AddLiveRegistration(this);
     39 }
     40 
     41 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
     42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     43   DCHECK(!listeners_.might_have_observers());
     44   if (context_)
     45     context_->RemoveLiveRegistration(registration_id_);
     46   if (active_version())
     47     active_version()->RemoveListener(this);
     48 }
     49 
     50 ServiceWorkerVersion* ServiceWorkerRegistration::GetNewestVersion() const {
     51   if (installing_version())
     52     return installing_version();
     53   if (waiting_version())
     54     return waiting_version();
     55   return active_version();
     56 }
     57 
     58 void ServiceWorkerRegistration::AddListener(Listener* listener) {
     59   listeners_.AddObserver(listener);
     60 }
     61 
     62 void ServiceWorkerRegistration::RemoveListener(Listener* listener) {
     63   listeners_.RemoveObserver(listener);
     64 }
     65 
     66 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
     67   FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this));
     68 }
     69 
     70 void ServiceWorkerRegistration::NotifyUpdateFound() {
     71   FOR_EACH_OBSERVER(Listener, listeners_, OnUpdateFound(this));
     72 }
     73 
     74 ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() {
     75   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     76   return ServiceWorkerRegistrationInfo(
     77       pattern(),
     78       registration_id_,
     79       GetVersionInfo(active_version_.get()),
     80       GetVersionInfo(waiting_version_.get()),
     81       GetVersionInfo(installing_version_.get()));
     82 }
     83 
     84 void ServiceWorkerRegistration::SetActiveVersion(
     85     ServiceWorkerVersion* version) {
     86   should_activate_when_ready_ = false;
     87   SetVersionInternal(version, &active_version_,
     88                      ChangedVersionAttributesMask::ACTIVE_VERSION);
     89 }
     90 
     91 void ServiceWorkerRegistration::SetWaitingVersion(
     92     ServiceWorkerVersion* version) {
     93   should_activate_when_ready_ = false;
     94   SetVersionInternal(version, &waiting_version_,
     95                      ChangedVersionAttributesMask::WAITING_VERSION);
     96 }
     97 
     98 void ServiceWorkerRegistration::SetInstallingVersion(
     99     ServiceWorkerVersion* version) {
    100   SetVersionInternal(version, &installing_version_,
    101                      ChangedVersionAttributesMask::INSTALLING_VERSION);
    102 }
    103 
    104 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion* version) {
    105   if (!version)
    106     return;
    107   ChangedVersionAttributesMask mask;
    108   UnsetVersionInternal(version, &mask);
    109   if (mask.changed()) {
    110     ServiceWorkerRegistrationInfo info = GetInfo();
    111     FOR_EACH_OBSERVER(Listener, listeners_,
    112                       OnVersionAttributesChanged(this, mask, info));
    113   }
    114 }
    115 
    116 void ServiceWorkerRegistration::SetVersionInternal(
    117     ServiceWorkerVersion* version,
    118     scoped_refptr<ServiceWorkerVersion>* data_member,
    119     int change_flag) {
    120   if (version == data_member->get())
    121     return;
    122   scoped_refptr<ServiceWorkerVersion> protect(version);
    123   ChangedVersionAttributesMask mask;
    124   if (version)
    125     UnsetVersionInternal(version, &mask);
    126   *data_member = version;
    127   if (active_version_.get() && active_version_.get() == version)
    128     active_version_->AddListener(this);
    129   mask.add(change_flag);
    130   ServiceWorkerRegistrationInfo info = GetInfo();
    131   FOR_EACH_OBSERVER(Listener, listeners_,
    132                     OnVersionAttributesChanged(this, mask, info));
    133 }
    134 
    135 void ServiceWorkerRegistration::UnsetVersionInternal(
    136     ServiceWorkerVersion* version,
    137     ChangedVersionAttributesMask* mask) {
    138   DCHECK(version);
    139   if (installing_version_.get() == version) {
    140     installing_version_ = NULL;
    141     mask->add(ChangedVersionAttributesMask::INSTALLING_VERSION);
    142   } else if (waiting_version_.get() == version) {
    143     waiting_version_ = NULL;
    144     mask->add(ChangedVersionAttributesMask::WAITING_VERSION);
    145   } else if (active_version_.get() == version) {
    146     active_version_->RemoveListener(this);
    147     active_version_ = NULL;
    148     mask->add(ChangedVersionAttributesMask::ACTIVE_VERSION);
    149   }
    150 }
    151 
    152 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
    153   DCHECK(waiting_version());
    154   should_activate_when_ready_ = true;
    155   if (!active_version() || !active_version()->HasControllee())
    156     ActivateWaitingVersion();
    157 }
    158 
    159 void ServiceWorkerRegistration::ClearWhenReady() {
    160   DCHECK(context_);
    161   if (is_uninstalling_)
    162     return;
    163   is_uninstalling_ = true;
    164 
    165   context_->storage()->NotifyUninstallingRegistration(this);
    166   context_->storage()->DeleteRegistration(
    167       id(),
    168       pattern().GetOrigin(),
    169       base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
    170 
    171   if (!active_version() || !active_version()->HasControllee())
    172     Clear();
    173 }
    174 
    175 void ServiceWorkerRegistration::AbortPendingClear(
    176     const StatusCallback& callback) {
    177   DCHECK(context_);
    178   if (!is_uninstalling()) {
    179     callback.Run(SERVICE_WORKER_OK);
    180     return;
    181   }
    182   is_uninstalling_ = false;
    183   context_->storage()->NotifyDoneUninstallingRegistration(this);
    184 
    185   scoped_refptr<ServiceWorkerVersion> most_recent_version =
    186       waiting_version() ? waiting_version() : active_version();
    187   DCHECK(most_recent_version.get());
    188   context_->storage()->NotifyInstallingRegistration(this);
    189   context_->storage()->StoreRegistration(
    190       this,
    191       most_recent_version.get(),
    192       base::Bind(&ServiceWorkerRegistration::OnRestoreFinished,
    193                  this,
    194                  callback,
    195                  most_recent_version));
    196 }
    197 
    198 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) {
    199   DCHECK_EQ(active_version(), version);
    200   if (is_uninstalling_)
    201     Clear();
    202   else if (should_activate_when_ready_)
    203     ActivateWaitingVersion();
    204   is_uninstalling_ = false;
    205   should_activate_when_ready_ = false;
    206 }
    207 
    208 void ServiceWorkerRegistration::ActivateWaitingVersion() {
    209   DCHECK(context_);
    210   DCHECK(waiting_version());
    211   DCHECK(should_activate_when_ready_);
    212   should_activate_when_ready_ = false;
    213   scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version();
    214   scoped_refptr<ServiceWorkerVersion> exiting_version = active_version();
    215 
    216   if (activating_version->is_doomed() ||
    217       activating_version->status() == ServiceWorkerVersion::REDUNDANT) {
    218     return;  // Activation is no longer relevant.
    219   }
    220 
    221   // "4. If exitingWorker is not null,
    222   if (exiting_version.get()) {
    223     DCHECK(!exiting_version->HasControllee());
    224     // TODO(michaeln): should wait for events to be complete
    225     // "1. Wait for exitingWorker to finish handling any in-progress requests."
    226     // "2. Terminate exitingWorker."
    227     exiting_version->StopWorker(
    228         base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
    229     // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
    230     // "redundant" as the arguments."
    231     exiting_version->SetStatus(ServiceWorkerVersion::REDUNDANT);
    232   }
    233 
    234   // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker."
    235   // "6. Set serviceWorkerRegistration.waitingWorker to null."
    236   SetActiveVersion(activating_version.get());
    237 
    238   // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and
    239   // "activating" as arguments."
    240   activating_version->SetStatus(ServiceWorkerVersion::ACTIVATING);
    241 
    242   // TODO(nhiroki): "8. Fire a simple event named controllerchange..."
    243 
    244   // "9. Queue a task to fire an event named activate..."
    245   activating_version->DispatchActivateEvent(
    246       base::Bind(&ServiceWorkerRegistration::OnActivateEventFinished,
    247                  this, activating_version));
    248 }
    249 
    250 void ServiceWorkerRegistration::OnActivateEventFinished(
    251     ServiceWorkerVersion* activating_version,
    252     ServiceWorkerStatusCode status) {
    253   if (!context_ || activating_version != active_version())
    254     return;
    255   // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
    256   // unexpectedly terminated) we may want to retry sending the event again.
    257   if (status != SERVICE_WORKER_OK) {
    258     // "11. If activateFailed is true, then:..."
    259     UnsetVersion(activating_version);
    260     activating_version->Doom();
    261     if (!waiting_version()) {
    262       // Delete the records from the db.
    263       context_->storage()->DeleteRegistration(
    264           id(), pattern().GetOrigin(),
    265           base::Bind(&ServiceWorkerRegistration::OnDeleteFinished, this));
    266       // But not from memory if there is a version in the pipeline.
    267       if (installing_version())
    268         is_deleted_ = false;
    269     }
    270     return;
    271   }
    272 
    273   // "12. Run the [[UpdateState]] algorithm passing registration.activeWorker
    274   // and "activated" as the arguments."
    275   activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
    276   if (context_) {
    277     context_->storage()->UpdateToActiveState(
    278         this,
    279         base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
    280   }
    281 }
    282 
    283 void ServiceWorkerRegistration::OnDeleteFinished(
    284     ServiceWorkerStatusCode status) {
    285   // Intentionally empty completion callback, used to prevent
    286   // |this| from being deleted until the storage method completes.
    287 }
    288 
    289 void ServiceWorkerRegistration::Clear() {
    290   is_uninstalling_ = false;
    291   is_uninstalled_ = true;
    292   if (context_)
    293     context_->storage()->NotifyDoneUninstallingRegistration(this);
    294 
    295   ChangedVersionAttributesMask mask;
    296   if (installing_version_.get()) {
    297     installing_version_->Doom();
    298     installing_version_ = NULL;
    299     mask.add(ChangedVersionAttributesMask::INSTALLING_VERSION);
    300   }
    301   if (waiting_version_.get()) {
    302     waiting_version_->Doom();
    303     waiting_version_ = NULL;
    304     mask.add(ChangedVersionAttributesMask::WAITING_VERSION);
    305   }
    306   if (active_version_.get()) {
    307     active_version_->Doom();
    308     active_version_->RemoveListener(this);
    309     active_version_ = NULL;
    310     mask.add(ChangedVersionAttributesMask::ACTIVE_VERSION);
    311   }
    312   if (mask.changed()) {
    313     ServiceWorkerRegistrationInfo info = GetInfo();
    314     FOR_EACH_OBSERVER(Listener, listeners_,
    315                       OnVersionAttributesChanged(this, mask, info));
    316   }
    317 
    318   FOR_EACH_OBSERVER(
    319       Listener, listeners_, OnRegistrationFinishedUninstalling(this));
    320 }
    321 
    322 void ServiceWorkerRegistration::OnRestoreFinished(
    323     const StatusCallback& callback,
    324     scoped_refptr<ServiceWorkerVersion> version,
    325     ServiceWorkerStatusCode status) {
    326   if (!context_) {
    327     callback.Run(SERVICE_WORKER_ERROR_ABORT);
    328     return;
    329   }
    330   context_->storage()->NotifyDoneInstallingRegistration(
    331       this, version.get(), status);
    332   callback.Run(status);
    333 }
    334 
    335 }  // namespace content
    336