ServiceWorker: Make '.ready' return a promise to be resolved with ServiceWorkerRegistration (2/3)

Potential controllees need to observe version change events to make a ready
registration, but, before this patch, those events are sent not to potential
controllees but to pages that have the corresponding registration object
(to be more precise, pages that have the reference to the registration).

After this patch, ServiceWorkerProviderHost sends an IPC message to the
corresponding ServiceWorkerProviderContext when the host gets associated with
a registration and the provider context retains a reference of the registration
so that the potential controllee can observe the version change events.

1) Blink: https://codereview.chromium.org/476043002/
2) Chromium: THIS PATCH
3) Blink: https://codereview.chromium.org/532653002/

BUG=399533
TEST=content_unittests --gtest_filter=ServiceWorker*
TEST=run_webkit_tests.py --debug http/tests/serviceworker/ (with the patch (3))

Review URL: https://codereview.chromium.org/477593007

Cr-Commit-Position: refs/heads/master@{#294147}
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 0db1289..c290ee66 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -114,7 +114,7 @@
       "URL", url.spec());
   // The corresponding provider_host may already have associated a registration
   // in redirect case, unassociate it now.
-  provider_host_->UnassociateRegistration();
+  provider_host_->DisassociateRegistration();
 
   GURL stripped_url = net::SimplifyUrlForRequest(url);
   provider_host_->SetDocumentUrl(stripped_url);
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 4cc054a7..7d887c5 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -60,7 +60,7 @@
 void ServiceWorkerProviderHost::OnRegistrationFailed(
     ServiceWorkerRegistration* registration) {
   DCHECK_EQ(associated_registration_.get(), registration);
-  UnassociateRegistration();
+  DisassociateRegistration();
 }
 
 void ServiceWorkerProviderHost::SetDocumentUrl(const GURL& url) {
@@ -116,15 +116,32 @@
     DecreaseProcessReference(associated_registration_->pattern());
   IncreaseProcessReference(registration->pattern());
 
+  if (dispatcher_host_) {
+    ServiceWorkerRegistrationHandle* handle =
+        dispatcher_host_->GetOrCreateRegistrationHandle(
+            provider_id(), registration);
+
+    ServiceWorkerVersionAttributes attrs;
+    attrs.installing = handle->CreateServiceWorkerHandleAndPass(
+        registration->installing_version());
+    attrs.waiting = handle->CreateServiceWorkerHandleAndPass(
+        registration->waiting_version());
+    attrs.active = handle->CreateServiceWorkerHandleAndPass(
+        registration->active_version());
+
+    dispatcher_host_->Send(new ServiceWorkerMsg_AssociateRegistration(
+        kDocumentMainThreadId, provider_id(), handle->GetObjectInfo(), attrs));
+  }
+
   associated_registration_ = registration;
-  registration->AddListener(this);
+  associated_registration_->AddListener(this);
   installing_version_ = registration->installing_version();
   waiting_version_ = registration->waiting_version();
   active_version_ = registration->active_version();
   SetControllerVersionAttribute(registration->active_version());
 }
 
-void ServiceWorkerProviderHost::UnassociateRegistration() {
+void ServiceWorkerProviderHost::DisassociateRegistration() {
   if (!associated_registration_.get())
     return;
   DecreaseProcessReference(associated_registration_->pattern());
@@ -134,6 +151,11 @@
   waiting_version_ = NULL;
   active_version_ = NULL;
   SetControllerVersionAttribute(NULL);
+
+  if (dispatcher_host_) {
+    dispatcher_host_->Send(new ServiceWorkerMsg_DisassociateRegistration(
+        kDocumentMainThreadId, provider_id()));
+  }
 }
 
 scoped_ptr<ServiceWorkerRequestHandler>
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index a005e0aa..7551e31 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -83,7 +83,7 @@
   void AssociateRegistration(ServiceWorkerRegistration* registration);
 
   // Clears the associated registration and stop listening to it.
-  void UnassociateRegistration();
+  void DisassociateRegistration();
 
   // Returns false if the version is not in the expected STARTING in our
   // process state. That would be indicative of a bad IPC message.
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 181ecf707..a4af6ca 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -97,7 +97,7 @@
 
   // Resetting the provider_host's active version should remove process refs
   // from the version.
-  provider_host1_->UnassociateRegistration();
+  provider_host1_->DisassociateRegistration();
   ASSERT_FALSE(HasProcessToRun());
 }
 
@@ -113,11 +113,11 @@
 
   // Disassociating one provider_host shouldn't remove all process refs
   // from the version yet.
-  provider_host1_->UnassociateRegistration();
+  provider_host1_->DisassociateRegistration();
   ASSERT_TRUE(HasProcessToRun());
 
   // Disassociating the other provider_host will remove all process refs.
-  provider_host2_->UnassociateRegistration();
+  provider_host2_->DisassociateRegistration();
   ASSERT_FALSE(HasProcessToRun());
 }
 
@@ -136,7 +136,7 @@
 
   // Resetting the provider_host's waiting version should remove process refs
   // from the version.
-  provider_host1_->UnassociateRegistration();
+  provider_host1_->DisassociateRegistration();
   ASSERT_FALSE(HasProcessToRun());
 }
 
@@ -152,11 +152,11 @@
 
   // Disassociating one provider_host shouldn't remove all process refs
   // from the version yet.
-  provider_host1_->UnassociateRegistration();
+  provider_host1_->DisassociateRegistration();
   ASSERT_TRUE(HasProcessToRun());
 
   // Disassociating the other provider_host will remove all process refs.
-  provider_host2_->UnassociateRegistration();
+  provider_host2_->DisassociateRegistration();
   ASSERT_FALSE(HasProcessToRun());
 }
 
@@ -176,7 +176,7 @@
   VerifyVersionAttributes(provider_host2_, NULL, version_.get(), NULL);
 
   // Disassociating the registration should clear all version attributes.
-  provider_host2_->UnassociateRegistration();
+  provider_host2_->DisassociateRegistration();
   VerifyVersionAttributes(provider_host1_, NULL, version_.get(), NULL);
   VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL);
 
@@ -213,7 +213,7 @@
       provider_host2_, version2.get(), version1.get(), NULL);
 
   // Disassociating the registration should clear all version attributes.
-  provider_host2_->UnassociateRegistration();
+  provider_host2_->DisassociateRegistration();
   VerifyVersionAttributes(
       provider_host1_, version2.get(), version1.get(), NULL);
   VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL);
diff --git a/content/child/service_worker/service_worker_dispatcher.cc b/content/child/service_worker/service_worker_dispatcher.cc
index d4e404b..df616df 100644
--- a/content/child/service_worker/service_worker_dispatcher.cc
+++ b/content/child/service_worker/service_worker_dispatcher.cc
@@ -54,6 +54,10 @@
 void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message& msg) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher, msg)
+    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_AssociateRegistration,
+                        OnAssociateRegistration)
+    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DisassociateRegistration,
+                        OnDisassociateRegistration)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnRegistered)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered,
                         OnUnregistered)
@@ -219,25 +223,30 @@
 }
 
 WebServiceWorkerRegistrationImpl*
-ServiceWorkerDispatcher::GetServiceWorkerRegistration(
+ServiceWorkerDispatcher::FindServiceWorkerRegistration(
     const ServiceWorkerRegistrationObjectInfo& info,
     bool adopt_handle) {
+  RegistrationObjectMap::iterator registration =
+      registrations_.find(info.handle_id);
+  if (registration == registrations_.end())
+    return NULL;
+  if (adopt_handle) {
+    // We are instructed to adopt a handle but we already have one, so
+    // adopt and destroy a handle ref.
+    ServiceWorkerRegistrationHandleReference::Adopt(
+        info, thread_safe_sender_.get());
+  }
+  return registration->second;
+}
+
+WebServiceWorkerRegistrationImpl*
+ServiceWorkerDispatcher::CreateServiceWorkerRegistration(
+    const ServiceWorkerRegistrationObjectInfo& info,
+    bool adopt_handle) {
+  DCHECK(!FindServiceWorkerRegistration(info, adopt_handle));
   if (info.handle_id == kInvalidServiceWorkerRegistrationHandleId)
     return NULL;
 
-  RegistrationObjectMap::iterator existing_registration =
-      registrations_.find(info.handle_id);
-
-  if (existing_registration != registrations_.end()) {
-    if (adopt_handle) {
-      // We are instructed to adopt a handle but we already have one, so
-      // adopt and destroy a handle ref.
-      ServiceWorkerRegistrationHandleReference::Adopt(
-          info, thread_safe_sender_.get());
-    }
-    return existing_registration->second;
-  }
-
   scoped_ptr<ServiceWorkerRegistrationHandleReference> handle_ref =
       adopt_handle ? ServiceWorkerRegistrationHandleReference::Adopt(
                          info, thread_safe_sender_.get())
@@ -249,6 +258,36 @@
   return new WebServiceWorkerRegistrationImpl(handle_ref.Pass());
 }
 
+void ServiceWorkerDispatcher::OnAssociateRegistration(
+    int thread_id,
+    int provider_id,
+    const ServiceWorkerRegistrationObjectInfo& info,
+    const ServiceWorkerVersionAttributes& attrs) {
+  ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
+  if (provider == provider_contexts_.end())
+    return;
+  provider->second->OnAssociateRegistration(info, attrs);
+  if (attrs.installing.handle_id != kInvalidServiceWorkerHandleId)
+    worker_to_provider_[attrs.installing.handle_id] = provider->second;
+  if (attrs.waiting.handle_id != kInvalidServiceWorkerHandleId)
+    worker_to_provider_[attrs.waiting.handle_id] = provider->second;
+  if (attrs.active.handle_id != kInvalidServiceWorkerHandleId)
+    worker_to_provider_[attrs.active.handle_id] = provider->second;
+}
+
+void ServiceWorkerDispatcher::OnDisassociateRegistration(
+    int thread_id,
+    int provider_id) {
+  ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
+  if (provider == provider_contexts_.end())
+    return;
+  provider->second->OnDisassociateRegistration();
+  worker_to_provider_.erase(provider->second->installing_handle_id());
+  worker_to_provider_.erase(provider->second->waiting_handle_id());
+  worker_to_provider_.erase(provider->second->active_handle_id());
+  worker_to_provider_.erase(provider->second->controller_handle_id());
+}
+
 void ServiceWorkerDispatcher::OnRegistered(
     int thread_id,
     int request_id,
@@ -265,10 +304,22 @@
     return;
 
   WebServiceWorkerRegistrationImpl* registration =
-      GetServiceWorkerRegistration(info, true);
-  registration->SetInstalling(GetServiceWorker(attrs.installing, true));
-  registration->SetWaiting(GetServiceWorker(attrs.waiting, true));
-  registration->SetActive(GetServiceWorker(attrs.active, true));
+      FindServiceWorkerRegistration(info, true);
+  if (!registration) {
+    registration = CreateServiceWorkerRegistration(info, true);
+    registration->SetInstalling(GetServiceWorker(attrs.installing, true));
+    registration->SetWaiting(GetServiceWorker(attrs.waiting, true));
+    registration->SetActive(GetServiceWorker(attrs.active, true));
+  } else {
+    // |registration| must already have version attributes, so adopt and destroy
+    // handle refs for them.
+    ServiceWorkerHandleReference::Adopt(
+        attrs.installing, thread_safe_sender_.get());
+    ServiceWorkerHandleReference::Adopt(
+        attrs.waiting, thread_safe_sender_.get());
+    ServiceWorkerHandleReference::Adopt(
+        attrs.active, thread_safe_sender_.get());
+  }
 
   callbacks->onSuccess(registration);
   pending_registration_callbacks_.Remove(request_id);
@@ -387,6 +438,7 @@
     SetActiveServiceWorker(provider_id,
                            registration_handle_id,
                            attributes.active);
+    SetReadyRegistration(provider_id, registration_handle_id);
   }
 }
 
@@ -405,7 +457,8 @@
     int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
   ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
-  if (provider != provider_contexts_.end()) {
+  if (provider != provider_contexts_.end() &&
+      provider->second->registration_handle_id() == registration_handle_id) {
     int existing_installing_id = provider->second->installing_handle_id();
     if (existing_installing_id != info.handle_id &&
         existing_installing_id != kInvalidServiceWorkerHandleId) {
@@ -415,7 +468,8 @@
       DCHECK(associated_provider->second->provider_id() == provider_id);
       worker_to_provider_.erase(associated_provider);
     }
-    provider->second->OnSetInstallingServiceWorker(provider_id, info);
+    provider->second->OnSetInstallingServiceWorker(
+        registration_handle_id, info);
     if (info.handle_id != kInvalidServiceWorkerHandleId)
       worker_to_provider_[info.handle_id] = provider->second;
   }
@@ -433,7 +487,8 @@
     int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
   ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
-  if (provider != provider_contexts_.end()) {
+  if (provider != provider_contexts_.end() &&
+      provider->second->registration_handle_id() == registration_handle_id) {
     int existing_waiting_id = provider->second->waiting_handle_id();
     if (existing_waiting_id != info.handle_id &&
         existing_waiting_id != kInvalidServiceWorkerHandleId) {
@@ -443,7 +498,7 @@
       DCHECK(associated_provider->second->provider_id() == provider_id);
       worker_to_provider_.erase(associated_provider);
     }
-    provider->second->OnSetWaitingServiceWorker(provider_id, info);
+    provider->second->OnSetWaitingServiceWorker(registration_handle_id, info);
     if (info.handle_id != kInvalidServiceWorkerHandleId)
       worker_to_provider_[info.handle_id] = provider->second;
   }
@@ -461,7 +516,8 @@
     int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
   ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
-  if (provider != provider_contexts_.end()) {
+  if (provider != provider_contexts_.end() &&
+      provider->second->registration_handle_id() == registration_handle_id) {
     int existing_active_id = provider->second->active_handle_id();
     if (existing_active_id != info.handle_id &&
         existing_active_id != kInvalidServiceWorkerHandleId) {
@@ -471,7 +527,7 @@
       DCHECK(associated_provider->second->provider_id() == provider_id);
       worker_to_provider_.erase(associated_provider);
     }
-    provider->second->OnSetActiveServiceWorker(provider_id, info);
+    provider->second->OnSetActiveServiceWorker(registration_handle_id, info);
     if (info.handle_id != kInvalidServiceWorkerHandleId)
       worker_to_provider_[info.handle_id] = provider->second;
   }
@@ -484,6 +540,37 @@
   }
 }
 
+void ServiceWorkerDispatcher::SetReadyRegistration(
+    int provider_id,
+    int registration_handle_id) {
+  ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
+  if (provider == provider_contexts_.end() ||
+      provider->second->registration_handle_id() != registration_handle_id ||
+      provider->second->active_handle_id() == kInvalidServiceWorkerHandleId) {
+    return;
+  }
+
+  ScriptClientMap::iterator client = script_clients_.find(provider_id);
+  if (client == script_clients_.end())
+    return;
+
+  ServiceWorkerRegistrationObjectInfo info =
+      provider->second->registration()->info();
+  WebServiceWorkerRegistrationImpl* registration =
+      FindServiceWorkerRegistration(info, false);
+  if (!registration) {
+    registration = CreateServiceWorkerRegistration(info, false);
+    ServiceWorkerVersionAttributes attrs =
+        provider->second->GetVersionAttributes();
+    registration->SetInstalling(GetServiceWorker(attrs.installing, false));
+    registration->SetWaiting(GetServiceWorker(attrs.waiting, false));
+    registration->SetActive(GetServiceWorker(attrs.active, false));
+  }
+
+  // Resolve the .ready promise with the registration object.
+  client->second->setReadyRegistration(registration);
+}
+
 void ServiceWorkerDispatcher::OnSetControllerServiceWorker(
     int thread_id,
     int provider_id,
@@ -494,7 +581,8 @@
                "Provider ID", provider_id);
   ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
   if (provider != provider_contexts_.end()) {
-    provider->second->OnSetControllerServiceWorker(provider_id, info);
+    provider->second->OnSetControllerServiceWorker(
+        provider->second->registration_handle_id(), info);
     worker_to_provider_[info.handle_id] = provider->second;
   }
 
diff --git a/content/child/service_worker/service_worker_dispatcher.h b/content/child/service_worker/service_worker_dispatcher.h
index 61d66d0..80ae40a 100644
--- a/content/child/service_worker/service_worker_dispatcher.h
+++ b/content/child/service_worker/service_worker_dispatcher.h
@@ -96,12 +96,19 @@
       const ServiceWorkerObjectInfo& info,
       bool adopt_handle);
 
-  // If an existing WebServiceWorkerRegistrationImpl exists for the
-  // registration, it is returned; otherwise a WebServiceWorkerRegistrationImpl
-  // is created and its ownership is transferred to the caller. If
-  // |adopt_handle| is true, a ServiceWorkerRegistrationHandleReference will be
-  // adopted for the specified registration.
-  WebServiceWorkerRegistrationImpl* GetServiceWorkerRegistration(
+  // Finds a WebServiceWorkerRegistrationImpl for the specified registration.
+  // If it's not found, returns NULL. If |adopt_handle| is true,
+  // a ServiceWorkerRegistrationHandleReference will be adopted for the
+  // registration.
+  WebServiceWorkerRegistrationImpl* FindServiceWorkerRegistration(
+      const ServiceWorkerRegistrationObjectInfo& info,
+      bool adopt_handle);
+
+  // Creates a WebServiceWorkerRegistrationImpl for the specified registration
+  // and transfers its ownership to the caller. If |adopt_handle| is true, a
+  // ServiceWorkerRegistrationHandleReference will be adopted for the
+  // registration.
+  WebServiceWorkerRegistrationImpl* CreateServiceWorkerRegistration(
       const ServiceWorkerRegistrationObjectInfo& info,
       bool adopt_handle);
 
@@ -132,6 +139,12 @@
   // WorkerTaskRunner::Observer implementation.
   virtual void OnWorkerRunLoopStopped() OVERRIDE;
 
+  void OnAssociateRegistration(int thread_id,
+                               int provider_id,
+                               const ServiceWorkerRegistrationObjectInfo& info,
+                               const ServiceWorkerVersionAttributes& attrs);
+  void OnDisassociateRegistration(int thread_id,
+                                  int provider_id);
   void OnRegistered(int thread_id,
                     int request_id,
                     const ServiceWorkerRegistrationObjectInfo& info,
@@ -178,6 +191,9 @@
       int provider_id,
       int registration_handle_id,
       const ServiceWorkerObjectInfo& info);
+  void SetReadyRegistration(
+      int provider_id,
+      int registration_handle_id);
 
   // Keeps map from handle_id to ServiceWorker object.
   void AddServiceWorker(int handle_id, WebServiceWorkerImpl* worker);
diff --git a/content/child/service_worker/service_worker_provider_context.cc b/content/child/service_worker/service_worker_provider_context.cc
index 3dfcff84..cb86fa56 100644
--- a/content/child/service_worker/service_worker_provider_context.cc
+++ b/content/child/service_worker/service_worker_provider_context.cc
@@ -10,6 +10,7 @@
 #include "content/child/child_thread.h"
 #include "content/child/service_worker/service_worker_dispatcher.h"
 #include "content/child/service_worker/service_worker_handle_reference.h"
+#include "content/child/service_worker/service_worker_registration_handle_reference.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/child/worker_task_runner.h"
 #include "content/common/service_worker/service_worker_messages.h"
@@ -56,6 +57,47 @@
   return controller_.get();
 }
 
+ServiceWorkerRegistrationHandleReference*
+ServiceWorkerProviderContext::registration() {
+  DCHECK(main_thread_loop_proxy_->RunsTasksOnCurrentThread());
+  return registration_.get();
+}
+
+ServiceWorkerVersionAttributes
+ServiceWorkerProviderContext::GetVersionAttributes() {
+  ServiceWorkerVersionAttributes attrs;
+  if (installing())
+    attrs.installing = installing()->info();
+  if (waiting())
+    attrs.waiting = waiting()->info();
+  if (active())
+    attrs.active = active()->info();
+  return attrs;
+}
+
+void ServiceWorkerProviderContext::OnAssociateRegistration(
+    const ServiceWorkerRegistrationObjectInfo& info,
+    const ServiceWorkerVersionAttributes& attrs) {
+  DCHECK(!registration_);
+  DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId, info.handle_id);
+  registration_ = ServiceWorkerRegistrationHandleReference::Adopt(
+      info, thread_safe_sender_.get());
+  installing_ = ServiceWorkerHandleReference::Adopt(
+      attrs.installing, thread_safe_sender_.get());
+  waiting_ = ServiceWorkerHandleReference::Adopt(
+      attrs.waiting, thread_safe_sender_.get());
+  active_ = ServiceWorkerHandleReference::Adopt(
+      attrs.active, thread_safe_sender_.get());
+}
+
+void ServiceWorkerProviderContext::OnDisassociateRegistration() {
+  controller_.reset();
+  active_.reset();
+  waiting_.reset();
+  installing_.reset();
+  registration_.reset();
+}
+
 void ServiceWorkerProviderContext::OnServiceWorkerStateChanged(
     int handle_id,
     blink::WebServiceWorkerState state) {
@@ -80,33 +122,33 @@
 }
 
 void ServiceWorkerProviderContext::OnSetInstallingServiceWorker(
-    int provider_id,
+    int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
-  DCHECK_EQ(provider_id_, provider_id);
+  DCHECK(IsAssociatedWithRegistration(registration_handle_id));
   installing_ =
       ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_.get());
 }
 
 void ServiceWorkerProviderContext::OnSetWaitingServiceWorker(
-    int provider_id,
+    int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
-  DCHECK_EQ(provider_id_, provider_id);
+  DCHECK(IsAssociatedWithRegistration(registration_handle_id));
   waiting_ =
       ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_.get());
 }
 
 void ServiceWorkerProviderContext::OnSetActiveServiceWorker(
-    int provider_id,
+    int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
-  DCHECK_EQ(provider_id_, provider_id);
+  DCHECK(IsAssociatedWithRegistration(registration_handle_id));
   active_ =
       ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_.get());
 }
 
 void ServiceWorkerProviderContext::OnSetControllerServiceWorker(
-    int provider_id,
+    int registration_handle_id,
     const ServiceWorkerObjectInfo& info) {
-  DCHECK_EQ(provider_id_, provider_id);
+  DCHECK(IsAssociatedWithRegistration(registration_handle_id));
 
   // This context is is the primary owner of this handle, keeps the
   // initial reference until it goes away.
@@ -141,4 +183,19 @@
                      : kInvalidServiceWorkerHandleId;
 }
 
+int ServiceWorkerProviderContext::registration_handle_id() const {
+  DCHECK(main_thread_loop_proxy_->RunsTasksOnCurrentThread());
+  return registration_ ? registration_->info().handle_id
+                       : kInvalidServiceWorkerRegistrationHandleId;
+}
+
+bool ServiceWorkerProviderContext::IsAssociatedWithRegistration(
+    int registration_handle_id) const {
+  if (!registration_)
+    return false;
+  if (registration_handle_id == kInvalidServiceWorkerRegistrationHandleId)
+    return false;
+  return registration_->info().handle_id == registration_handle_id;
+}
+
 }  // namespace content
diff --git a/content/child/service_worker/service_worker_provider_context.h b/content/child/service_worker/service_worker_provider_context.h
index ee294d99..75348bb 100644
--- a/content/child/service_worker/service_worker_provider_context.h
+++ b/content/child/service_worker/service_worker_provider_context.h
@@ -24,6 +24,7 @@
 namespace content {
 
 class ServiceWorkerHandleReference;
+class ServiceWorkerRegistrationHandleReference;
 struct ServiceWorkerProviderContextDeleter;
 class ThreadSafeSender;
 
@@ -38,15 +39,18 @@
   explicit ServiceWorkerProviderContext(int provider_id);
 
   // Called from ServiceWorkerDispatcher.
+  void OnAssociateRegistration(const ServiceWorkerRegistrationObjectInfo& info,
+                               const ServiceWorkerVersionAttributes& attrs);
+  void OnDisassociateRegistration();
   void OnServiceWorkerStateChanged(int handle_id,
                                    blink::WebServiceWorkerState state);
-  void OnSetInstallingServiceWorker(int provider_id,
+  void OnSetInstallingServiceWorker(int registration_handle_id,
                                     const ServiceWorkerObjectInfo& info);
-  void OnSetWaitingServiceWorker(int provider_id,
+  void OnSetWaitingServiceWorker(int registration_handle_id,
                                  const ServiceWorkerObjectInfo& info);
-  void OnSetActiveServiceWorker(int provider_id,
+  void OnSetActiveServiceWorker(int registration_handle_id,
                                 const ServiceWorkerObjectInfo& info);
-  void OnSetControllerServiceWorker(int provider_id,
+  void OnSetControllerServiceWorker(int registration_handle_id,
                                     const ServiceWorkerObjectInfo& info);
 
   int provider_id() const { return provider_id_; }
@@ -55,6 +59,9 @@
   ServiceWorkerHandleReference* waiting();
   ServiceWorkerHandleReference* active();
   ServiceWorkerHandleReference* controller();
+  ServiceWorkerRegistrationHandleReference* registration();
+
+  ServiceWorkerVersionAttributes GetVersionAttributes();
 
   // Gets the handle ID of the installing Service Worker, or
   // kInvalidServiceWorkerHandleId if the provider does not have a
@@ -76,10 +83,17 @@
   // by a Service Worker.
   int controller_handle_id() const;
 
+  // Gets the handle ID of the associated registration, or
+  // kInvalidRegistrationHandleId if the provider is not associated with any
+  // registration.
+  int registration_handle_id() const;
+
  private:
   friend class base::RefCounted<ServiceWorkerProviderContext>;
   ~ServiceWorkerProviderContext();
 
+  bool IsAssociatedWithRegistration(int registration_handle_id) const;
+
   const int provider_id_;
   scoped_refptr<base::MessageLoopProxy> main_thread_loop_proxy_;
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
@@ -87,6 +101,7 @@
   scoped_ptr<ServiceWorkerHandleReference> waiting_;
   scoped_ptr<ServiceWorkerHandleReference> active_;
   scoped_ptr<ServiceWorkerHandleReference> controller_;
+  scoped_ptr<ServiceWorkerRegistrationHandleReference> registration_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderContext);
 };
diff --git a/content/child/service_worker/service_worker_registration_handle_reference.h b/content/child/service_worker/service_worker_registration_handle_reference.h
index 066c89f4..605094d 100644
--- a/content/child/service_worker/service_worker_registration_handle_reference.h
+++ b/content/child/service_worker/service_worker_registration_handle_reference.h
@@ -30,6 +30,7 @@
 
   ~ServiceWorkerRegistrationHandleReference();
 
+  const ServiceWorkerRegistrationObjectInfo& info() const { return info_; }
   int handle_id() const { return info_.handle_id; }
   GURL scope() const { return info_.scope; }
 
diff --git a/content/child/service_worker/web_service_worker_provider_impl.cc b/content/child/service_worker/web_service_worker_provider_impl.cc
index 5b6edbff..c416440 100644
--- a/content/child/service_worker/web_service_worker_provider_impl.cc
+++ b/content/child/service_worker/web_service_worker_provider_impl.cc
@@ -10,7 +10,9 @@
 #include "content/child/service_worker/service_worker_dispatcher.h"
 #include "content/child/service_worker/service_worker_handle_reference.h"
 #include "content/child/service_worker/service_worker_provider_context.h"
+#include "content/child/service_worker/service_worker_registration_handle_reference.h"
 #include "content/child/service_worker/web_service_worker_impl.h"
+#include "content/child/service_worker/web_service_worker_registration_impl.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerProviderClient.h"
@@ -47,6 +49,30 @@
   // for more context)
   GetDispatcher()->AddScriptClient(provider_id_, client);
 
+  if (!context_->registration()) {
+    // This provider is not associated with any registration.
+    return;
+  }
+
+  // Set .ready if the associated registration has the active service worker.
+  if (context_->active_handle_id() != kInvalidServiceWorkerHandleId) {
+    WebServiceWorkerRegistrationImpl* registration =
+        GetDispatcher()->FindServiceWorkerRegistration(
+            context_->registration()->info(), false);
+    if (!registration) {
+      registration = GetDispatcher()->CreateServiceWorkerRegistration(
+          context_->registration()->info(), false);
+      ServiceWorkerVersionAttributes attrs = context_->GetVersionAttributes();
+      registration->SetInstalling(
+          GetDispatcher()->GetServiceWorker(attrs.installing, false));
+      registration->SetWaiting(
+          GetDispatcher()->GetServiceWorker(attrs.waiting, false));
+      registration->SetActive(
+          GetDispatcher()->GetServiceWorker(attrs.active, false));
+    }
+    client->setReadyRegistration(registration);
+  }
+
   if (context_->controller_handle_id() != kInvalidServiceWorkerHandleId) {
     client->setController(GetDispatcher()->GetServiceWorker(
         context_->controller()->info(), false));
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 5349ff8..8a13422 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -184,6 +184,17 @@
 // extract it and dispatch the message to the correct ServiceWorkerDispatcher
 // on the correct thread.
 
+// Informs the child process that the given provider gets associated or
+// disassociated with the registration.
+IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_AssociateRegistration,
+                     int /* thread_id */,
+                     int /* provider_id */,
+                     content::ServiceWorkerRegistrationObjectInfo,
+                     content::ServiceWorkerVersionAttributes)
+IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_DisassociateRegistration,
+                     int /* thread_id */,
+                     int /* provider_id */)
+
 // Response to ServiceWorkerMsg_RegisterServiceWorker.
 IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_ServiceWorkerRegistered,
                      int /* thread_id */,