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
This commit is contained in:
Mossroy 2017-04-04 19:27:41 +02:00
parent ca6d5335ea
commit 387af28bcf

View File

@ -35,6 +35,15 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
*/ */
var MAX_SEARCH_RESULT_SIZE = 50; var MAX_SEARCH_RESULT_SIZE = 50;
/**
* 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 ZIMArchive * @type ZIMArchive
*/ */
@ -191,7 +200,6 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
else { else {
setContentInjectionMode('jquery'); setContentInjectionMode('jquery');
} }
}); });
/** /**
@ -225,6 +233,29 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
} }
var contentInjectionMode; var contentInjectionMode;
var keepAliveServiceWorkerHandle;
/**
* 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-js/issues/145 to understand why
clearTimeout(keepAliveServiceWorkerHandle);
keepAliveServiceWorkerHandle = setTimeout(initOrKeepAliveServiceWorker, DELAY_BETWEEN_KEEPALIVE_SERVICEWORKER, false);
}
}
/** /**
* Sets the given injection mode. * Sets the given injection mode.
@ -256,13 +287,6 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
return; 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()) { if (!isServiceWorkerReady()) {
$('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it..."); $('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it...");
navigator.serviceWorker.register('../service-worker.js').then(function (reg) { navigator.serviceWorker.register('../service-worker.js').then(function (reg) {
@ -275,19 +299,24 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
var serviceWorker = reg.installing || reg.waiting || reg.active; var serviceWorker = reg.installing || reg.waiting || reg.active;
serviceWorker.addEventListener('statechange', function(statechangeevent) { serviceWorker.addEventListener('statechange', function(statechangeevent) {
if (statechangeevent.target.state === 'activated') { if (statechangeevent.target.state === 'activated') {
console.log("try to post an init message to ServiceWorker"); // Create the MessageChannel
navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]); // and send the 'init' message to the ServiceWorker
console.log("init message sent to ServiceWorker"); initOrKeepAliveServiceWorker();
} }
}); });
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();
}
}, function (err) { }, function (err) {
console.error('error while registering serviceWorker', err); console.error('error while registering serviceWorker', err);
refreshAPIStatus(); refreshAPIStatus();
}); });
} else { } else {
console.log("try to re-post an init message to ServiceWorker, to re-enable it in case it was disabled"); initOrKeepAliveServiceWorker();
navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]);
console.log("init message sent to ServiceWorker");
} }
} }
$('input:radio[name=contentInjectionMode]').prop('checked', false); $('input:radio[name=contentInjectionMode]').prop('checked', false);