diff --git a/addons/bus/static/src/services/bus_service.js b/addons/bus/static/src/services/bus_service.js index 71640baa450..22d6555dd94 100644 --- a/addons/bus/static/src/services/bus_service.js +++ b/addons/bus/static/src/services/bus_service.js @@ -25,25 +25,11 @@ export const busService = { async start(env, { multi_tab: multiTab, "bus.parameters": params }) { const bus = new EventBus(); + let worker; let isActive = false; - let workerURL = `${params.serverURL}/bus/websocket_worker_bundle?v=${WORKER_VERSION}`; - if (params.serverURL !== window.origin) { - // Bus service is loaded from a different origin than the bundle - // URL. The Worker expects an URL from this origin, give it a base64 - // URL that will then load the bundle via "importScripts" which - // allows cross origin. - const source = `importScripts("${workerURL}");`; - workerURL = 'data:application/javascript;base64,' + window.btoa(source); - } - const workerClass = 'SharedWorker' in window && !isIosApp() ? browser.SharedWorker : browser.Worker; - const worker = new workerClass(workerURL, { - name: 'SharedWorker' in window && !isIosApp() ? 'odoo:websocket_shared_worker' : 'odoo:websocket_worker', - }); + let isInitialized = false; + let isUsingSharedWorker = 'SharedWorker' in browser && !isIosApp(); const connectionInitializedDeferred = new Deferred(); - worker.addEventListener("error", (e) => { - connectionInitializedDeferred.resolve(); - console.warn("Error while loading 'bus_service' SharedWorker"); - }); /** * Send a message to the worker. @@ -55,7 +41,7 @@ export const busService = { */ function send(action, data) { const message = { action, data }; - if ('SharedWorker' in window && !isIosApp()) { + if (isUsingSharedWorker) { worker.port.postMessage(message); } else { worker.postMessage(message); @@ -76,6 +62,7 @@ export const busService = { multiTab.setSharedValue('last_notification_id', data[data.length - 1].id); data = data.map(notification => notification.message); } else if (type === 'initialized') { + isInitialized = true; connectionInitializedDeferred.resolve(); return; } @@ -108,13 +95,46 @@ export const busService = { }); } - if ('SharedWorker' in window && !isIosApp()) { - worker.port.start(); - worker.port.addEventListener('message', handleMessage); - } else { - worker.addEventListener('message', handleMessage); + /** + * Start the "bus_service" worker. + */ + function startWorker() { + let workerURL = `${params.serverURL}/bus/websocket_worker_bundle?v=${WORKER_VERSION}`; + if (params.serverURL !== window.origin) { + // Bus service is loaded from a different origin than the bundle + // URL. The Worker expects an URL from this origin, give it a base64 + // URL that will then load the bundle via "importScripts" which + // allows cross origin. + const source = `importScripts("${workerURL}");`; + workerURL = 'data:application/javascript;base64,' + window.btoa(source); + } + const workerClass = isUsingSharedWorker ? browser.SharedWorker : browser.Worker; + worker = new workerClass(workerURL, { + name: isUsingSharedWorker + ? 'odoo:websocket_shared_worker' + : 'odoo:websocket_worker', + }); + worker.addEventListener("error", (e) => { + if (!isInitialized && workerClass === browser.SharedWorker) { + console.warn( + 'Error while loading "bus_service" SharedWorker, fallback on Worker.' + ); + isUsingSharedWorker = false; + startWorker(); + } else if (!isInitialized) { + isInitialized = true; + connectionInitializedDeferred.resolve(); + console.warn('Bus service failed to initialized.'); + } + }); + if (isUsingSharedWorker) { + worker.port.start(); + worker.port.addEventListener('message', handleMessage); + } else { + worker.addEventListener('message', handleMessage); + } + initializeWorkerConnection(); } - initializeWorkerConnection(); browser.addEventListener('pagehide', ({ persisted }) => { if (!persisted) { // Page is gonna be unloaded, disconnect this client @@ -128,6 +148,7 @@ export const busService = { } }); browser.addEventListener('offline', () => send('stop')); + startWorker(); await connectionInitializedDeferred; return { diff --git a/addons/bus/static/tests/bus_tests.js b/addons/bus/static/tests/bus_tests.js index 4d162754028..e16ffa2c125 100644 --- a/addons/bus/static/tests/bus_tests.js +++ b/addons/bus/static/tests/bus_tests.js @@ -547,6 +547,38 @@ QUnit.module('Bus', { "reconnect", ]); }); + + QUnit.test( + "Fallback on simple worker when shared worker failed to initialize", + async function (assert) { + const originalSharedWorker = browser.SharedWorker; + const originalWorker = browser.Worker; + patchWithCleanup(browser, { + SharedWorker: function (url, options) { + assert.step("shared-worker creation"); + const sw = new originalSharedWorker(url, options); + // Simulate error during shared worker creation. + setTimeout(() => sw.dispatchEvent(new Event("error"))); + return sw; + }, + Worker: function (url, options) { + assert.step("worker creation"); + return new originalWorker(url, options); + }, + }, { pure: true }); + patchWithCleanup(window.console, { + warn(message) { + assert.step(message); + }, + }) + await makeTestEnv(); + assert.verifySteps([ + "shared-worker creation", + "Error while loading \"bus_service\" SharedWorker, fallback on Worker.", + "worker creation", + ]); + } + ); }); });