From 53ba099246d0dd27dd9c54480299d5f62d177467 Mon Sep 17 00:00:00 2001 From: Mossroy Date: Thu, 4 Aug 2022 17:52:27 +0200 Subject: [PATCH] Switch to PWA in Firefox extension when SW API is unavailable (#878) This is necessary for Firefox >=103, where the ServiceWorker API has been removed in WebExtensions. See https://hg.mozilla.org/integration/autoland/rev/3a2907ad88e8 Fixes #877 --- www/js/app.js | 137 +++++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/www/js/app.js b/www/js/app.js index cd6ee4eb0..4d2baaa96 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -780,76 +780,85 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'settingsStore','abstractFilesys caches.delete(ASSETS_CACHE); } } else if (value === 'serviceworker') { - if (!isServiceWorkerAvailable()) { - uiUtil.systemAlert('The ServiceWorker API is not available on your device. Falling back to JQuery mode', 'ServiceWorker API not available').then(function () { - setContentInjectionMode('jquery'); - }); - return; - } - if (!isMessageChannelAvailable()) { - uiUtil.systemAlert('The MessageChannel API is not available on your device. Falling back to JQuery mode', 'MessageChannel API not available').then(function () { - setContentInjectionMode('jquery'); - }); - return; - } var protocol = window.location.protocol; - if (!isServiceWorkerReady()) { - $('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it..."); - if (navigator.serviceWorker.controller) { - console.log("Active service worker found, no need to register"); - serviceWorkerRegistration = true; - // Remove any jQuery hooks from a previous jQuery session - $('#articleContent').contents().remove(); - // Create the MessageChannel and send 'init' - initOrKeepAliveServiceWorker(); - refreshAPIStatus(); - } else { - navigator.serviceWorker.register('../service-worker.js').then(function (reg) { - // The ServiceWorker is registered - serviceWorkerRegistration = reg; - // We need to wait for the ServiceWorker to be activated - // before sending the first init message - var serviceWorker = reg.installing || reg.waiting || reg.active; - serviceWorker.addEventListener('statechange', function(statechangeevent) { - if (statechangeevent.target.state === 'activated') { - // Remove any jQuery hooks from a previous jQuery session - $('#articleContent').contents().remove(); - // Create the MessageChannel and send the 'init' message to the ServiceWorker + // Since Firefox 103, the ServiceWorker API is not available any more in Webextensions. See https://hg.mozilla.org/integration/autoland/rev/3a2907ad88e8 and https://bugzilla.mozilla.org/show_bug.cgi?id=1593931 + // Previously, the API was available, but failed to register (which we could trap a fews lines below). + // So we now need to suggest a switch to the PWA if we are inside a Firefox Extension and the ServiceWorker API is unavailable. + // Even if some older firefox versions do not support ServiceWorkers at all (versions 42, 43, 45ESR, 52ESR, 60ESR and 68ESR, based on https://caniuse.com/serviceworkers). In this case, the PWA will not work either. + if (protocol === 'moz-extension:' && !isServiceWorkerAvailable()) { + launchMozillaExtensionServiceWorker(); + } else { + if (!isServiceWorkerAvailable()) { + uiUtil.systemAlert('The ServiceWorker API is not available on your device. Falling back to JQuery mode', 'ServiceWorker API not available').then(function () { + setContentInjectionMode('jquery'); + }); + return; + } + if (!isMessageChannelAvailable()) { + uiUtil.systemAlert('The MessageChannel API is not available on your device. Falling back to JQuery mode', 'MessageChannel API not available').then(function () { + setContentInjectionMode('jquery'); + }); + return; + } + if (!isServiceWorkerReady()) { + $('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it..."); + if (navigator.serviceWorker.controller) { + console.log("Active service worker found, no need to register"); + serviceWorkerRegistration = true; + // Remove any jQuery hooks from a previous jQuery session + $('#articleContent').contents().remove(); + // Create the MessageChannel and send 'init' + initOrKeepAliveServiceWorker(); + refreshAPIStatus(); + } else { + navigator.serviceWorker.register('../service-worker.js').then(function (reg) { + // The ServiceWorker is registered + serviceWorkerRegistration = reg; + // We need to wait for the ServiceWorker to be activated + // before sending the first init message + var serviceWorker = reg.installing || reg.waiting || reg.active; + serviceWorker.addEventListener('statechange', function(statechangeevent) { + if (statechangeevent.target.state === 'activated') { + // Remove any jQuery hooks from a previous jQuery session + $('#articleContent').contents().remove(); + // Create the MessageChannel and send the 'init' message to the ServiceWorker + initOrKeepAliveServiceWorker(); + // We need to refresh cache status here on first activation because SW was inaccessible till now + // We also initialize the ASSETS_CACHE constant in SW here + refreshCacheStatus(); + refreshAPIStatus(); + } + }); + if (serviceWorker.state === 'activated') { + // Even if the ServiceWorker is already activated, + // We need to re-create the MessageChannel + // and send the 'init' message to the ServiceWorker + // in case it has been stopped and lost its context initOrKeepAliveServiceWorker(); - // We need to refresh cache status here on first activation because SW was inaccessible till now - // We also initialize the ASSETS_CACHE constant in SW here - refreshCacheStatus(); - refreshAPIStatus(); } - }); - if (serviceWorker.state === 'activated') { - // Even if the ServiceWorker is already activated, - // We need to re-create the MessageChannel - // and send the 'init' message to the ServiceWorker - // in case it has been stopped and lost its context - initOrKeepAliveServiceWorker(); - } - refreshCacheStatus(); - refreshAPIStatus(); - }).catch(function (err) { - if (protocol === 'moz-extension:') { - launchMozillaExtensionServiceWorker(); - } else { - console.error('Error while registering serviceWorker', err); + refreshCacheStatus(); refreshAPIStatus(); - var message = "The ServiceWorker could not be properly registered. Switching back to jQuery mode. Error message : " + err; - if (protocol === 'file:') { - message += "

You seem to be opening kiwix-js with the file:// protocol. You should open it through a web server : either through a local one (http://localhost/...) or through a remote one (but you need SSL : https://webserver/...)"; + }).catch(function (err) { + if (protocol === 'moz-extension:') { + // This is still useful for Firefox<103 extensions, where the ServiceWorker API is available, but fails to register + launchMozillaExtensionServiceWorker(); + } else { + console.error('Error while registering serviceWorker', err); + refreshAPIStatus(); + var message = "The ServiceWorker could not be properly registered. Switching back to jQuery mode. Error message : " + err; + if (protocol === 'file:') { + message += "

You seem to be opening kiwix-js with the file:// protocol. You should open it through a web server : either through a local one (http://localhost/...) or through a remote one (but you need SSL : https://webserver/...)"; + } + uiUtil.systemAlert(message, "Failed to register ServiceWorker").then(function () { + setContentInjectionMode('jquery'); + }); } - uiUtil.systemAlert(message, "Failed to register ServiceWorker").then(function () { - setContentInjectionMode('jquery'); - }); - } - }); + }); + } + } else { + // We need to reactivate Service Worker + initOrKeepAliveServiceWorker(); } - } else { - // We need to reactivate Service Worker - initOrKeepAliveServiceWorker(); } // User has switched to Service Worker mode, so no longer needs the memory cache // We should empty it to ensure good memory management