import delay from 'delay'; async function tryUntilItWorks(func, delayBetweenTries = 1, maxTries = 10) { for (let i = 0; i < maxTries; i++) { try { return await func(); } catch (err) { await delay(delayBetweenTries); continue; } } } async function insertOurAudioPlayer(tabId) { // Avoid any short glitch of sound (observed in Firefox) before our injected script executes. await browser.tabs.update(tabId, { muted: true }); // Too quickly executing the script may fail (bug in Firefox), so we retry if necessary. await tryUntilItWorks(() => browser.tabs.executeScript(tabId, { runAt: 'document_end', file: '/scripts/contentscript.js', })); // Unmute again. await browser.tabs.update(tabId, { muted: false }); await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { runAt: 'document_end', file: '/assets/waveform-playlist.css', })); await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { runAt: 'document_end', file: '/assets/main.css', })); } // Load the player on any file with an audio/* mime type. async function onHeadersReceived({ responseHeaders, url, tabId }) { const isAudio = responseHeaders.some(header => header.name.toLowerCase() === 'content-type' && header.value.toLowerCase().startsWith('audio/') ); if (isAudio) { insertOurAudioPlayer(tabId); } } browser.webRequest.onHeadersReceived.addListener( onHeadersReceived, { urls: [''], types: ['main_frame'], }, ['responseHeaders'], ); // As files loaded through file://… URLs do not have headers, we run a separate check for these. async function onVisitFileUrl({ url, tabId }) { // I don’t know how we could neatly test whether the file is an audio file. This expression is a // pragmatic solution working for (at least) the current versions of Firefox and Chromium. const isAudioPlayerExpression = `document.body.childElementCount === 1 && document.body.firstElementChild instanceof HTMLVideoElement && document.body.firstElementChild.videoHeight === 0 && document.body.firstElementChild.videoWidth === 0 `; const [isAudio] = await tryUntilItWorks(() => browser.tabs.executeScript(tabId, { code: isAudioPlayerExpression, runAt: 'document_idle', // (at document_end it’s not clear yet whether it’s audio or video..) })); if (isAudio) { insertOurAudioPlayer(tabId); } } browser.webNavigation.onCommitted.addListener( onVisitFileUrl, { url: [{ schemes: ['file'] }] }, );