Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_register_job.cc
index 5f9d2f2..b728eca 100644 (file)
 
 #include <vector>
 
+#include "base/message_loop/message_loop.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_job_coordinator.h"
 #include "content/browser/service_worker/service_worker_registration.h"
-#include "content/public/browser/browser_thread.h"
-#include "url/gurl.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "net/base/net_errors.h"
 
 namespace content {
 
+namespace {
+
+void RunSoon(const base::Closure& closure) {
+  base::MessageLoop::current()->PostTask(FROM_HERE, closure);
+}
+
+}  // namespace
+
+typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
+
 ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
-    ServiceWorkerStorage* storage,
-    EmbeddedWorkerRegistry* worker_registry,
-    ServiceWorkerJobCoordinator* coordinator,
+    base::WeakPtr<ServiceWorkerContextCore> context,
     const GURL& pattern,
-    const GURL& script_url,
-    RegistrationType type)
-    : storage_(storage),
-      worker_registry_(worker_registry),
-      coordinator_(coordinator),
-      pending_version_(NULL),
+    const GURL& script_url)
+    : context_(context),
+      job_type_(REGISTRATION_JOB),
       pattern_(pattern),
       script_url_(script_url),
-      type_(type),
+      phase_(INITIAL),
+      is_promise_resolved_(false),
+      promise_resolved_status_(SERVICE_WORKER_OK),
       weak_factory_(this) {}
 
-ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {}
+ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
+    base::WeakPtr<ServiceWorkerContextCore> context,
+    ServiceWorkerRegistration* registration)
+    : context_(context),
+      job_type_(UPDATE_JOB),
+      pattern_(registration->pattern()),
+      script_url_(registration->GetNewestVersion()->script_url()),
+      phase_(INITIAL),
+      is_promise_resolved_(false),
+      promise_resolved_status_(SERVICE_WORKER_OK),
+      weak_factory_(this) {
+  internal_.registration = registration;
+}
 
-void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback,
-                                           int process_id) {
-  // if we've created a pending version, associate source_provider it with
-  // that, otherwise queue it up
-  callbacks_.push_back(callback);
-  DCHECK(process_id != -1);
-  if (pending_version_) {
-    pending_version_->AddProcessToWorker(process_id);
-  } else {
-    pending_process_ids_.push_back(process_id);
+ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
+  DCHECK(!context_ ||
+         phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
+      << "Jobs should only be interrupted during shutdown.";
+}
+
+void ServiceWorkerRegisterJob::AddCallback(
+    const RegistrationCallback& callback,
+    ServiceWorkerProviderHost* provider_host) {
+  if (!is_promise_resolved_) {
+    callbacks_.push_back(callback);
+    if (provider_host)
+      provider_host->AddScopedProcessReferenceToPattern(pattern_);
+    return;
   }
+  RunSoon(base::Bind(
+      callback, promise_resolved_status_,
+      promise_resolved_registration_, promise_resolved_version_));
 }
 
 void ServiceWorkerRegisterJob::Start() {
-  if (type_ == REGISTER)
-    StartRegister();
+  SetPhase(START);
+  ServiceWorkerStorage::FindRegistrationCallback next_step;
+  if (job_type_ == REGISTRATION_JOB) {
+    next_step = base::Bind(
+        &ServiceWorkerRegisterJob::ContinueWithRegistration,
+        weak_factory_.GetWeakPtr());
+  } else {
+    next_step = base::Bind(
+        &ServiceWorkerRegisterJob::ContinueWithUpdate,
+        weak_factory_.GetWeakPtr());
+  }
+
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      context_->storage()->GetUninstallingRegistration(pattern_);
+  if (registration.get())
+    RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration));
   else
-    StartUnregister();
+    context_->storage()->FindRegistrationForPattern(pattern_, next_step);
 }
 
-bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJob* job) {
-  return job->type_ == type_ &&
-         (type_ == ServiceWorkerRegisterJob::UNREGISTER ||
-          job->script_url_ == script_url_);
+void ServiceWorkerRegisterJob::Abort() {
+  SetPhase(ABORT);
+  CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
+  // Don't have to call FinishJob() because the caller takes care of removing
+  // the jobs from the queue.
 }
 
-void ServiceWorkerRegisterJob::StartRegister() {
-  // Set up a chain of callbacks, in reverse order. Each of these
-  // callbacks may be called asynchronously by the previous callback.
-  StatusCallback finish_registration(base::Bind(
-      &ServiceWorkerRegisterJob::RegisterComplete, weak_factory_.GetWeakPtr()));
+bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
+  if (job->GetType() != GetType())
+    return false;
+  ServiceWorkerRegisterJob* register_job =
+      static_cast<ServiceWorkerRegisterJob*>(job);
+  return register_job->pattern_ == pattern_ &&
+         register_job->script_url_ == script_url_;
+}
 
-  RegistrationCallback start_worker(
-      base::Bind(&ServiceWorkerRegisterJob::StartWorkerAndContinue,
-                 weak_factory_.GetWeakPtr(),
-                 finish_registration));
+RegistrationJobType ServiceWorkerRegisterJob::GetType() {
+  return job_type_;
+}
 
-  UnregistrationCallback register_new(
-      base::Bind(&ServiceWorkerRegisterJob::RegisterPatternAndContinue,
-                 weak_factory_.GetWeakPtr(),
-                 start_worker));
+ServiceWorkerRegisterJob::Internal::Internal() {}
 
-  ServiceWorkerStorage::FindRegistrationCallback unregister_old(
-      base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
-                 weak_factory_.GetWeakPtr(),
-                 register_new));
+ServiceWorkerRegisterJob::Internal::~Internal() {}
 
-  storage_->FindRegistrationForPattern(pattern_, unregister_old);
+void ServiceWorkerRegisterJob::set_registration(
+    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+  DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+  DCHECK(!internal_.registration.get());
+  internal_.registration = registration;
 }
 
-void ServiceWorkerRegisterJob::StartUnregister() {
-  // Set up a chain of callbacks, in reverse order. Each of these
-  // callbacks may be called asynchronously by the previous callback.
-  UnregistrationCallback finish_unregistration(
-      base::Bind(&ServiceWorkerRegisterJob::UnregisterComplete,
-                 weak_factory_.GetWeakPtr()));
+ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
+  DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_;
+  return internal_.registration.get();
+}
 
-  ServiceWorkerStorage::FindRegistrationCallback unregister(
-      base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
-                 weak_factory_.GetWeakPtr(),
-                 finish_unregistration));
+void ServiceWorkerRegisterJob::set_new_version(
+    ServiceWorkerVersion* version) {
+  DCHECK(phase_ == UPDATE) << phase_;
+  DCHECK(!internal_.new_version.get());
+  internal_.new_version = version;
+}
 
-  storage_->FindRegistrationForPattern(pattern_, unregister);
+ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
+  DCHECK(phase_ >= UPDATE) << phase_;
+  return internal_.new_version.get();
 }
 
-void ServiceWorkerRegisterJob::StartWorkerAndContinue(
-    const StatusCallback& callback,
-    ServiceWorkerStatusCode status,
+void ServiceWorkerRegisterJob::set_uninstalling_registration(
     const scoped_refptr<ServiceWorkerRegistration>& registration) {
-  if (registration->active_version()) {
-    // We have an active version, so we can complete immediately, even
-    // if the service worker isn't running.
-    callback.Run(registration, SERVICE_WORKER_OK);
+  DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+  internal_.uninstalling_registration = registration;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerRegisterJob::uninstalling_registration() {
+  DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+  return internal_.uninstalling_registration.get();
+}
+
+void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
+  switch (phase) {
+    case INITIAL:
+      NOTREACHED();
+      break;
+    case START:
+      DCHECK(phase_ == INITIAL) << phase_;
+      break;
+    case WAIT_FOR_UNINSTALL:
+      DCHECK(phase_ == START) << phase_;
+      break;
+    case REGISTER:
+      DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << phase_;
+      break;
+    case UPDATE:
+      DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+      break;
+    case INSTALL:
+      DCHECK(phase_ == UPDATE) << phase_;
+      break;
+    case STORE:
+      DCHECK(phase_ == INSTALL) << phase_;
+      break;
+    case COMPLETE:
+      DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
+      break;
+    case ABORT:
+      break;
+  }
+  phase_ = phase;
+}
+
+// This function corresponds to the steps in [[Register]] following
+// "Let registration be the result of running the [[GetRegistration]] algorithm.
+// Throughout this file, comments in quotes are excerpts from the spec.
+void ServiceWorkerRegisterJob::ContinueWithRegistration(
+    ServiceWorkerStatusCode status,
+    const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+  DCHECK_EQ(REGISTRATION_JOB, job_type_);
+  if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
+    Complete(status);
     return;
   }
 
-  pending_version_ = new ServiceWorkerVersion(
-      registration, worker_registry_, registration->next_version_id());
-  for (std::vector<int>::const_iterator it = pending_process_ids_.begin();
-       it != pending_process_ids_.end();
-       ++it)
-    pending_version_->AddProcessToWorker(*it);
+  if (!existing_registration.get() || existing_registration->is_uninstalled()) {
+    RegisterAndContinue(SERVICE_WORKER_OK);
+    return;
+  }
+
+  DCHECK(existing_registration->GetNewestVersion());
+  // "If scriptURL is equal to registration.[[ScriptURL]], then:"
+  if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
+    // "Set registration.[[Uninstalling]] to false."
+    existing_registration->AbortPendingClear(base::Bind(
+        &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
+        weak_factory_.GetWeakPtr(),
+        existing_registration));
+    return;
+  }
+
+  if (existing_registration->is_uninstalling()) {
+    // "Wait until the Record {[[key]], [[value]]} entry of its
+    // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]]
+    // is deleted."
+    WaitForUninstall(existing_registration);
+    return;
+  }
 
-  // The callback to watch "installation" actually fires as soon as
-  // the worker is up and running, just before the install event is
-  // dispatched. The job will continue to run even though the main
-  // callback has executed.
-  pending_version_->StartWorker(base::Bind(callback, registration));
+  // "Set registration.[[Uninstalling]] to false."
+  DCHECK(!existing_registration->is_uninstalling());
 
-  // TODO(alecflett): Don't set the active version until just before
-  // the activate event is dispatched.
-  registration->set_active_version(pending_version_);
+  // "Return the result of running the [[Update]] algorithm, or its equivalent,
+  // passing registration as the argument."
+  set_registration(existing_registration);
+  UpdateAndContinue();
 }
 
-void ServiceWorkerRegisterJob::RegisterPatternAndContinue(
-    const RegistrationCallback& callback,
-    ServiceWorkerStatusCode previous_status) {
-  if (previous_status != SERVICE_WORKER_OK) {
-    BrowserThread::PostTask(
-        BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(callback,
-                   previous_status,
-                   scoped_refptr<ServiceWorkerRegistration>()));
+void ServiceWorkerRegisterJob::ContinueWithUpdate(
+    ServiceWorkerStatusCode status,
+    const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+  DCHECK_EQ(UPDATE_JOB, job_type_);
+  if (status != SERVICE_WORKER_OK) {
+    Complete(status);
     return;
   }
 
-  // TODO: Eventually RegisterInternal will be replaced by an asynchronous
-  // operation. Pass its resulting status through 'callback'.
-  scoped_refptr<ServiceWorkerRegistration> registration =
-      storage_->RegisterInternal(pattern_, script_url_);
-
-  BrowserThread::PostTask(BrowserThread::IO,
-                          FROM_HERE,
-                          base::Bind(callback, SERVICE_WORKER_OK,
-                                     registration));
-}
-
-void ServiceWorkerRegisterJob::UnregisterPatternAndContinue(
-    const UnregistrationCallback& callback,
-    bool found,
-    ServiceWorkerStatusCode previous_status,
-    const scoped_refptr<ServiceWorkerRegistration>& previous_registration) {
-
-  // The previous registration may not exist, which is ok.
-  if (previous_status == SERVICE_WORKER_OK && found &&
-      (script_url_.is_empty() ||
-       previous_registration->script_url() != script_url_)) {
-    // TODO: Eventually UnregisterInternal will be replaced by an
-    // asynchronous operation. Pass its resulting status though
-    // 'callback'.
-    storage_->UnregisterInternal(pattern_);
-    DCHECK(previous_registration->is_shutdown());
-  } else {
-    // TODO(alecflett): We have an existing registration, we should
-    // schedule an update.
+  if (existing_registration.get() != registration()) {
+    Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
+    return;
+  }
+
+  // A previous job may have unregistered or installed a new version to this
+  // registration.
+  if (registration()->is_uninstalling() ||
+      registration()->GetNewestVersion()->script_url() != script_url_) {
+    Complete(SERVICE_WORKER_ERROR_NOT_FOUND);
+    return;
+  }
+
+  // TODO(michaeln): If the last update check was less than 24 hours
+  // ago, depending on the freshness of the cached worker script we
+  // may be able to complete the update job right here.
+
+  UpdateAndContinue();
+}
+
+// Creates a new ServiceWorkerRegistration.
+void ServiceWorkerRegisterJob::RegisterAndContinue(
+    ServiceWorkerStatusCode status) {
+  SetPhase(REGISTER);
+  if (status != SERVICE_WORKER_OK) {
+    // Abort this registration job.
+    Complete(status);
+    return;
+  }
+
+  set_registration(new ServiceWorkerRegistration(
+      pattern_, context_->storage()->NewRegistrationId(), context_));
+  AssociateProviderHostsToRegistration(registration());
+  UpdateAndContinue();
+}
+
+void ServiceWorkerRegisterJob::WaitForUninstall(
+    const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+  SetPhase(WAIT_FOR_UNINSTALL);
+  set_uninstalling_registration(existing_registration);
+  uninstalling_registration()->AddListener(this);
+}
+
+void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
+    const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+    ServiceWorkerStatusCode status) {
+  if (status != SERVICE_WORKER_OK) {
+    Complete(status);
+    return;
+  }
+  set_registration(existing_registration);
+
+  // TODO(falken): Follow the spec: resolve the promise
+  // with the newest version.
+
+  if (!existing_registration->active_version()) {
+    UpdateAndContinue();
+    return;
+  }
+
+  ResolvePromise(status,
+                 existing_registration.get(),
+                 existing_registration->active_version());
+  Complete(SERVICE_WORKER_OK);
+}
+
+// This function corresponds to the spec's [[Update]] algorithm.
+void ServiceWorkerRegisterJob::UpdateAndContinue() {
+  SetPhase(UPDATE);
+  context_->storage()->NotifyInstallingRegistration(registration());
+
+  // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.."
+  // then terminate the installing worker. It doesn't make sense to implement
+  // yet since we always activate the worker if install completed, so there can
+  // be no installing worker at this point.
+
+  // "Let serviceWorker be a newly-created ServiceWorker object..." and start
+  // the worker.
+  set_new_version(new ServiceWorkerVersion(registration(),
+                                           script_url_,
+                                           context_->storage()->NewVersionId(),
+                                           context_));
+
+  bool pause_after_download = job_type_ == UPDATE_JOB;
+  if (pause_after_download)
+    new_version()->embedded_worker()->AddListener(this);
+  new_version()->StartWorker(
+      pause_after_download,
+      base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnStartWorkerFinished(
+    ServiceWorkerStatusCode status) {
+  if (status == SERVICE_WORKER_OK) {
+    InstallAndContinue();
+    return;
+  }
+
+  // "If serviceWorker fails to start up..." then reject the promise with an
+  // error and abort. When there is a main script network error, the status will
+  // be updated to a more specific one.
+  const net::URLRequestStatus& main_script_status =
+      new_version()->script_cache_map()->main_script_status();
+  if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
+    switch (main_script_status.error()) {
+      case net::ERR_INSECURE_RESPONSE:
+      case net::ERR_UNSAFE_REDIRECT:
+        status = SERVICE_WORKER_ERROR_SECURITY;
+        break;
+      case net::ERR_ABORTED:
+        status = SERVICE_WORKER_ERROR_ABORT;
+        break;
+      case net::ERR_FAILED:
+        status = SERVICE_WORKER_ERROR_NETWORK;
+        break;
+      default:
+        NOTREACHED();
+    }
   }
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE, base::Bind(callback, previous_status));
+  Complete(status);
 }
 
-void ServiceWorkerRegisterJob::RunCallbacks(
+// This function corresponds to the spec's [[Install]] algorithm.
+void ServiceWorkerRegisterJob::InstallAndContinue() {
+  SetPhase(INSTALL);
+
+  // "2. Set registration.installingWorker to worker."
+  registration()->SetInstallingVersion(new_version());
+
+  // "3. Resolve promise with registration."
+  ResolvePromise(SERVICE_WORKER_OK, registration(), new_version());
+
+  // "4. Run the [[UpdateState]] algorithm passing registration.installingWorker
+  // and "installing" as the arguments."
+  new_version()->SetStatus(ServiceWorkerVersion::INSTALLING);
+
+  // "5. Fire a simple event named updatefound..."
+  registration()->NotifyUpdateFound();
+
+  // "6. Fire an event named install..."
+  new_version()->DispatchInstallEvent(
+      -1,
+      base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnInstallFinished(
+    ServiceWorkerStatusCode status) {
+  // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
+  // unexpectedly terminated) we may want to retry sending the event again.
+  if (status != SERVICE_WORKER_OK) {
+    // "8. If installFailed is true, then:..."
+    Complete(status);
+    return;
+  }
+
+  SetPhase(STORE);
+  registration()->set_last_update_check(base::Time::Now());
+  context_->storage()->StoreRegistration(
+      registration(),
+      new_version(),
+      base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
+    ServiceWorkerStatusCode status) {
+  if (status != SERVICE_WORKER_OK) {
+    Complete(status);
+    return;
+  }
+
+  // "9. If registration.waitingWorker is not null, then:..."
+  if (registration()->waiting_version()) {
+    // "1. Run the [[UpdateState]] algorithm passing registration.waitingWorker
+    // and "redundant" as the arguments."
+    registration()->waiting_version()->SetStatus(
+        ServiceWorkerVersion::REDUNDANT);
+  }
+
+  // "10. Set registration.waitingWorker to registration.installingWorker."
+  // "11. Set registration.installingWorker to null."
+  registration()->SetWaitingVersion(new_version());
+
+  // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker
+  // and "installed" as the arguments."
+  new_version()->SetStatus(ServiceWorkerVersion::INSTALLED);
+
+  // TODO(michaeln): "13. If activateImmediate is true, then..."
+
+  // "14. Wait until no document is using registration as their
+  // Service Worker registration."
+  registration()->ActivateWaitingVersionWhenReady();
+
+  Complete(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
+  CompleteInternal(status);
+  context_->job_coordinator()->FinishJob(pattern_, this);
+}
+
+void ServiceWorkerRegisterJob::CompleteInternal(
+    ServiceWorkerStatusCode status) {
+  SetPhase(COMPLETE);
+  if (status != SERVICE_WORKER_OK) {
+    if (registration()) {
+      if (new_version()) {
+        registration()->UnsetVersion(new_version());
+        new_version()->Doom();
+      }
+      if (!registration()->waiting_version() &&
+          !registration()->active_version()) {
+        registration()->NotifyRegistrationFailed();
+        context_->storage()->DeleteRegistration(
+            registration()->id(),
+            registration()->pattern().GetOrigin(),
+            base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+      }
+    }
+    if (!is_promise_resolved_)
+      ResolvePromise(status, NULL, NULL);
+  }
+  DCHECK(callbacks_.empty());
+  if (registration()) {
+    context_->storage()->NotifyDoneInstallingRegistration(
+        registration(), new_version(), status);
+  }
+  if (new_version())
+    new_version()->embedded_worker()->RemoveListener(this);
+}
+
+void ServiceWorkerRegisterJob::ResolvePromise(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    ServiceWorkerRegistration* registration,
+    ServiceWorkerVersion* version) {
+  DCHECK(!is_promise_resolved_);
+  is_promise_resolved_ = true;
+  promise_resolved_status_ = status;
+  promise_resolved_registration_ = registration;
+  promise_resolved_version_ = version;
   for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
        it != callbacks_.end();
        ++it) {
-    it->Run(status, registration);
+    it->Run(status, registration, version);
   }
+  callbacks_.clear();
 }
 
-void ServiceWorkerRegisterJob::RegisterComplete(
-    const scoped_refptr<ServiceWorkerRegistration>& registration,
-    ServiceWorkerStatusCode start_status) {
-  RunCallbacks(start_status, registration);
-  coordinator_->FinishJob(pattern_, this);
+void ServiceWorkerRegisterJob::OnPausedAfterDownload() {
+  // This happens prior to OnStartWorkerFinished time.
+  scoped_refptr<ServiceWorkerVersion> most_recent_version =
+      registration()->waiting_version() ?
+          registration()->waiting_version() :
+          registration()->active_version();
+  DCHECK(most_recent_version.get());
+  int64 most_recent_script_id =
+      most_recent_version->script_cache_map()->Lookup(script_url_);
+  int64 new_script_id =
+      new_version()->script_cache_map()->Lookup(script_url_);
+
+  // TODO(michaeln): It would be better to compare as the new resource
+  // is being downloaded and to avoid writing it to disk until we know
+  // its needed.
+  context_->storage()->CompareScriptResources(
+      most_recent_script_id, new_script_id,
+      base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete,
+                 weak_factory_.GetWeakPtr(),
+                 most_recent_version));
 }
 
-void ServiceWorkerRegisterJob::UnregisterComplete(
-    ServiceWorkerStatusCode status) {
-  RunCallbacks(status, NULL);
-  coordinator_->FinishJob(pattern_, this);
+bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
+  return false;
+}
+
+void ServiceWorkerRegisterJob::OnRegistrationFinishedUninstalling(
+    ServiceWorkerRegistration* existing_registration) {
+  DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
+  DCHECK_EQ(existing_registration, uninstalling_registration());
+  existing_registration->RemoveListener(this);
+  set_uninstalling_registration(NULL);
+  RegisterAndContinue(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
+    ServiceWorkerVersion* most_recent_version,
+    ServiceWorkerStatusCode status,
+    bool are_equal) {
+  if (are_equal) {
+    // Only bump the last check time when we've bypassed the browser cache.
+    base::TimeDelta time_since_last_check =
+        base::Time::Now() - registration()->last_update_check();
+    if (time_since_last_check > base::TimeDelta::FromHours(24)) {
+      registration()->set_last_update_check(base::Time::Now());
+      context_->storage()->UpdateLastUpdateCheckTime(registration());
+    }
+
+    ResolvePromise(SERVICE_WORKER_OK, registration(), most_recent_version);
+    Complete(SERVICE_WORKER_ERROR_EXISTS);
+    return;
+  }
+
+  // Proceed with really starting the worker.
+  new_version()->embedded_worker()->ResumeAfterDownload();
+  new_version()->embedded_worker()->RemoveListener(this);
+}
+
+void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration(
+    ServiceWorkerRegistration* registration) {
+  DCHECK(registration);
+  for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
+           context_->GetProviderHostIterator();
+       !it->IsAtEnd(); it->Advance()) {
+    ServiceWorkerProviderHost* host = it->GetProviderHost();
+    if (ServiceWorkerUtils::ScopeMatches(registration->pattern(),
+                                         host->document_url())) {
+      if (host->CanAssociateRegistration(registration))
+        host->AssociateRegistration(registration);
+    }
+  }
 }
 
 }  // namespace content