workbox-broadcast-update.dev.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. this.workbox = this.workbox || {};
  2. this.workbox.broadcastUpdate = (function (exports, assert_js, timeout_js, resultingClientExists_js, logger_js, WorkboxError_js, dontWaitFor_js) {
  3. 'use strict';
  4. try {
  5. self['workbox:broadcast-update:5.1.4'] && _();
  6. } catch (e) {}
  7. /*
  8. Copyright 2018 Google LLC
  9. Use of this source code is governed by an MIT-style
  10. license that can be found in the LICENSE file or at
  11. https://opensource.org/licenses/MIT.
  12. */
  13. /**
  14. * Given two `Response's`, compares several header values to see if they are
  15. * the same or not.
  16. *
  17. * @param {Response} firstResponse
  18. * @param {Response} secondResponse
  19. * @param {Array<string>} headersToCheck
  20. * @return {boolean}
  21. *
  22. * @memberof module:workbox-broadcast-update
  23. */
  24. const responsesAreSame = (firstResponse, secondResponse, headersToCheck) => {
  25. {
  26. if (!(firstResponse instanceof Response && secondResponse instanceof Response)) {
  27. throw new WorkboxError_js.WorkboxError('invalid-responses-are-same-args');
  28. }
  29. }
  30. const atLeastOneHeaderAvailable = headersToCheck.some(header => {
  31. return firstResponse.headers.has(header) && secondResponse.headers.has(header);
  32. });
  33. if (!atLeastOneHeaderAvailable) {
  34. {
  35. logger_js.logger.warn(`Unable to determine where the response has been updated ` + `because none of the headers that would be checked are present.`);
  36. logger_js.logger.debug(`Attempting to compare the following: `, firstResponse, secondResponse, headersToCheck);
  37. } // Just return true, indicating the that responses are the same, since we
  38. // can't determine otherwise.
  39. return true;
  40. }
  41. return headersToCheck.every(header => {
  42. const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header);
  43. const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header);
  44. return headerStateComparison && headerValueComparison;
  45. });
  46. };
  47. /*
  48. Copyright 2018 Google LLC
  49. Use of this source code is governed by an MIT-style
  50. license that can be found in the LICENSE file or at
  51. https://opensource.org/licenses/MIT.
  52. */
  53. const CACHE_UPDATED_MESSAGE_TYPE = 'CACHE_UPDATED';
  54. const CACHE_UPDATED_MESSAGE_META = 'workbox-broadcast-update';
  55. const DEFAULT_HEADERS_TO_CHECK = ['content-length', 'etag', 'last-modified'];
  56. /*
  57. Copyright 2018 Google LLC
  58. Use of this source code is governed by an MIT-style
  59. license that can be found in the LICENSE file or at
  60. https://opensource.org/licenses/MIT.
  61. */
  62. // TODO(philipwalton): remove once this Safari bug fix has been released.
  63. // https://bugs.webkit.org/show_bug.cgi?id=201169
  64. const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
  65. /**
  66. * Generates the default payload used in update messages. By default the
  67. * payload includes the `cacheName` and `updatedURL` fields.
  68. *
  69. * @return Object
  70. * @private
  71. */
  72. function defaultPayloadGenerator(data) {
  73. return {
  74. cacheName: data.cacheName,
  75. updatedURL: data.request.url
  76. };
  77. }
  78. /**
  79. * Uses the `postMessage()` API to inform any open windows/tabs when a cached
  80. * response has been updated.
  81. *
  82. * For efficiency's sake, the underlying response bodies are not compared;
  83. * only specific response headers are checked.
  84. *
  85. * @memberof module:workbox-broadcast-update
  86. */
  87. class BroadcastCacheUpdate {
  88. /**
  89. * Construct a BroadcastCacheUpdate instance with a specific `channelName` to
  90. * broadcast messages on
  91. *
  92. * @param {Object} options
  93. * @param {Array<string>} [options.headersToCheck=['content-length', 'etag', 'last-modified']]
  94. * A list of headers that will be used to determine whether the responses
  95. * differ.
  96. * @param {string} [options.generatePayload] A function whose return value
  97. * will be used as the `payload` field in any cache update messages sent
  98. * to the window clients.
  99. */
  100. constructor({
  101. headersToCheck,
  102. generatePayload
  103. } = {}) {
  104. this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK;
  105. this._generatePayload = generatePayload || defaultPayloadGenerator;
  106. }
  107. /**
  108. * Compares two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
  109. * and sends a message (via `postMessage()`) to all window clients if the
  110. * responses differ (note: neither of the Responses can be
  111. * {@link http://stackoverflow.com/questions/39109789|opaque}).
  112. *
  113. * The message that's posted has the following format (where `payload` can
  114. * be customized via the `generatePayload` option the instance is created
  115. * with):
  116. *
  117. * ```
  118. * {
  119. * type: 'CACHE_UPDATED',
  120. * meta: 'workbox-broadcast-update',
  121. * payload: {
  122. * cacheName: 'the-cache-name',
  123. * updatedURL: 'https://example.com/'
  124. * }
  125. * }
  126. * ```
  127. *
  128. * @param {Object} options
  129. * @param {Response} [options.oldResponse] Cached response to compare.
  130. * @param {Response} options.newResponse Possibly updated response to compare.
  131. * @param {Request} options.request The request.
  132. * @param {string} options.cacheName Name of the cache the responses belong
  133. * to. This is included in the broadcast message.
  134. * @param {Event} [options.event] event An optional event that triggered
  135. * this possible cache update.
  136. * @return {Promise} Resolves once the update is sent.
  137. */
  138. async notifyIfUpdated(options) {
  139. {
  140. assert_js.assert.isType(options.cacheName, 'string', {
  141. moduleName: 'workbox-broadcast-update',
  142. className: 'BroadcastCacheUpdate',
  143. funcName: 'notifyIfUpdated',
  144. paramName: 'cacheName'
  145. });
  146. assert_js.assert.isInstance(options.newResponse, Response, {
  147. moduleName: 'workbox-broadcast-update',
  148. className: 'BroadcastCacheUpdate',
  149. funcName: 'notifyIfUpdated',
  150. paramName: 'newResponse'
  151. });
  152. assert_js.assert.isInstance(options.request, Request, {
  153. moduleName: 'workbox-broadcast-update',
  154. className: 'BroadcastCacheUpdate',
  155. funcName: 'notifyIfUpdated',
  156. paramName: 'request'
  157. });
  158. } // Without two responses there is nothing to compare.
  159. if (!options.oldResponse) {
  160. return;
  161. }
  162. if (!responsesAreSame(options.oldResponse, options.newResponse, this._headersToCheck)) {
  163. {
  164. logger_js.logger.log(`Newer response found (and cached) for:`, options.request.url);
  165. }
  166. const messageData = {
  167. type: CACHE_UPDATED_MESSAGE_TYPE,
  168. meta: CACHE_UPDATED_MESSAGE_META,
  169. payload: this._generatePayload(options)
  170. }; // For navigation requests, wait until the new window client exists
  171. // before sending the message
  172. if (options.request.mode === 'navigate') {
  173. let resultingClientId;
  174. if (options.event instanceof FetchEvent) {
  175. resultingClientId = options.event.resultingClientId;
  176. }
  177. const resultingWin = await resultingClientExists_js.resultingClientExists(resultingClientId); // Safari does not currently implement postMessage buffering and
  178. // there's no good way to feature detect that, so to increase the
  179. // chances of the message being delivered in Safari, we add a timeout.
  180. // We also do this if `resultingClientExists()` didn't return a client,
  181. // which means it timed out, so it's worth waiting a bit longer.
  182. if (!resultingWin || isSafari) {
  183. // 3500 is chosen because (according to CrUX data) 80% of mobile
  184. // websites hit the DOMContentLoaded event in less than 3.5 seconds.
  185. // And presumably sites implementing service worker are on the
  186. // higher end of the performance spectrum.
  187. await timeout_js.timeout(3500);
  188. }
  189. }
  190. const windows = await self.clients.matchAll({
  191. type: 'window'
  192. });
  193. for (const win of windows) {
  194. win.postMessage(messageData);
  195. }
  196. }
  197. }
  198. }
  199. /*
  200. Copyright 2018 Google LLC
  201. Use of this source code is governed by an MIT-style
  202. license that can be found in the LICENSE file or at
  203. https://opensource.org/licenses/MIT.
  204. */
  205. /**
  206. * This plugin will automatically broadcast a message whenever a cached response
  207. * is updated.
  208. *
  209. * @memberof module:workbox-broadcast-update
  210. */
  211. class BroadcastUpdatePlugin {
  212. /**
  213. * Construct a BroadcastCacheUpdate instance with the passed options and
  214. * calls its [`notifyIfUpdated()`]{@link module:workbox-broadcast-update.BroadcastCacheUpdate~notifyIfUpdated}
  215. * method whenever the plugin's `cacheDidUpdate` callback is invoked.
  216. *
  217. * @param {Object} options
  218. * @param {Array<string>} [options.headersToCheck=['content-length', 'etag', 'last-modified']]
  219. * A list of headers that will be used to determine whether the responses
  220. * differ.
  221. * @param {string} [options.generatePayload] A function whose return value
  222. * will be used as the `payload` field in any cache update messages sent
  223. * to the window clients.
  224. */
  225. constructor(options) {
  226. /**
  227. * A "lifecycle" callback that will be triggered automatically by the
  228. * `workbox-sw` and `workbox-runtime-caching` handlers when an entry is
  229. * added to a cache.
  230. *
  231. * @private
  232. * @param {Object} options The input object to this function.
  233. * @param {string} options.cacheName Name of the cache being updated.
  234. * @param {Response} [options.oldResponse] The previous cached value, if any.
  235. * @param {Response} options.newResponse The new value in the cache.
  236. * @param {Request} options.request The request that triggered the update.
  237. * @param {Request} [options.event] The event that triggered the update.
  238. */
  239. this.cacheDidUpdate = async options => {
  240. dontWaitFor_js.dontWaitFor(this._broadcastUpdate.notifyIfUpdated(options));
  241. };
  242. this._broadcastUpdate = new BroadcastCacheUpdate(options);
  243. }
  244. }
  245. exports.BroadcastCacheUpdate = BroadcastCacheUpdate;
  246. exports.BroadcastUpdatePlugin = BroadcastUpdatePlugin;
  247. exports.responsesAreSame = responsesAreSame;
  248. return exports;
  249. }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
  250. //# sourceMappingURL=workbox-broadcast-update.dev.js.map