From 2c63535cac52150d6a6b0592144f6f201d9cd0bb Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Wed, 26 Aug 2020 21:31:53 +0000 Subject: [PATCH] Bug 1643027 - Add tests for the WebRTC global mute toggles. r=pbz Depends on D86619 Differential Revision: https://phabricator.services.mozilla.com/D86762 --- browser/base/content/test/webrtc/browser.ini | 1 + .../webrtc/browser_devices_get_user_media.js | 17 +- .../webrtc/browser_global_mute_toggles.js | 312 ++++++++++++++++++ 3 files changed, 322 insertions(+), 8 deletions(-) create mode 100644 browser/base/content/test/webrtc/browser_global_mute_toggles.js diff --git a/browser/base/content/test/webrtc/browser.ini b/browser/base/content/test/webrtc/browser.ini index c2c3f9fd727d..1e98b2224ea4 100644 --- a/browser/base/content/test/webrtc/browser.ini +++ b/browser/base/content/test/webrtc/browser.ini @@ -32,6 +32,7 @@ skip-if = (os == 'linux') # Bug 1503991 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js] skip-if = (os == "win" && bits == 64) # win8: bug 1334752 [browser_devices_get_user_media_unprompted_access_queue_request.js] +[browser_global_mute_toggles.js] [browser_notification_silencing.js] [browser_stop_sharing_button.js] [browser_stop_streams_on_indicator_close.js] diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media.js b/browser/base/content/test/webrtc/browser_devices_get_user_media.js index 0d719bc2765a..b78ebf449f6c 100644 --- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js +++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js @@ -717,6 +717,12 @@ var gTests = [ { desc: "test showControlCenter", run: async function checkShowControlCenter() { + if (!USING_LEGACY_INDICATOR) { + // The indicator only links to the control center for the + // legacy indicator. + return; + } + let observerPromise = expectObserverCalled("getUserMedia:request"); let promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(false, true); @@ -744,7 +750,7 @@ var gTests = [ await checkSharingUI({ video: true }); ok(identityPopupHidden(), "control center should be hidden"); - if (USING_LEGACY_INDICATOR && IS_MAC) { + if (IS_MAC) { let activeStreams = webrtcUI.getActiveStreams(true, false, false); webrtcUI.showSharingDoorhanger(activeStreams[0]); } else { @@ -752,15 +758,10 @@ var gTests = [ "Browser:WebRTCGlobalIndicator" ); - // The legacy indicator uses a different button ID when sharing - // your camera. - let buttonID = USING_LEGACY_INDICATOR - ? "audioVideoButton" - : "camera-button"; - - let elt = win.document.getElementById(buttonID); + let elt = win.document.getElementById("audioVideoButton"); EventUtils.synthesizeMouseAtCenter(elt, {}, win); } + await TestUtils.waitForCondition( () => !identityPopupHidden(), "wait for control center to open" diff --git a/browser/base/content/test/webrtc/browser_global_mute_toggles.js b/browser/base/content/test/webrtc/browser_global_mute_toggles.js new file mode 100644 index 000000000000..6284a9b82b01 --- /dev/null +++ b/browser/base/content/test/webrtc/browser_global_mute_toggles.js @@ -0,0 +1,312 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_ROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "https://example.com/" +); +const TEST_PAGE = TEST_ROOT + "get_user_media.html"; +const MUTE_TOPICS = [ + "getUserMedia:muteVideo", + "getUserMedia:unmuteVideo", + "getUserMedia:muteAudio", + "getUserMedia:unmuteAudio", +]; + +add_task(async function setup() { + let prefs = [ + [PREF_PERMISSION_FAKE, true], + [PREF_AUDIO_LOOPBACK, ""], + [PREF_VIDEO_LOOPBACK, ""], + [PREF_FAKE_STREAMS, true], + [PREF_FOCUS_SOURCE, false], + ]; + await SpecialPowers.pushPrefEnv({ set: prefs }); +}); + +/** + * Returns a Promise that resolves when the content process for + * fires the right observer notification based on the + * value of isMuted for the camera. + * + * Note: Callers must ensure that they first call + * BrowserTestUtils.startObservingTopics to monitor the mute and + * unmute observer notifications for this to work properly. + * + * @param {} browser - The browser running in the content process + * to be monitored. + * @param {Boolean} isMuted - True if the muted topic should be fired. + * @return {Promise} + * @resolves {undefined} When the notification fires. + */ +function waitForCameraMuteState(browser, isMuted) { + let topic = isMuted ? "getUserMedia:muteVideo" : "getUserMedia:unmuteVideo"; + return BrowserTestUtils.contentTopicObserved(browser.browsingContext, topic); +} + +/** + * Returns a Promise that resolves when the content process for + * fires the right observer notification based on the + * value of isMuted for the microphone. + * + * Note: Callers must ensure that they first call + * BrowserTestUtils.startObservingTopics to monitor the mute and + * unmute observer notifications for this to work properly. + * + * @param {} browser - The browser running in the content process + * to be monitored. + * @param {Boolean} isMuted - True if the muted topic should be fired. + * @return {Promise} + * @resolves {undefined} When the notification fires. + */ +function waitForMicrophoneMuteState(browser, isMuted) { + let topic = isMuted ? "getUserMedia:muteAudio" : "getUserMedia:unmuteAudio"; + return BrowserTestUtils.contentTopicObserved(browser.browsingContext, topic); +} + +/** + * Tests that the global mute toggles fire the right observer + * notifications in pre-existing content processes. + */ +add_task(async function test_notifications() { + await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => { + let indicatorPromise = promiseIndicatorWindow(); + + await shareDevices( + browser, + true /* camera */, + true /* microphone */, + false /* screen */ + ); + + let indicator = await indicatorPromise; + let doc = indicator.document; + + let microphoneMute = doc.getElementById("microphone-mute-toggle"); + let cameraMute = doc.getElementById("camera-mute-toggle"); + + Assert.ok( + !microphoneMute.checked, + "Microphone toggle should not start checked." + ); + Assert.ok(!cameraMute.checked, "Camera toggle should not start checked."); + + await BrowserTestUtils.startObservingTopics( + browser.browsingContext, + MUTE_TOPICS + ); + + info("Muting microphone..."); + let microphoneMuted = waitForMicrophoneMuteState(browser, true); + microphoneMute.click(); + await microphoneMuted; + info("Microphone successfully muted."); + + info("Muting camera..."); + let cameraMuted = waitForCameraMuteState(browser, true); + cameraMute.click(); + await cameraMuted; + info("Camera successfully muted."); + + Assert.ok( + microphoneMute.checked, + "Microphone toggle should now be checked." + ); + Assert.ok(cameraMute.checked, "Camera toggle should now be checked."); + + info("Unmuting microphone..."); + let microphoneUnmuted = waitForMicrophoneMuteState(browser, false); + microphoneMute.click(); + await microphoneUnmuted; + info("Microphone successfully unmuted."); + + info("Unmuting camera..."); + let cameraUnmuted = waitForCameraMuteState(browser, false); + cameraMute.click(); + await cameraUnmuted; + info("Camera successfully unmuted."); + + await BrowserTestUtils.stopObservingTopics( + browser.browsingContext, + MUTE_TOPICS + ); + }); +}); + +/** + * Tests that if sharing stops while muted, and the indicator closes, + * then the mute state is reset. + */ +add_task(async function test_closing_indicator_resets_mute() { + await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => { + let indicatorPromise = promiseIndicatorWindow(); + + await shareDevices( + browser, + true /* camera */, + true /* microphone */, + false /* screen */ + ); + + let indicator = await indicatorPromise; + let doc = indicator.document; + + let microphoneMute = doc.getElementById("microphone-mute-toggle"); + let cameraMute = doc.getElementById("camera-mute-toggle"); + + Assert.ok( + !microphoneMute.checked, + "Microphone toggle should not start checked." + ); + Assert.ok(!cameraMute.checked, "Camera toggle should not start checked."); + + await BrowserTestUtils.startObservingTopics( + browser.browsingContext, + MUTE_TOPICS + ); + + info("Muting microphone..."); + let microphoneMuted = waitForMicrophoneMuteState(browser, true); + microphoneMute.click(); + await microphoneMuted; + info("Microphone successfully muted."); + + info("Muting camera..."); + let cameraMuted = waitForCameraMuteState(browser, true); + cameraMute.click(); + await cameraMuted; + info("Camera successfully muted."); + + Assert.ok( + microphoneMute.checked, + "Microphone toggle should now be checked." + ); + Assert.ok(cameraMute.checked, "Camera toggle should now be checked."); + + let allUnmuted = Promise.all([ + waitForMicrophoneMuteState(browser, false), + waitForCameraMuteState(browser, false), + ]); + + await closeStream(); + await allUnmuted; + + await BrowserTestUtils.stopObservingTopics( + browser.browsingContext, + MUTE_TOPICS + ); + }); +}); + +/** + * Test that if the global mute state is set, then newly created + * content processes also have their tracks muted after sending + * a getUserMedia request. + */ +add_task(async function test_new_processes() { + let tab1 = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: TEST_PAGE, + }); + let browser1 = tab1.linkedBrowser; + + let indicatorPromise = promiseIndicatorWindow(); + + await shareDevices( + browser1, + true /* camera */, + true /* microphone */, + false /* screen */ + ); + + let indicator = await indicatorPromise; + let doc = indicator.document; + + let microphoneMute = doc.getElementById("microphone-mute-toggle"); + let cameraMute = doc.getElementById("camera-mute-toggle"); + + Assert.ok( + !microphoneMute.checked, + "Microphone toggle should not start checked." + ); + Assert.ok(!cameraMute.checked, "Camera toggle should not start checked."); + + await BrowserTestUtils.startObservingTopics( + browser1.browsingContext, + MUTE_TOPICS + ); + + info("Muting microphone..."); + let microphoneMuted = waitForMicrophoneMuteState(browser1, true); + microphoneMute.click(); + await microphoneMuted; + info("Microphone successfully muted."); + + info("Muting camera..."); + let cameraMuted = waitForCameraMuteState(browser1, true); + cameraMute.click(); + await cameraMuted; + info("Camera successfully muted."); + + // We'll make sure a new process is being launched by observing + // for the ipc:content-created notification. + let processLaunched = TestUtils.topicObserved("ipc:content-created"); + + let tab2 = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: TEST_PAGE, + forceNewProcess: true, + }); + let browser2 = tab2.linkedBrowser; + + await processLaunched; + + await BrowserTestUtils.startObservingTopics( + browser2.browsingContext, + MUTE_TOPICS + ); + + let microphoneMuted2 = waitForMicrophoneMuteState(browser2, true); + let cameraMuted2 = waitForCameraMuteState(browser2, true); + info("Sharing the microphone and camera from a new process."); + await shareDevices( + browser2, + true /* camera */, + true /* microphone */, + false /* screen */ + ); + await Promise.all([microphoneMuted2, cameraMuted2]); + + info("Unmuting microphone..."); + let microphoneUnmuted = Promise.all([ + waitForMicrophoneMuteState(browser1, false), + waitForMicrophoneMuteState(browser2, false), + ]); + microphoneMute.click(); + await microphoneUnmuted; + info("Microphone successfully unmuted."); + + info("Unmuting camera..."); + let cameraUnmuted = Promise.all([ + waitForCameraMuteState(browser1, false), + waitForCameraMuteState(browser2, false), + ]); + cameraMute.click(); + await cameraUnmuted; + info("Camera successfully unmuted."); + + await BrowserTestUtils.stopObservingTopics( + browser1.browsingContext, + MUTE_TOPICS + ); + + await BrowserTestUtils.stopObservingTopics( + browser2.browsingContext, + MUTE_TOPICS + ); + + BrowserTestUtils.removeTab(tab2); + BrowserTestUtils.removeTab(tab1); +});