Bug 1643027 - Add tests for the WebRTC global mute toggles. r=pbz
☠☠ backed out by 4933a84ef41e ☠ ☠
authorMike Conley <mconley@mozilla.com>
Wed, 26 Aug 2020 21:31:53 +0000
changeset 546591 e50501b546744c27d03e013dfc2039dc01a65643
parent 546590 aa5d33b0d0b5c9b09724c765397ccf631c9f7ff3
child 546592 20f6b7bc22cf618803806dabcadb16bcb6d824c0
push id37735
push userabutkovits@mozilla.com
push dateThu, 27 Aug 2020 21:29:40 +0000
treeherdermozilla-central@109f3a4de567 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbz
bugs1643027
milestone82.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
browser/base/content/test/webrtc/browser_devices_get_user_media.js
browser/base/content/test/webrtc/browser_global_mute_toggles.js
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -27,14 +27,15 @@ skip-if = (os == "win" && !debug) || (os
 [browser_devices_get_user_media_screen.js]
 skip-if = (os == 'linux') # Bug 1503991
 [browser_devices_get_user_media_tear_off_tab.js]
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [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]
 [browser_tab_switch_warning.js]
 [browser_webrtc_hooks.js]
 [browser_devices_get_user_media_queue_request.js]
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js
@@ -712,16 +712,22 @@ var gTests = [
       info("request video, stop sharing resets video only");
       await stopAndCheckPerm(false, true);
     },
   },
 
   {
     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);
       await promise;
       await observerPromise;
       checkDeviceSelectors(false, true);
 
       let indicator = promiseIndicatorWindow();
@@ -739,33 +745,28 @@ var gTests = [
         { video: true },
         "expected camera to be shared"
       );
 
       await indicator;
       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 {
         let win = Services.wm.getMostRecentWindow(
           "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"
       );
       ok(!identityPopupHidden(), "control center should be open");
 
       gIdentityHandler._identityPopup.hidePopup();
 
new file mode 100644
--- /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
+ * <browser> 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 {<xul:browser>} 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
+ * <browser> 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 {<xul:browser>} 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);
+});