Bug 1333468 - Part 2 - Add tests for paused/disabled sharing state indicators. r=florian
authorJohann Hofmann <jhofmann@mozilla.com>
Tue, 13 Feb 2018 10:09:07 +0100
changeset 759118 12d5f88351d1ec0c808d51e84ce683f40cf332fc
parent 759117 4052f793407410421c8c3c9315872ba762dc34ff
child 759119 24bbb77b58a2a04cb1e5f9664a6a02f31777bbc6
push id100272
push userrwood@mozilla.com
push dateFri, 23 Feb 2018 18:27:33 +0000
reviewersflorian
bugs1333468
milestone60.0a1
Bug 1333468 - Part 2 - Add tests for paused/disabled sharing state indicators. r=florian MozReview-Commit-ID: G1CTAefOPNB
browser/base/content/test/webrtc/browser.ini
browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js
browser/base/content/test/webrtc/head.js
browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -7,16 +7,17 @@ support-files =
 
 [browser_devices_get_user_media.js]
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
 skip-if = debug # bug 1369731
 [browser_devices_get_user_media_multi_process.js]
 skip-if = debug && (os == "win" || os == "mac") # bug 1393761
+[browser_devices_get_user_media_paused.js]
 [browser_devices_get_user_media_screen.js]
 skip-if = (os == "win" && ccov) # bug 1421724
 [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]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js
@@ -0,0 +1,155 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function setTrackEnabled(audio, video) {
+  return ContentTask.spawn(gBrowser.selectedBrowser, {audio, video}, function(args) {
+    let stream = content.wrappedJSObject.gStreams[0];
+    if (args.audio != null) {
+      stream.getAudioTracks()[0].enabled = args.audio;
+    }
+    if (args.video != null) {
+      stream.getVideoTracks()[0].enabled = args.video;
+    }
+  });
+}
+
+var gTests = [
+{
+  desc: "getUserMedia audio+video: disabling the stream shows the paused indicator",
+  run: async function checkPaused() {
+    let promise = promisePopupNotificationShown("webRTC-shareDevices");
+    await promiseRequestDevice(true, true);
+    await promise;
+    await expectObserverCalled("getUserMedia:request");
+    checkDeviceSelectors(true, true);
+
+    let indicator = promiseIndicatorWindow();
+    await promiseMessage("ok", () => {
+      PopupNotifications.panel.firstChild.button.click();
+    });
+    await expectObserverCalled("getUserMedia:response:allow");
+    await expectObserverCalled("recording-device-events");
+    Assert.deepEqual((await getMediaCaptureState()), {audio: true, video: true},
+                     "expected camera and microphone to be shared");
+    await indicator;
+    await checkSharingUI({
+      video: STATE_CAPTURE_ENABLED,
+      audio: STATE_CAPTURE_ENABLED,
+    });
+
+    // Disable both audio and video.
+    await setTrackEnabled(false, false);
+
+    // It sometimes takes a bit longer before the change propagates to the UI,
+    // wait for it to avoid intermittents.
+    await BrowserTestUtils.waitForCondition(
+      () => window.gIdentityHandler._sharingState.camera == STATE_CAPTURE_DISABLED,
+      "video should be disabled"
+    );
+
+    await expectObserverCalled("recording-device-events", 2);
+
+    // The identity UI should show both as disabled.
+    await checkSharingUI({
+      video: STATE_CAPTURE_DISABLED,
+      audio: STATE_CAPTURE_DISABLED,
+    });
+
+    // Enable only audio again.
+    await setTrackEnabled(true);
+
+    await BrowserTestUtils.waitForCondition(
+      () => window.gIdentityHandler._sharingState.microphone == STATE_CAPTURE_ENABLED,
+      "audio should be enabled"
+    );
+
+    await expectObserverCalled("recording-device-events");
+
+    // The identity UI should show only video as disabled.
+    await checkSharingUI({
+      video: STATE_CAPTURE_DISABLED,
+      audio: STATE_CAPTURE_ENABLED,
+    });
+
+    // Enable video again.
+    await setTrackEnabled(null, true);
+
+    await BrowserTestUtils.waitForCondition(
+      () => window.gIdentityHandler._sharingState.camera == STATE_CAPTURE_ENABLED,
+      "video should be enabled"
+    );
+
+    await expectObserverCalled("recording-device-events");
+
+    // Both streams should show as running.
+    await checkSharingUI({
+      video: STATE_CAPTURE_ENABLED,
+      audio: STATE_CAPTURE_ENABLED,
+    });
+    await closeStream();
+  }
+},
+
+{
+  desc: "getUserMedia screen: disabling the stream shows the paused indicator",
+  run: async function checkScreenPaused() {
+    let promise = promisePopupNotificationShown("webRTC-shareDevices");
+    await promiseRequestDevice(false, true, null, "screen");
+    await promise;
+    await expectObserverCalled("getUserMedia:request");
+
+    is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
+       "webRTC-shareScreen-notification-icon", "anchored to device icon");
+    checkDeviceSelectors(false, false, true);
+    let notification = PopupNotifications.panel.firstChild;
+    let iconclass = notification.getAttribute("iconclass");
+    ok(iconclass.includes("screen-icon"), "panel using screen icon");
+
+    let menulist =
+      document.getElementById("webRTC-selectWindow-menulist");
+    menulist.getItemAtIndex(2).doCommand();
+
+    let indicator = promiseIndicatorWindow();
+    await promiseMessage("ok", () => {
+      PopupNotifications.panel.firstChild.button.click();
+    });
+    await expectObserverCalled("getUserMedia:response:allow");
+    await expectObserverCalled("recording-device-events");
+    Assert.deepEqual((await getMediaCaptureState()), {screen: "Screen"},
+                     "expected screen to be shared");
+
+    await indicator;
+    await checkSharingUI({screen: "Screen"});
+
+    await setTrackEnabled(null, false);
+
+    // It sometimes takes a bit longer before the change propagates to the UI,
+    // wait for it to avoid intermittents.
+    await BrowserTestUtils.waitForCondition(
+      () => window.gIdentityHandler._sharingState.screen == "ScreenPaused",
+      "screen should be disabled"
+    );
+    await expectObserverCalled("recording-device-events");
+    await checkSharingUI({screen: "ScreenPaused"}, window, {screen: "Screen"});
+
+    await setTrackEnabled(null, true);
+
+    await BrowserTestUtils.waitForCondition(
+      () => window.gIdentityHandler._sharingState.screen == "Screen",
+      "screen should be enabled"
+    );
+    await expectObserverCalled("recording-device-events");
+    await checkSharingUI({screen: "Screen"});
+  }
+},
+];
+
+add_task(async function test() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["media.getusermedia.camera.off_while_disabled.delay_ms", 0],
+    ["media.getusermedia.microphone.off_while_disabled.delay_ms", 0],
+  ]});
+
+  SimpleTest.requestCompleteLog();
+  await runTests(gTests);
+});
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -1,15 +1,17 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/SitePermissions.jsm");
 
-
 const PREF_PERMISSION_FAKE = "media.navigator.permission.fake";
 const CONTENT_SCRIPT_HELPER = getRootDirectory(gTestPath) + "get_user_media_content_script.js";
 
+const STATE_CAPTURE_ENABLED = Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED;
+const STATE_CAPTURE_DISABLED = Ci.nsIMediaManagerService.STATE_CAPTURE_DISABLED;
+
 function waitForCondition(condition, nextTest, errorMsg, retryTimes) {
   retryTimes = typeof retryTimes !== "undefined" ? retryTimes : 30;
   var tries = 0;
   var interval = setInterval(function() {
     if (tries >= retryTimes) {
       ok(false, errorMsg);
       moveOn();
     }
@@ -454,27 +456,38 @@ function checkDeviceSelectors(aAudio, aV
     ok(!screenSelector.hidden, "screen selector visible");
   else
     ok(screenSelector.hidden, "screen selector hidden");
 }
 
 // aExpected is for the current tab,
 // aExpectedGlobal is for all tabs.
 async function checkSharingUI(aExpected, aWin = window, aExpectedGlobal = null) {
+  function isPaused(streamState) {
+    if (typeof streamState == "string") {
+      return streamState.includes("Paused");
+    }
+    return streamState == STATE_CAPTURE_DISABLED;
+  }
+
   let doc = aWin.document;
   // First check the icon above the control center (i) icon.
   let identityBox = doc.getElementById("identity-box");
   ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
   let sharing = identityBox.getAttribute("sharing");
   if (aExpected.screen)
-    is(sharing, "screen", "showing screen icon on the control center icon");
+    is(sharing, "screen", "showing screen icon in the identity block");
   else if (aExpected.video)
-    is(sharing, "camera", "showing camera icon on the control center icon");
+    is(sharing, "camera", "showing camera icon in the identity block");
   else if (aExpected.audio)
-    is(sharing, "microphone", "showing mic icon on the control center icon");
+    is(sharing, "microphone", "showing mic icon in the identity block");
+
+  let allStreamsPaused = Object.values(aExpected).every(isPaused);
+  is(identityBox.hasAttribute("paused"), allStreamsPaused,
+     "sharing icon(s) should be in paused state when paused");
 
   // Then check the sharing indicators inside the control center panel.
   identityBox.click();
   let permissions = doc.getElementById("identity-popup-permission-list");
   for (let id of ["microphone", "camera", "screen"]) {
     let convertId = idToConvert => {
       if (idToConvert == "camera")
         return "video";
@@ -484,17 +497,18 @@ async function checkSharingUI(aExpected,
     };
     let expected = aExpected[convertId(id)];
     is(!!aWin.gIdentityHandler._sharingState[id], !!expected,
        "sharing state for " + id + " as expected");
     let icon = permissions.querySelectorAll(
       ".identity-popup-permission-icon." + id + "-icon");
     if (expected) {
       is(icon.length, 1, "should show " + id + " icon in control center panel");
-      ok(icon[0].classList.contains("in-use"), "icon should have the in-use class");
+      is(icon[0].classList.contains("in-use"), expected && !isPaused(expected),
+         "icon should have the in-use class, unless paused");
     } else if (!icon.length) {
       ok(true, "should not show " + id + " icon in the control center panel");
     } else {
       // This will happen if there are persistent permissions set.
       ok(!icon[0].classList.contains("in-use"),
          "if shown, the " + id + " icon should not have the in-use class");
       is(icon.length, 1, "should not show more than 1 " + id + " icon");
     }
--- a/browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js
@@ -4,17 +4,22 @@ add_task(async function test_tabs_mediaI
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.webextensions.tabhide.enabled", true]],
   });
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
   // setBrowserSharing is called when a request for media icons occurs.  We're
   // just testing that extension tabs get the info and are updated when it is
   // called.
-  gBrowser.setBrowserSharing(tab.linkedBrowser, {screen: "Window", microphone: true, camera: true});
+  gBrowser.setBrowserSharing(tab.linkedBrowser, {
+    sharing: "screen",
+    screen: "Window",
+    microphone: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
+    camera: Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED,
+  });
 
   async function background() {
     let tabs = await browser.tabs.query({microphone: true});
     let testTab = tabs[0];
 
     let state = testTab.sharingState;
     browser.test.assertTrue(state.camera, "sharing camera was turned on");
     browser.test.assertTrue(state.microphone, "sharing mic was turned on");