Commit 0b0c1c51 authored by falken's avatar falken Committed by Commit bot

(Reland) Service Worker: Handle same-scope, new script registration

Reland of 2da192af after:
- disabling some layout tests that need to be rebaselined
- landing 6cce6b78f "Decouple script_url from ServiceWorkerRegistration"
so Registration doesn't get in a weird state with a script_url that failed to
register

Before this patch, register() would delete an existing registration at
the scope if the script URL didn't match, and register a new one. This
overwriting creates a scenario where old tabs have a different
controller than new tabs, which the Service Worker spec avoids.

This patch implements the spec steps for same-scope, new script
register(). That means:
- If the existing registration is uninstalling, wait for that to
complete before doing anything.
- Create a new worker which becomes the installing worker of the
existing registration.

BUG=398355
TEST=https://codereview.chromium.org/480943002/

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

Cr-Commit-Position: refs/heads/master@{#292106}
parent 78e51a1e
...@@ -300,8 +300,8 @@ TEST_F(ServiceWorkerContextTest, Unregister) { ...@@ -300,8 +300,8 @@ TEST_F(ServiceWorkerContextTest, Unregister) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
// Make sure that when a new registration replaces an existing // Make sure registering a new script creates a new version and shares an
// registration, that the old one is cleaned up. // existing registration.
TEST_F(ServiceWorkerContextTest, RegisterNewScript) { TEST_F(ServiceWorkerContextTest, RegisterNewScript) {
GURL pattern("http://www.example.com/"); GURL pattern("http://www.example.com/");
...@@ -335,11 +335,9 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { ...@@ -335,11 +335,9 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called); ASSERT_TRUE(called);
// Returned IDs should be valid, and should differ from the values
// returned for the previous registration.
EXPECT_NE(kInvalidServiceWorkerRegistrationId, new_registration_id); EXPECT_NE(kInvalidServiceWorkerRegistrationId, new_registration_id);
EXPECT_NE(kInvalidServiceWorkerVersionId, new_version_id); EXPECT_NE(kInvalidServiceWorkerVersionId, new_version_id);
EXPECT_NE(old_registration_id, new_registration_id); EXPECT_EQ(old_registration_id, new_registration_id);
EXPECT_NE(old_version_id, new_version_id); EXPECT_NE(old_version_id, new_version_id);
} }
......
...@@ -288,8 +288,8 @@ TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) { ...@@ -288,8 +288,8 @@ TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) {
ASSERT_TRUE(called); ASSERT_TRUE(called);
} }
// Make sure that when a new registration replaces an existing // Make sure registering a new script creates a new version and shares an
// registration, that the old one is cleaned up. // existing registration.
TEST_F(ServiceWorkerJobTest, RegisterNewScript) { TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
GURL pattern("http://www.example.com/"); GURL pattern("http://www.example.com/");
...@@ -329,9 +329,7 @@ TEST_F(ServiceWorkerJobTest, RegisterNewScript) { ...@@ -329,9 +329,7 @@ TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called); ASSERT_TRUE(called);
ASSERT_TRUE(old_registration->HasOneRef()); ASSERT_EQ(old_registration, new_registration);
ASSERT_NE(old_registration, new_registration);
scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern; scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
storage()->FindRegistrationForPattern( storage()->FindRegistrationForPattern(
......
...@@ -40,7 +40,7 @@ class ServiceWorkerVersion; ...@@ -40,7 +40,7 @@ class ServiceWorkerVersion;
// Note this class can also host a running service worker, in which // Note this class can also host a running service worker, in which
// case it will observe resource loads made directly by the service worker. // case it will observe resource loads made directly by the service worker.
class CONTENT_EXPORT ServiceWorkerProviderHost class CONTENT_EXPORT ServiceWorkerProviderHost
: public ServiceWorkerRegistration::Listener, : public NON_EXPORTED_BASE(ServiceWorkerRegistration::Listener),
public base::SupportsWeakPtr<ServiceWorkerProviderHost> { public base::SupportsWeakPtr<ServiceWorkerProviderHost> {
public: public:
ServiceWorkerProviderHost(int process_id, ServiceWorkerProviderHost(int process_id,
......
...@@ -140,6 +140,18 @@ ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() { ...@@ -140,6 +140,18 @@ ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() {
return internal_.new_version.get(); return internal_.new_version.get();
} }
void ServiceWorkerRegisterJob::set_uninstalling_registration(
ServiceWorkerRegistration* registration) {
DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
internal_.uninstalling_registration = registration;
}
ServiceWorkerRegistration*
ServiceWorkerRegisterJob::uninstalling_registration() {
DCHECK_EQ(phase_, WAIT_FOR_UNINSTALL);
return internal_.uninstalling_registration;
}
void ServiceWorkerRegisterJob::SetPhase(Phase phase) { void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
switch (phase) { switch (phase) {
case INITIAL: case INITIAL:
...@@ -148,9 +160,12 @@ void ServiceWorkerRegisterJob::SetPhase(Phase phase) { ...@@ -148,9 +160,12 @@ void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
case START: case START:
DCHECK(phase_ == INITIAL) << phase_; DCHECK(phase_ == INITIAL) << phase_;
break; break;
case REGISTER: case WAIT_FOR_UNINSTALL:
DCHECK(phase_ == START) << phase_; DCHECK(phase_ == START) << phase_;
break; break;
case REGISTER:
DCHECK(phase_ == START || phase_ == WAIT_FOR_UNINSTALL) << phase_;
break;
case UPDATE: case UPDATE:
DCHECK(phase_ == START || phase_ == REGISTER) << phase_; DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
break; break;
...@@ -186,37 +201,31 @@ void ServiceWorkerRegisterJob::ContinueWithRegistration( ...@@ -186,37 +201,31 @@ void ServiceWorkerRegisterJob::ContinueWithRegistration(
return; return;
} }
// "Set registration.[[Uninstalling]] to false."
existing_registration->AbortPendingClear();
// "If scriptURL is equal to registration.[[ScriptURL]], then:" // "If scriptURL is equal to registration.[[ScriptURL]], then:"
if (existing_registration->GetNewestVersion()->script_url() == script_url_) { if (existing_registration->GetNewestVersion()->script_url() == script_url_) {
// Spec says to resolve with registration.[[GetNewestWorker]]. We come close // "Set registration.[[Uninstalling]] to false."
// by resolving with the active version. existing_registration->AbortPendingClear(base::Bind(
set_registration(existing_registration.get()); &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl,
weak_factory_.GetWeakPtr(),
if (!existing_registration->active_version()) { existing_registration));
UpdateAndContinue(); return;
return; }
}
ResolvePromise(status, if (existing_registration->is_uninstalling()) {
existing_registration.get(), // "Wait until the Record {[[key]], [[value]]} entry of its
existing_registration->active_version()); // [[ScopeToRegistrationMap]] where registation.scope matches entry.[[key]]
Complete(SERVICE_WORKER_OK); // is deleted."
WaitForUninstall(existing_registration);
return; return;
} }
// "Set registration.[[ScriptURL]] to scriptURL." We accomplish this by // "Set registration.[[Uninstalling]] to false."
// deleting the existing registration and registering a new one. DCHECK(!existing_registration->is_uninstalling());
// TODO(michaeln): Deactivate the live existing_registration object and
// eventually call storage->DeleteVersionResources() when it no longer has any // "Return the result of running the [[Update]] algorithm, or its equivalent,
// controllees. // passing registration as the argument."
context_->storage()->DeleteRegistration( set_registration(existing_registration);
existing_registration->id(), UpdateAndContinue();
existing_registration->pattern().GetOrigin(),
base::Bind(&ServiceWorkerRegisterJob::RegisterAndContinue,
weak_factory_.GetWeakPtr()));
} }
void ServiceWorkerRegisterJob::ContinueWithUpdate( void ServiceWorkerRegisterJob::ContinueWithUpdate(
...@@ -264,6 +273,35 @@ void ServiceWorkerRegisterJob::RegisterAndContinue( ...@@ -264,6 +273,35 @@ void ServiceWorkerRegisterJob::RegisterAndContinue(
UpdateAndContinue(); 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, existing_registration->active_version());
Complete(SERVICE_WORKER_OK);
}
// This function corresponds to the spec's [[Update]] algorithm. // This function corresponds to the spec's [[Update]] algorithm.
void ServiceWorkerRegisterJob::UpdateAndContinue() { void ServiceWorkerRegisterJob::UpdateAndContinue() {
SetPhase(UPDATE); SetPhase(UPDATE);
...@@ -455,6 +493,15 @@ bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) { ...@@ -455,6 +493,15 @@ bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) {
return false; 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( void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete(
ServiceWorkerVersion* most_recent_version, ServiceWorkerVersion* most_recent_version,
ServiceWorkerStatusCode status, ServiceWorkerStatusCode status,
......
...@@ -33,9 +33,9 @@ class ServiceWorkerStorage; ...@@ -33,9 +33,9 @@ class ServiceWorkerStorage;
// - waiting for older ServiceWorkerVersions to deactivate // - waiting for older ServiceWorkerVersions to deactivate
// - designating the new version to be the 'active' version // - designating the new version to be the 'active' version
// - updating storage // - updating storage
class ServiceWorkerRegisterJob class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase,
: public ServiceWorkerRegisterJobBase, public EmbeddedWorkerInstance::Listener,
public EmbeddedWorkerInstance::Listener { public ServiceWorkerRegistration::Listener {
public: public:
typedef base::Callback<void(ServiceWorkerStatusCode status, typedef base::Callback<void(ServiceWorkerStatusCode status,
ServiceWorkerRegistration* registration, ServiceWorkerRegistration* registration,
...@@ -74,14 +74,15 @@ class ServiceWorkerRegisterJob ...@@ -74,14 +74,15 @@ class ServiceWorkerRegisterJob
DisassociateVersionFromDocuments); DisassociateVersionFromDocuments);
enum Phase { enum Phase {
INITIAL, INITIAL,
START, START,
REGISTER, WAIT_FOR_UNINSTALL,
UPDATE, REGISTER,
INSTALL, UPDATE,
STORE, INSTALL,
COMPLETE, STORE,
ABORT, COMPLETE,
ABORT,
}; };
// Holds internal state of ServiceWorkerRegistrationJob, to compel use of the // Holds internal state of ServiceWorkerRegistrationJob, to compel use of the
...@@ -94,12 +95,16 @@ class ServiceWorkerRegisterJob ...@@ -94,12 +95,16 @@ class ServiceWorkerRegisterJob
// Holds the version created by this job. It can be the 'installing', // Holds the version created by this job. It can be the 'installing',
// 'waiting', or 'active' version depending on the phase. // 'waiting', or 'active' version depending on the phase.
scoped_refptr<ServiceWorkerVersion> new_version; scoped_refptr<ServiceWorkerVersion> new_version;
scoped_refptr<ServiceWorkerRegistration> uninstalling_registration;
}; };
void set_registration(ServiceWorkerRegistration* registration); void set_registration(ServiceWorkerRegistration* registration);
ServiceWorkerRegistration* registration(); ServiceWorkerRegistration* registration();
void set_new_version(ServiceWorkerVersion* version); void set_new_version(ServiceWorkerVersion* version);
ServiceWorkerVersion* new_version(); ServiceWorkerVersion* new_version();
void set_uninstalling_registration(ServiceWorkerRegistration* registration);
ServiceWorkerRegistration* uninstalling_registration();
void SetPhase(Phase phase); void SetPhase(Phase phase);
...@@ -110,6 +115,11 @@ class ServiceWorkerRegisterJob ...@@ -110,6 +115,11 @@ class ServiceWorkerRegisterJob
ServiceWorkerStatusCode status, ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration); const scoped_refptr<ServiceWorkerRegistration>& registration);
void RegisterAndContinue(ServiceWorkerStatusCode status); void RegisterAndContinue(ServiceWorkerStatusCode status);
void WaitForUninstall(
const scoped_refptr<ServiceWorkerRegistration>& registration);
void ContinueWithRegistrationForSameScriptUrl(
const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
ServiceWorkerStatusCode status);
void UpdateAndContinue(); void UpdateAndContinue();
void OnStartWorkerFinished(ServiceWorkerStatusCode status); void OnStartWorkerFinished(ServiceWorkerStatusCode status);
void OnStoreRegistrationComplete(ServiceWorkerStatusCode status); void OnStoreRegistrationComplete(ServiceWorkerStatusCode status);
...@@ -127,6 +137,10 @@ class ServiceWorkerRegisterJob ...@@ -127,6 +137,10 @@ class ServiceWorkerRegisterJob
virtual void OnPausedAfterDownload() OVERRIDE; virtual void OnPausedAfterDownload() OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// ServiceWorkerRegistration::Listener overrides
virtual void OnRegistrationFinishedUninstalling(
ServiceWorkerRegistration* registration) OVERRIDE;
void OnCompareScriptResourcesComplete( void OnCompareScriptResourcesComplete(
ServiceWorkerVersion* most_recent_version, ServiceWorkerVersion* most_recent_version,
ServiceWorkerStatusCode status, ServiceWorkerStatusCode status,
......
...@@ -167,10 +167,13 @@ void ServiceWorkerRegistration::ClearWhenReady() { ...@@ -167,10 +167,13 @@ void ServiceWorkerRegistration::ClearWhenReady() {
Clear(); Clear();
} }
void ServiceWorkerRegistration::AbortPendingClear() { void ServiceWorkerRegistration::AbortPendingClear(
const StatusCallback& callback) {
DCHECK(context_); DCHECK(context_);
if (!is_uninstalling()) if (!is_uninstalling()) {
callback.Run(SERVICE_WORKER_OK);
return; return;
}
is_uninstalling_ = false; is_uninstalling_ = false;
context_->storage()->NotifyDoneUninstallingRegistration(this); context_->storage()->NotifyDoneUninstallingRegistration(this);
...@@ -181,8 +184,9 @@ void ServiceWorkerRegistration::AbortPendingClear() { ...@@ -181,8 +184,9 @@ void ServiceWorkerRegistration::AbortPendingClear() {
context_->storage()->StoreRegistration( context_->storage()->StoreRegistration(
this, this,
most_recent_version.get(), most_recent_version.get(),
base::Bind(&ServiceWorkerRegistration::OnStoreFinished, base::Bind(&ServiceWorkerRegistration::OnRestoreFinished,
this, this,
callback,
most_recent_version)); most_recent_version));
} }
...@@ -278,7 +282,9 @@ void ServiceWorkerRegistration::OnDeleteFinished( ...@@ -278,7 +282,9 @@ void ServiceWorkerRegistration::OnDeleteFinished(
} }
void ServiceWorkerRegistration::Clear() { void ServiceWorkerRegistration::Clear() {
context_->storage()->NotifyDoneUninstallingRegistration(this); is_uninstalling_ = false;
if (context_)
context_->storage()->NotifyDoneUninstallingRegistration(this);
ChangedVersionAttributesMask mask; ChangedVersionAttributesMask mask;
if (installing_version_.get()) { if (installing_version_.get()) {
...@@ -302,15 +308,22 @@ void ServiceWorkerRegistration::Clear() { ...@@ -302,15 +308,22 @@ void ServiceWorkerRegistration::Clear() {
FOR_EACH_OBSERVER(Listener, listeners_, FOR_EACH_OBSERVER(Listener, listeners_,
OnVersionAttributesChanged(this, mask, info)); OnVersionAttributesChanged(this, mask, info));
} }
FOR_EACH_OBSERVER(
Listener, listeners_, OnRegistrationFinishedUninstalling(this));
} }
void ServiceWorkerRegistration::OnStoreFinished( void ServiceWorkerRegistration::OnRestoreFinished(
const StatusCallback& callback,
scoped_refptr<ServiceWorkerVersion> version, scoped_refptr<ServiceWorkerVersion> version,
ServiceWorkerStatusCode status) { ServiceWorkerStatusCode status) {
if (!context_) if (!context_) {
callback.Run(ServiceWorkerStatusCode::SERVICE_WORKER_ERROR_ABORT);
return; return;
}
context_->storage()->NotifyDoneInstallingRegistration( context_->storage()->NotifyDoneInstallingRegistration(
this, version.get(), status); this, version.get(), status);
callback.Run(status);
} }
} // namespace content } // namespace content
...@@ -20,10 +20,9 @@ namespace content { ...@@ -20,10 +20,9 @@ namespace content {
class ServiceWorkerRegistrationInfo; class ServiceWorkerRegistrationInfo;
class ServiceWorkerVersion; class ServiceWorkerVersion;
// This class represents a service worker registration. The // This class represents a Service Worker registration. The scope is constant
// scope is constant for the life of the persistent // for the life of the persistent registration. It's refcounted to facilitate
// registration. It's refcounted to facillitate multiple controllees // multiple controllees being associated with the same registration.
// being associated with the same registration.
class CONTENT_EXPORT ServiceWorkerRegistration class CONTENT_EXPORT ServiceWorkerRegistration
: NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerRegistration>), : NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerRegistration>),
public ServiceWorkerVersion::Listener { public ServiceWorkerVersion::Listener {
...@@ -35,9 +34,11 @@ class CONTENT_EXPORT ServiceWorkerRegistration ...@@ -35,9 +34,11 @@ class CONTENT_EXPORT ServiceWorkerRegistration
virtual void OnVersionAttributesChanged( virtual void OnVersionAttributesChanged(
ServiceWorkerRegistration* registration, ServiceWorkerRegistration* registration,
ChangedVersionAttributesMask changed_mask, ChangedVersionAttributesMask changed_mask,
const ServiceWorkerRegistrationInfo& info) = 0; const ServiceWorkerRegistrationInfo& info) {}
virtual void OnRegistrationFailed( virtual void OnRegistrationFailed(
ServiceWorkerRegistration* registration) = 0; ServiceWorkerRegistration* registration) {}
virtual void OnRegistrationFinishedUninstalling(
ServiceWorkerRegistration* registration) {}
}; };
ServiceWorkerRegistration(const GURL& pattern, ServiceWorkerRegistration(const GURL& pattern,
...@@ -95,9 +96,8 @@ class CONTENT_EXPORT ServiceWorkerRegistration ...@@ -95,9 +96,8 @@ class CONTENT_EXPORT ServiceWorkerRegistration
void ClearWhenReady(); void ClearWhenReady();
// Restores this registration in storage and cancels the pending // Restores this registration in storage and cancels the pending
// [[ClearRegistration]] algorithm. If the algorithm was already triggered, // [[ClearRegistration]] algorithm.
// does nothing. void AbortPendingClear(const StatusCallback& callback);
void AbortPendingClear();
// The time of the most recent update check. // The time of the most recent update check.
base::Time last_update_check() const { return last_update_check_; } base::Time last_update_check() const { return last_update_check_; }
...@@ -128,8 +128,10 @@ class CONTENT_EXPORT ServiceWorkerRegistration ...@@ -128,8 +128,10 @@ class CONTENT_EXPORT ServiceWorkerRegistration
// This method corresponds to the [[ClearRegistration]] algorithm. // This method corresponds to the [[ClearRegistration]] algorithm.
void Clear(); void Clear();
void OnStoreFinished(scoped_refptr<ServiceWorkerVersion> version,
ServiceWorkerStatusCode status); void OnRestoreFinished(const StatusCallback& callback,
scoped_refptr<ServiceWorkerVersion> version,
ServiceWorkerStatusCode status);
const GURL pattern_; const GURL pattern_;
const int64 registration_id_; const int64 registration_id_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment