Skip to content

Commit

Permalink
notifyAllClients option (#2920)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffposnick committed Aug 19, 2021
1 parent 833d801 commit 43602e3
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 13 deletions.
56 changes: 44 additions & 12 deletions packages/workbox-broadcast-update/src/BroadcastCacheUpdate.ts
Expand Up @@ -12,23 +12,29 @@ import {resultingClientExists} from 'workbox-core/_private/resultingClientExists
import {CacheDidUpdateCallbackParam} from 'workbox-core/types.js';
import {logger} from 'workbox-core/_private/logger.js';
import {responsesAreSame} from './responsesAreSame.js';
import {CACHE_UPDATED_MESSAGE_TYPE, CACHE_UPDATED_MESSAGE_META, DEFAULT_HEADERS_TO_CHECK} from './utils/constants.js';
import {
CACHE_UPDATED_MESSAGE_META,
CACHE_UPDATED_MESSAGE_TYPE,
DEFAULT_HEADERS_TO_CHECK,
NOTIFY_ALL_CLIENTS,
} from './utils/constants.js';

import './_version.js';


// UA-sniff Safari: https://stackoverflow.com/questions/7944460/detect-safari-browser
// TODO(philipwalton): remove once this Safari bug fix has been released.
// https://bugs.webkit.org/show_bug.cgi?id=201169
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);


// Give TypeScript the correct global.
declare let self: ServiceWorkerGlobalScope;

export interface BroadcastCacheUpdateOptions {
headersToCheck?: string[];
generatePayload?: (options: CacheDidUpdateCallbackParam) => Record<string, any>;
generatePayload?: (
options: CacheDidUpdateCallbackParam,
) => Record<string, any>;
notifyAllClients?: boolean;
}

/**
Expand All @@ -38,7 +44,9 @@ export interface BroadcastCacheUpdateOptions {
* @return Object
* @private
*/
function defaultPayloadGenerator(data: CacheDidUpdateCallbackParam): Record<string, any> {
function defaultPayloadGenerator(
data: CacheDidUpdateCallbackParam,
): Record<string, any> {
return {
cacheName: data.cacheName,
updatedURL: data.request.url,
Expand All @@ -56,7 +64,10 @@ function defaultPayloadGenerator(data: CacheDidUpdateCallbackParam): Record<stri
*/
class BroadcastCacheUpdate {
private readonly _headersToCheck: string[];
private readonly _generatePayload: (options: CacheDidUpdateCallbackParam) => Record<string, any>;
private readonly _generatePayload: (
options: CacheDidUpdateCallbackParam,
) => Record<string, any>;
private readonly _notifyAllClients: boolean;

/**
* Construct a BroadcastCacheUpdate instance with a specific `channelName` to
Expand All @@ -69,13 +80,18 @@ class BroadcastCacheUpdate {
* @param {string} [options.generatePayload] A function whose return value
* will be used as the `payload` field in any cache update messages sent
* to the window clients.
* @param {boolean} [options.notifyAllClients=true] If true (the default) then
* all open clients will receive a message. If false, then only the client
* that make the original request will be notified of the update.
*/
constructor({
headersToCheck,
generatePayload,
headersToCheck,
notifyAllClients,
}: BroadcastCacheUpdateOptions = {}) {
this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK;
this._generatePayload = generatePayload || defaultPayloadGenerator;
this._notifyAllClients = notifyAllClients ?? NOTIFY_ALL_CLIENTS;
}

/**
Expand Down Expand Up @@ -136,10 +152,18 @@ class BroadcastCacheUpdate {
return;
}

if (!responsesAreSame(options.oldResponse, options.newResponse, this._headersToCheck)) {
if (
!responsesAreSame(
options.oldResponse,
options.newResponse,
this._headersToCheck,
)
) {
if (process.env.NODE_ENV !== 'production') {
logger.log(
`Newer response found (and cached) for:`, options.request.url);
`Newer response found (and cached) for:`,
options.request.url,
);
}

const messageData = {
Expand Down Expand Up @@ -172,9 +196,17 @@ class BroadcastCacheUpdate {
}
}

const windows = await self.clients.matchAll({type: 'window'});
for (const win of windows) {
win.postMessage(messageData);
if (this._notifyAllClients) {
const windows = await self.clients.matchAll({type: 'window'});
for (const win of windows) {
win.postMessage(messageData);
}
} else {
// See https://github.com/GoogleChrome/workbox/issues/2895
if (options.event instanceof FetchEvent) {
const client = await self.clients.get(options.event.clientId);
client?.postMessage(messageData);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/workbox-broadcast-update/src/utils/constants.ts
Expand Up @@ -10,6 +10,7 @@ import '../_version.js';

export const CACHE_UPDATED_MESSAGE_TYPE = 'CACHE_UPDATED';
export const CACHE_UPDATED_MESSAGE_META = 'workbox-broadcast-update';
export const NOTIFY_ALL_CLIENTS = true;
export const DEFAULT_HEADERS_TO_CHECK: string[] = [
'content-length',
'etag',
Expand Down
3 changes: 3 additions & 0 deletions packages/workbox-build/src/schema/GenerateSWOptions.json
Expand Up @@ -446,6 +446,9 @@
"generatePayload": {
"type": "object",
"additionalProperties": false
},
"notifyAllClients": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
3 changes: 3 additions & 0 deletions packages/workbox-build/src/schema/GetManifestOptions.json
Expand Up @@ -338,6 +338,9 @@
"generatePayload": {
"type": "object",
"additionalProperties": false
},
"notifyAllClients": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
3 changes: 3 additions & 0 deletions packages/workbox-build/src/schema/InjectManifestOptions.json
Expand Up @@ -350,6 +350,9 @@
"generatePayload": {
"type": "object",
"additionalProperties": false
},
"notifyAllClients": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
Expand Up @@ -425,6 +425,9 @@
"generatePayload": {
"type": "object",
"additionalProperties": false
},
"notifyAllClients": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
Expand Up @@ -337,6 +337,9 @@
"generatePayload": {
"type": "object",
"additionalProperties": false
},
"notifyAllClients": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
48 changes: 47 additions & 1 deletion test/workbox-broadcast-update/integration/test-all.js
Expand Up @@ -121,7 +121,7 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
});
});

it(`should broadcast a message to all open window clients`, async function() {
it(`should broadcast a message to all open window clients by default`, async function() {
// This test doesn't work in Safari:
// https://github.com/GoogleChrome/workbox/issues/2755
if (seleniumBrowser.getId() === 'safari') {
Expand Down Expand Up @@ -181,4 +181,50 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
},
}]);
});

it(`should only broadcast a message to the client that made the request when notifyAllClients is false`, async function() {
// This test doesn't work in Safari:
// https://github.com/GoogleChrome/workbox/issues/2755
if (seleniumBrowser.getId() === 'safari') {
this.skip();
}

const url = `${apiURL}?notifyAllClientsTest`;
const tabManager = new TabManager(webdriver);

await webdriver.get(testingURL);
await webdriver.executeAsyncScript((url, cb) => {
fetch(url).then(() => cb()).catch((err) => cb(err.message));
}, url);

await tabManager.openTab(testingURL);
await webdriver.executeAsyncScript((url, cb) => {
fetch(url).then(() => cb()).catch((err) => cb(err.message));
}, url);

await webdriver.wait(() => {
return webdriver.executeScript(() => {
return window.__messages.length > 0;
});
});

const populatedMessages = await webdriver.executeScript(() => {
return window.__messages;
});
expect(populatedMessages).to.eql([{
type: 'CACHE_UPDATED',
meta: 'workbox-broadcast-update',
payload: {
cacheName: 'bcu-integration-test',
updatedURL: url,
},
}]);

await tabManager.closeOpenedTabs();

const unpopulatedMessages = await webdriver.executeScript(() => {
return window.__messages;
});
expect(unpopulatedMessages).to.be.empty;
});
});
12 changes: 12 additions & 0 deletions test/workbox-broadcast-update/static/sw.js
Expand Up @@ -13,6 +13,18 @@ importScripts('/__WORKBOX/buildFile/workbox-strategies');

const cacheName = 'bcu-integration-test';

workbox.routing.registerRoute(
({url}) => url.searchParams.has('notifyAllClientsTest'),
new workbox.strategies.NetworkFirst({
cacheName,
plugins: [
new workbox.broadcastUpdate.BroadcastUpdatePlugin({
notifyAllClients: false,
}),
],
}),
);

workbox.routing.registerRoute(
new RegExp('/__WORKBOX/uniqueETag$'),
new workbox.strategies.StaleWhileRevalidate({
Expand Down

0 comments on commit 43602e3

Please sign in to comment.