diff --git a/packages/service-worker/worker/src/driver.ts b/packages/service-worker/worker/src/driver.ts index 3c44568657bd6..09b966aeedc0a 100644 --- a/packages/service-worker/worker/src/driver.ts +++ b/packages/service-worker/worker/src/driver.ts @@ -802,8 +802,15 @@ export class Driver implements Debuggable, UpdateSource { // Future new clients will use this hash as the latest version. this.latestHash = hash; + // If we are in `EXISTING_CLIENTS_ONLY` mode (meaning we didn't have a clean copy of the last + // latest version), we can now recover to `NORMAL` mode and start accepting new clients. + if (this.state === DriverReadyState.EXISTING_CLIENTS_ONLY) { + this.state = DriverReadyState.NORMAL; + this.stateMessage = '(nominal)'; + } + await this.sync(); - await this.notifyClientsAboutUpdate(); + await this.notifyClientsAboutUpdate(newVersion); } async checkForUpdate(): Promise { @@ -959,19 +966,19 @@ export class Driver implements Debuggable, UpdateSource { async lookupResourceWithoutHash(url: string): Promise { await this.initialized; - const version = this.versions.get(this.latestHash !) !; - return version.lookupResourceWithoutHash(url); + const version = this.versions.get(this.latestHash !); + return version ? version.lookupResourceWithoutHash(url) : null; } async previouslyCachedResources(): Promise { await this.initialized; - const version = this.versions.get(this.latestHash !) !; - return version.previouslyCachedResources(); + const version = this.versions.get(this.latestHash !); + return version ? version.previouslyCachedResources() : []; } - recentCacheStatus(url: string): Promise { - const version = this.versions.get(this.latestHash !) !; - return version.recentCacheStatus(url); + async recentCacheStatus(url: string): Promise { + const version = this.versions.get(this.latestHash !); + return version ? version.recentCacheStatus(url) : UpdateCacheStatus.NOT_CACHED; } private mergeHashWithAppData(manifest: Manifest, hash: string): {hash: string, appData: Object} { @@ -981,11 +988,10 @@ export class Driver implements Debuggable, UpdateSource { }; } - async notifyClientsAboutUpdate(): Promise { + async notifyClientsAboutUpdate(next: AppVersion): Promise { await this.initialized; const clients = await this.scope.clients.matchAll(); - const next = this.versions.get(this.latestHash !) !; await clients.reduce(async(previous, client) => { await previous; diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index c9d550ac20472..aa6a673473023 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -1269,6 +1269,22 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope'; brokenLazyServer.assertNoOtherRequests(); }); + it('recovers from degraded `EXISTING_CLIENTS_ONLY` mode as soon as there is a valid update', + async() => { + await driver.initialized; + expect(driver.state).toBe(DriverReadyState.NORMAL); + + // Install a broken version. + scope.updateServerState(brokenServer); + await driver.checkForUpdate(); + expect(driver.state).toBe(DriverReadyState.EXISTING_CLIENTS_ONLY); + + // Install a good version. + scope.updateServerState(serverUpdate); + await driver.checkForUpdate(); + expect(driver.state).toBe(DriverReadyState.NORMAL); + }); + it('ignores invalid `only-if-cached` requests ', async() => { const requestFoo = (cache: RequestCache | 'only-if-cached', mode: RequestMode) => makeRequest(scope, '/foo.txt', undefined, {cache, mode});