const EMPTY_PAGE_URL = GetTestWebBasedURL("file_empty.html"); /** * When web audio changes its audible state, the sound indicator should be * updated as well, which should appear only when web audio is audible. */ add_task( async function testWebAudioAudibilityWouldAffectTheAppearenceOfTabSoundIndicator() { info(`sound indicator should appear when web audio plays audible sound`); const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, EMPTY_PAGE_URL ); await initWebAudioDocument(tab); await waitForTabSoundIndicatorAppears(tab); info(`sound indicator should disappear when suspending web audio`); await suspendWebAudio(tab); await waitForTabSoundIndicatorDisappears(tab); info(`sound indicator should appear when resuming web audio`); await resumeWebAudio(tab); await waitForTabSoundIndicatorAppears(tab); info(`sound indicator should disappear when muting web audio by docShell`); await muteWebAudioByDocShell(tab); await waitForTabSoundIndicatorDisappears(tab); info(`sound indicator should appear when unmuting web audio by docShell`); await unmuteWebAudioByDocShell(tab); await waitForTabSoundIndicatorAppears(tab); info(`sound indicator should disappear when muting web audio by gain node`); await muteWebAudioByGainNode(tab); await waitForTabSoundIndicatorDisappears(tab); info(`sound indicator should appear when unmuting web audio by gain node`); await unmuteWebAudioByGainNode(tab); await waitForTabSoundIndicatorAppears(tab); info(`sound indicator should disappear when closing web audio`); await closeWebAudio(tab); await waitForTabSoundIndicatorDisappears(tab); info("remove tab"); BrowserTestUtils.removeTab(tab); } ); add_task(async function testSoundIndicatorShouldDisappearAfterTabNavigation() { info("create a tab loading media document"); const tab = await createBlankForegroundTab(); info(`sound indicator should appear when audible web audio starts playing`); await Promise.all([ initWebAudioDocument(tab), waitForTabSoundIndicatorAppears(tab), ]); info(`sound indicator should disappear after navigating tab to blank page`); await Promise.all([ BrowserTestUtils.loadURIString(tab.linkedBrowser, "about:blank"), waitForTabSoundIndicatorDisappears(tab), ]); info("remove tab"); BrowserTestUtils.removeTab(tab); }); add_task( async function testSoundIndicatorShouldDisappearAfterWebAudioBecomesSilent() { info("create a tab loading media document"); const tab = await createBlankForegroundTab(); info(`sound indicator should appear when audible web audio starts playing`); await Promise.all([ initWebAudioDocument(tab, { duration: 0.1 }), waitForTabSoundIndicatorAppears(tab), ]); info(`sound indicator should disappear after web audio become silent`); await waitForTabSoundIndicatorDisappears(tab); info("remove tab"); BrowserTestUtils.removeTab(tab); } ); add_task(async function testNoSoundIndicatorWhenSimplyCreateAudioContext() { info("create a tab loading media document"); const tab = await createBlankForegroundTab({ needObserver: true }); info(`sound indicator should not appear when simply create an AudioContext`); await SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { content.ac = new content.AudioContext(); while (content.ac.state != "running") { info(`wait until web audio starts running`); await new Promise(r => (content.ac.onstatechange = r)); } }); ok(!tab.observer.hasEverUpdated(), "didn't ever update sound indicator"); info("remove tab"); BrowserTestUtils.removeTab(tab); }); /** * Following are helper functions */ function initWebAudioDocument(tab, { duration } = {}) { // eslint-disable-next-line no-shadow return SpecialPowers.spawn(tab.linkedBrowser, [duration], async duration => { content.ac = new content.AudioContext(); const ac = content.ac; const dest = ac.destination; const source = new content.OscillatorNode(ac); source.start(ac.currentTime); if (duration != undefined) { source.stop(ac.currentTime + duration); } // create a gain node for future muting/unmuting content.gainNode = ac.createGain(); source.connect(content.gainNode); content.gainNode.connect(dest); while (ac.state != "running") { info(`wait until web audio starts running`); await new Promise(r => (ac.onstatechange = r)); } }); } function suspendWebAudio(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { await content.ac.suspend(); }); } function resumeWebAudio(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { await content.ac.resume(); }); } function closeWebAudio(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { await content.ac.close(); }); } function muteWebAudioByDocShell(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], _ => { content.docShell.allowMedia = false; }); } function unmuteWebAudioByDocShell(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], _ => { content.docShell.allowMedia = true; }); } function muteWebAudioByGainNode(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], _ => { content.gainNode.gain.setValueAtTime(0, content.ac.currentTime); }); } function unmuteWebAudioByGainNode(tab) { return SpecialPowers.spawn(tab.linkedBrowser, [], _ => { content.gainNode.gain.setValueAtTime(1.0, content.ac.currentTime); }); }