background.js 2.4 KiB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import delay from 'delay';
  2. async function tryUntilItWorks(func, delayBetweenTries = 1, maxTries = 10) {
  3. for (let i = 0; i < maxTries; i++) {
  4. try {
  5. return await func();
  6. } catch (err) {
  7. await delay(delayBetweenTries);
  8. continue;
  9. }
  10. }
  11. }
  12. async function insertOurAudioPlayer(tabId) {
  13. // Avoid any short glitch of sound (observed in Firefox) before our injected script executes.
  14. await browser.tabs.update(tabId, { muted: true });
  15. // Too quickly executing the script may fail (bug in Firefox), so we retry if necessary.
  16. await tryUntilItWorks(() => browser.tabs.executeScript(tabId, {
  17. runAt: 'document_end',
  18. file: '/scripts/contentscript.js',
  19. }));
  20. // Unmute again.
  21. await browser.tabs.update(tabId, { muted: false });
  22. await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, {
  23. runAt: 'document_end',
  24. file: '/assets/waveform-playlist.css',
  25. }));
  26. await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, {
  27. runAt: 'document_end',
  28. file: '/assets/main.css',
  29. }));
  30. }
  31. // Load the player on any file with an audio/* mime type.
  32. async function onHeadersReceived({ responseHeaders, url, tabId }) {
  33. const isAudio = responseHeaders.some(header =>
  34. header.name.toLowerCase() === 'content-type' && header.value.toLowerCase().startsWith('audio/')
  35. );
  36. if (isAudio) {
  37. insertOurAudioPlayer(tabId);
  38. }
  39. }
  40. browser.webRequest.onHeadersReceived.addListener(
  41. onHeadersReceived,
  42. {
  43. urls: ['<all_urls>'],
  44. types: ['main_frame'],
  45. },
  46. ['responseHeaders'],
  47. );
  48. // As files loaded through file://… URLs do not have headers, we run a separate check for these.
  49. async function onVisitFileUrl({ url, tabId }) {
  50. // I don’t know how we could neatly test whether the file is an audio file. This expression is a
  51. // pragmatic solution working for (at least) the current versions of Firefox and Chromium.
  52. const isAudioPlayerExpression =
  53. `document.body.childElementCount === 1
  54. && document.body.firstElementChild instanceof HTMLVideoElement
  55. && document.body.firstElementChild.videoHeight === 0
  56. && document.body.firstElementChild.videoWidth === 0
  57. `;
  58. const [isAudio] = await tryUntilItWorks(() => browser.tabs.executeScript(tabId, {
  59. code: isAudioPlayerExpression,
  60. runAt: 'document_idle', // (at document_end it’s not clear yet whether it’s audio or video..)
  61. }));
  62. if (isAudio) {
  63. insertOurAudioPlayer(tabId);
  64. }
  65. }
  66. browser.webNavigation.onCommitted.addListener(
  67. onVisitFileUrl,
  68. { url: [{ schemes: ['file'] }] },
  69. );