service-worker.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // This service worker is required to expose an exported Godot project as a
  2. // Progressive Web App. It provides an offline fallback page telling the user
  3. // that they need an Internet connection to run the project if desired.
  4. // Incrementing CACHE_VERSION will kick off the install event and force
  5. // previously cached resources to be updated from the network.
  6. const CACHE_VERSION = "@GODOT_VERSION@";
  7. const CACHE_NAME = "@GODOT_NAME@-cache";
  8. const OFFLINE_URL = "offline.html";
  9. // Files that will be cached on load.
  10. const CACHED_FILES = [
  11. "godot.tools.html",
  12. "offline.html",
  13. "godot.tools.js",
  14. "godot.tools.worker.js",
  15. "godot.tools.audio.worklet.js",
  16. "logo.svg",
  17. "favicon.png",
  18. ];
  19. // Files that we might not want the user to preload, and will only be cached on first load.
  20. const CACHABLE_FILES = [
  21. "godot.tools.wasm",
  22. ];
  23. const FULL_CACHE = CACHED_FILES.concat(CACHABLE_FILES);
  24. self.addEventListener("install", (event) => {
  25. event.waitUntil(async function () {
  26. const cache = await caches.open(CACHE_NAME);
  27. // Clear old cache (including optionals).
  28. await Promise.all(FULL_CACHE.map(path => cache.delete(path)));
  29. // Insert new one.
  30. const done = await cache.addAll(CACHED_FILES);
  31. return done;
  32. }());
  33. });
  34. self.addEventListener("activate", (event) => {
  35. event.waitUntil(async function () {
  36. if ("navigationPreload" in self.registration) {
  37. await self.registration.navigationPreload.enable();
  38. }
  39. }());
  40. // Tell the active service worker to take control of the page immediately.
  41. self.clients.claim();
  42. });
  43. self.addEventListener("fetch", (event) => {
  44. const isNavigate = event.request.mode === "navigate";
  45. const url = event.request.url || "";
  46. const referrer = event.request.referrer || "";
  47. const base = referrer.slice(0, referrer.lastIndexOf("/") + 1);
  48. const local = url.startsWith(base) ? url.replace(base, "") : "";
  49. const isCachable = FULL_CACHE.some(v => v === local) || (base === referrer && base.endsWith(CACHED_FILES[0]));
  50. if (isNavigate || isCachable) {
  51. event.respondWith(async function () {
  52. try {
  53. // Use the preloaded response, if it's there
  54. let request = event.request.clone();
  55. let response = await event.preloadResponse;
  56. if (!response) {
  57. // Or, go over network.
  58. response = await fetch(event.request);
  59. }
  60. if (isCachable) {
  61. // Update the cache
  62. const cache = await caches.open(CACHE_NAME);
  63. cache.put(request, response.clone());
  64. }
  65. return response;
  66. } catch (error) {
  67. const cache = await caches.open(CACHE_NAME);
  68. if (event.request.mode === "navigate") {
  69. // Check if we have full cache.
  70. const cached = await Promise.all(FULL_CACHE.map(name => cache.match(name)));
  71. const missing = cached.some(v => v === undefined);
  72. const cachedResponse = missing ? await caches.match(OFFLINE_URL) : await caches.match(CACHED_FILES[0]);
  73. return cachedResponse;
  74. }
  75. const cachedResponse = await caches.match(event.request);
  76. return cachedResponse;
  77. }
  78. }());
  79. }
  80. });