From 8efc6a6e5d4875f4436c6cc562b7fc8e7fd074d8 Mon Sep 17 00:00:00 2001 From: Mossroy Date: Tue, 4 Apr 2017 19:27:41 +0200 Subject: [PATCH] Workaround ServiceWorker hanging after a while by keeping it alive periodically. The MessageChannel is re-created each time. It prevents the ServiceWorker from being restarted and losing its variables See #145 --- www/js/app.js | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/www/js/app.js b/www/js/app.js index db404af04..474ed4251 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -52,6 +52,15 @@ define(['jquery', 'abstractBackend', 'util', 'uiUtil', 'cookies','geometry','osa * @type Float */ var DEFAULT_MAX_DISTANCE_ARTICLES_NEARBY = 0.01; + + /** + * The delay (in milliseconds) between two "keepalive" messages + * sent to the ServiceWorker (so that it is not stopped by + * the browser, and keeps the MessageChannel to communicate + * with the application) + * @type Integer + */ + var DELAY_BETWEEN_KEEPALIVE_SERVICEWORKER = 30000; /** * @type LocalArchive|ZIMArchive @@ -302,6 +311,27 @@ define(['jquery', 'abstractBackend', 'util', 'uiUtil', 'cookies','geometry','osa var contentInjectionMode; + /** + * Send an 'init' message to the ServiceWorker with a new MessageChannel + * to initialize it, or to keep it alive. + * This MessageChannel allows a 2-way communication between the ServiceWorker + * and the application + */ + function initOrKeepAliveServiceWorker() { + if (contentInjectionMode === 'serviceworker') { + // Create a new messageChannel + var tmpMessageChannel = new MessageChannel(); + tmpMessageChannel.port1.onmessage = handleMessageChannelMessage; + // Send the init message to the ServiceWorker, with this MessageChannel as a parameter + navigator.serviceWorker.controller.postMessage({'action': 'init'}, [tmpMessageChannel.port2]); + messageChannel = tmpMessageChannel; + console.log("init message sent to ServiceWorker"); + // Schedule to do it again regularly to keep the 2-way communication alive. + // See https://github.com/kiwix/kiwix-html5/issues/145 to understand why + setTimeout(initOrKeepAliveServiceWorker, DELAY_BETWEEN_KEEPALIVE_SERVICEWORKER); + } + } + /** * Sets the given injection mode. * This involves registering (or re-enabling) the Service Worker if necessary @@ -332,13 +362,6 @@ define(['jquery', 'abstractBackend', 'util', 'uiUtil', 'cookies','geometry','osa return; } - if (!messageChannel) { - // Let's create the messageChannel for the 2-way communication - // with the Service Worker - messageChannel = new MessageChannel(); - messageChannel.port1.onmessage = handleMessageChannelMessage; - } - if (!isServiceWorkerReady()) { $('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it..."); navigator.serviceWorker.register('../service-worker.js').then(function (reg) { @@ -351,9 +374,9 @@ define(['jquery', 'abstractBackend', 'util', 'uiUtil', 'cookies','geometry','osa var serviceWorker = reg.installing || reg.waiting || reg.active; serviceWorker.addEventListener('statechange', function(statechangeevent) { if (statechangeevent.target.state === 'activated') { - console.log("try to post an init message to ServiceWorker"); - navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]); - console.log("init message sent to ServiceWorker"); + // Create the MessageChannel + // and send the 'init' message to the ServiceWorker + initOrKeepAliveServiceWorker(); } }); }, function (err) {