mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-09-09 04:06:27 -04:00
Port SW initialization solutions from Kiwix JS (#495)
This commit is contained in:
parent
f43f0b5b6e
commit
82c3c27d9d
@ -386,6 +386,12 @@ self.addEventListener('message', function (event) {
|
||||
if (event.data.action === 'init') {
|
||||
// On 'init' message, we enable the fetchEventListener
|
||||
fetchCaptureEnabled = true;
|
||||
// Acdknowledge the init message to all clients
|
||||
self.clients.matchAll().then(function (clientList) {
|
||||
clientList.forEach(function (client) {
|
||||
client.postMessage({ action: 'acknowledge' });
|
||||
});
|
||||
});
|
||||
} else if (event.data.action === 'disable') {
|
||||
// On 'disable' message, we disable the fetchEventListener
|
||||
// Note that this code doesn't currently run because the app currently never sends a 'disable' message
|
||||
|
115
www/js/app.js
115
www/js/app.js
@ -3,21 +3,21 @@
|
||||
* This file handles the interaction between the Kiwix JS back end and the user
|
||||
*
|
||||
* Copyright 2013-2023 Jaifroid, Mossroy and contributors
|
||||
* License GPL v3:
|
||||
* Licence GPL v3:
|
||||
*
|
||||
* This file is part of Kiwix.
|
||||
*
|
||||
* Kiwix is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* it under the terms of the GNU General Public Licence as published by
|
||||
* the Free Software Foundation, either version 3 of the Licence, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Kiwix is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* GNU General Public Licence for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* You should have received a copy of the GNU General Public Licence
|
||||
* along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@ -57,11 +57,13 @@ const DELAY_BETWEEN_KEEPALIVE_SERVICEWORKER = 30000;
|
||||
// The global parameter and app state objects are defined in init.js
|
||||
/* global params, appstate, nw, electronAPI, Windows, webpMachine, dialog, LaunchParams, launchQueue, abstractFilesystemAccess, MSApp */
|
||||
|
||||
// Placeholders for the article container, the article window and the article DOM
|
||||
// Placeholders for the article container, the article window, the article DOM and some UI elements
|
||||
var articleContainer = document.getElementById('articleContent');
|
||||
articleContainer.kiwixType = 'iframe';
|
||||
var articleWindow = articleContainer.contentWindow;
|
||||
var articleDocument;
|
||||
var scrollbox = document.getElementById('scrollbox');
|
||||
var prefix = document.getElementById('prefix');
|
||||
|
||||
// The following variables are used to store the current article and its state
|
||||
|
||||
@ -104,8 +106,12 @@ if (typeof Windows !== 'undefined' && Windows.UI && Windows.UI.WebUI && Windows.
|
||||
}, false);
|
||||
}
|
||||
|
||||
// At launch, we set the correct content injection mode
|
||||
setContentInjectionMode(params.contentInjectionMode);
|
||||
|
||||
// Test caching capability
|
||||
cache.test(function () {});
|
||||
|
||||
// Unique identifier of the article expected to be displayed
|
||||
appstate.expectedArticleURLToBeDisplayed = '';
|
||||
// Check if we have managed to switch to PWA mode (if running UWP app)
|
||||
@ -216,8 +222,6 @@ function onPointerUp (e) {
|
||||
|
||||
if (/UWP/.test(params.appType)) document.body.addEventListener('pointerup', onPointerUp);
|
||||
|
||||
var prefix = document.getElementById('prefix');
|
||||
var scrollbox = document.getElementById('scrollbox');
|
||||
var searchArticlesFocused = false;
|
||||
|
||||
document.getElementById('searchArticles').addEventListener('click', function () {
|
||||
@ -2663,61 +2667,71 @@ function refreshCacheStatus () {
|
||||
}
|
||||
}
|
||||
|
||||
var initServiceWorkerHandle = null;
|
||||
var serviceWorkerRegistration = null;
|
||||
|
||||
/**
|
||||
* Sends an 'init' message to the ServiceWorker and inititalizes the onmessage event
|
||||
* When the event is received, it will provide a MessageChannel port to respond to the ServiceWorker
|
||||
* It is called when the Service Worker is first activated, and also when a new archive is loaded
|
||||
* When a message is received, it will provide a MessageChannel port to respond to the ServiceWorker
|
||||
*/
|
||||
function initServiceWorkerMessaging () {
|
||||
// If no ZIM archive is loaded, return (it will be called when one is loaded)
|
||||
if (!appstate.selectedArchive) return;
|
||||
if (params.contentInjectionMode === 'serviceworker') {
|
||||
if (!(isServiceWorkerAvailable() && isMessageChannelAvailable())) {
|
||||
console.warn('Cannot initiate ServiceWorker messaging, because one or more API is unavailable!');
|
||||
return;
|
||||
};
|
||||
// Create a message listener
|
||||
navigator.serviceWorker.onmessage = function (event) {
|
||||
if (event.data.error) {
|
||||
console.error('Error in MessageChannel', event.data.error);
|
||||
throw event.data.error;
|
||||
} else if (event.data.action === 'acknowledge') {
|
||||
// The Service Worker is acknowledging receipt of init message
|
||||
console.log('SW acknowledged init message');
|
||||
serviceWorkerRegistration = true;
|
||||
refreshAPIStatus();
|
||||
} else if (event.data.action === 'askForContent') {
|
||||
// The Service Worker is asking for content. Check we have a loaded ZIM in this instance.
|
||||
// DEV: This can happen if there are various instances of the app open in different tabs or windows, and no archive has been selected in this instance.
|
||||
if (!appstate.selectedArchive) {
|
||||
console.warn('Message from SW received, but no archive is selected!');
|
||||
return;
|
||||
}
|
||||
if (event.data.error) {
|
||||
console.error('Error in MessageChannel', event.data.error);
|
||||
throw event.data.error;
|
||||
}
|
||||
if (event.data.action === 'askForContent') {
|
||||
// See below for explanation of this exception
|
||||
const videoException = appstate.selectedArchive.zimType === 'zimit' && /\/\/youtubei.*player/.test(event.data.title);
|
||||
// Check that the zimFileId in the messageChannel event data is the same as the one in the currently open archive
|
||||
// Because the SW broadcasts its request to all open tabs or windows, we need to check that the request is for this instance
|
||||
if (event.data.zimFileName !== appstate.selectedArchive.file.name) {
|
||||
console.warn('SW request does not match this insstance', '[zimFileName:' + event.data.zimFileName + ' !== ' + appstate.selectedArchive.file.name + ']');
|
||||
if (appstate.selectedArchive.zimType === 'zimit' && /\/\/youtubei.*player/.test(event.data.title)) {
|
||||
if (event.data.zimFileName !== appstate.selectedArchive.file.name && !videoException) {
|
||||
// Do nothing if the request is not for this instance
|
||||
// console.debug('SW request does not match this instance', '[zimFileName:' + event.data.zimFileName + ' !== ' + appstate.selectedArchive.file.name + ']');
|
||||
} else {
|
||||
if (videoException) {
|
||||
// DEV: This is a hack to allow YouTube videos to play in Zimit archives:
|
||||
// Because links are embedded in a nested iframe, the SW cannot identify the top-level window from which to request the ZIM content
|
||||
// Until we find a way to tell where it is coming from, we allow the request through and try to load the content
|
||||
console.warn('>>> Allowing passthrough to process YouTube video <<<');
|
||||
} else {
|
||||
return;
|
||||
// Until we find a way to tell where it is coming from, we allow the request through on all controlled clients and try to load the content
|
||||
console.warn('>>> Allowing passthrough of SW request to process Zimit video <<<');
|
||||
}
|
||||
handleMessageChannelMessage(event);
|
||||
}
|
||||
handleMessageChannelMessage(event)
|
||||
} else {
|
||||
console.error('Invalid message received', event.data);
|
||||
}
|
||||
};
|
||||
// Send the init message to the ServiceWorker
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log('Initializing SW messaging...');
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
action: 'init'
|
||||
});
|
||||
} else if (initServiceWorkerHandle) {
|
||||
console.error('The Service Worker is active but is not controlling the current page! We have to reload.');
|
||||
} else if (serviceWorkerRegistration) {
|
||||
// If this is the first time we are initiating the SW, allow Promises to complete by delaying potential reload till next tick
|
||||
console.warn('The Service Worker needs more time to load, or else the app was force-refrshed...');
|
||||
serviceWorkerRegistration = null;
|
||||
setTimeout(initServiceWorkerMessaging, 1200);
|
||||
} else {
|
||||
console.error('The Service Worker is not controlling the current page! We have to reload.');
|
||||
// Turn off failsafe, as this is a controlled reboot
|
||||
settingsStore.setItem('lastPageLoad', 'rebooting', Infinity);
|
||||
window.location.reload();
|
||||
} else {
|
||||
// If this is the first time we are initiating the SW, allow Promises to complete by delaying potential reload till next tick
|
||||
console.debug('The Service Worker needs more time to load...');
|
||||
initServiceWorkerHandle = setTimeout(initServiceWorkerMessaging, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2736,17 +2750,16 @@ function setContentInjectionMode (value) {
|
||||
if ('serviceWorker' in navigator) {
|
||||
serviceWorkerRegistration = null;
|
||||
}
|
||||
// User has switched to jQuery mode, so no longer needs ASSETS_CACHE
|
||||
// We should empty it and turn it off to prevent unnecessary space usage
|
||||
if ('caches' in window && isMessageChannelAvailable()) {
|
||||
if (isServiceWorkerAvailable() && navigator.serviceWorker.controller) {
|
||||
var channel = new MessageChannel();
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
action: { assetsCache: 'disable' }
|
||||
}, [channel.port2]);
|
||||
}
|
||||
caches.delete(cache.ASSETS_CACHE);
|
||||
}
|
||||
// User has switched to jQuery mode, so no longer needs ASSETS_CACHE on SW side (it will still be used app-side)
|
||||
// if ('caches' in window && isMessageChannelAvailable()) {
|
||||
// if (isServiceWorkerAvailable() && navigator.serviceWorker.controller) {
|
||||
// var channel = new MessageChannel();
|
||||
// navigator.serviceWorker.controller.postMessage({
|
||||
// action: { assetsCache: 'disable' }
|
||||
// }, [channel.port2]);
|
||||
// }
|
||||
// caches.delete(cache.ASSETS_CACHE);
|
||||
// }
|
||||
refreshAPIStatus();
|
||||
} else if (value === 'serviceworker') {
|
||||
if (!isServiceWorkerAvailable()) {
|
||||
@ -2839,21 +2852,13 @@ function setContentInjectionMode (value) {
|
||||
}
|
||||
$('input:radio[name=contentInjectionMode]').prop('checked', false);
|
||||
$('input:radio[name=contentInjectionMode]').filter('[value="' + value + '"]').prop('checked', true);
|
||||
params.contentInjectionMode = value;
|
||||
// Save the value in a cookie, so that to be able to keep it after a reload/restart
|
||||
// Save the value in the Settings Store, so that to be able to keep it after a reload/restart
|
||||
settingsStore.setItem('contentInjectionMode', value, Infinity);
|
||||
setWindowOpenerUI();
|
||||
// Even in JQuery mode, the PWA needs to be able to serve the app in offline mode
|
||||
setTimeout(initServiceWorkerMessaging, 600);
|
||||
}
|
||||
|
||||
// At launch, we try to set the last content injection mode (stored in Settings Store)
|
||||
setContentInjectionMode(params.contentInjectionMode);
|
||||
// var contentInjectionMode = settingsStore.getItem('contentInjectionMode');
|
||||
// if (contentInjectionMode) {
|
||||
// setContentInjectionMode(contentInjectionMode);
|
||||
// } else {
|
||||
// setContentInjectionMode('jquery');
|
||||
// }
|
||||
|
||||
/**
|
||||
* Detects whether the ServiceWorker API is available
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker
|
||||
|
@ -139,7 +139,7 @@ function count (callback) {
|
||||
var channel = new MessageChannel();
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
action: {
|
||||
assetsCache: params.assetsCache ? 'enable' : 'disable',
|
||||
assetsCache: params.assetsCache && params.contentInjectionMode === 'serviceworker' ? 'enable' : 'disable',
|
||||
appCache: params.appCache ? 'enable' : 'disable',
|
||||
checkCache: window.location.href
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user