Bug 1224453 - Add a test for WebRTC sharing indicators in frames. draft
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 16 Nov 2017 00:06:23 +0100
changeset 699050 6649266d22cdead12ed6095bac9ae9fd51235bde
parent 699049 e8406ec36e6c715fed0ec8aa449defa4cf0d89ec
child 740512 d0a26a1c377c298ed902dc93beacadedb78dcc74
push id89440
push userbmo:jhofmann@mozilla.com
push dateThu, 16 Nov 2017 12:49:54 +0000
bugs1224453
milestone59.0a1
Bug 1224453 - Add a test for WebRTC sharing indicators in frames. MozReview-Commit-ID: H1tyfryNrm
browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
browser/base/content/test/webrtc/get_user_media_in_frame.html
browser/base/content/test/webrtc/head.js
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
@@ -203,15 +203,67 @@ var gTests = [
     Assert.deepEqual((await getMediaCaptureState()), {audio: true, video: true},
                      "expected camera and microphone to be shared");
 
     await indicator;
     await checkSharingUI({video: true, audio: true});
 
     await reloadAndAssertClosedStreams();
   }
-}
+},
+
+{
+  desc: "getUserMedia audio+video: streaming from a frame with a different origin is shown in the control center",
+  run: async function checkAudioVideo() {
+    let promise = promisePopupNotificationShown("webRTC-shareDevices");
+    await promiseRequestDevice(true, true, "frame3");
+    await promise;
+    await expectObserverCalled("getUserMedia:request");
+
+    is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
+       "webRTC-shareDevices-notification-icon", "anchored to device icon");
+    checkDeviceSelectors(true, true);
+    is(PopupNotifications.panel.firstChild.getAttribute("popupid"),
+       "webRTC-shareDevices", "panel using devices icon");
+
+    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;
+    // First check the icon above the control center (i) icon.
+    let identityBox = document.getElementById("identity-box");
+    ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
+    let sharing = identityBox.getAttribute("sharing");
+    is(sharing, "camera", "showing camera icon on the control center icon");
+
+    // Then check the sharing indicators inside the control center panel.
+    let popupshown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
+    identityBox.click();
+    await popupshown;
+
+    let nestedPermissionsList = document.getElementById("identity-popup-nested-permission-list");
+    is(nestedPermissionsList.children.length, 1,
+       "Should have one subsection for nested frames with permissions.");
+
+    for (let id of ["microphone", "camera"]) {
+      let icon = nestedPermissionsList.querySelectorAll(
+        ".identity-popup-permission-icon." + id + "-icon");
+      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");
+    }
+
+    gIdentityHandler._identityPopup.hidden = true;
+
+    await closeStream(false, "frame3");
+  }
+},
 
 ];
 
 add_task(async function test() {
   await runTests(gTests, { relativeURI: "get_user_media_in_frame.html" });
 });
--- a/browser/base/content/test/webrtc/get_user_media_in_frame.html
+++ b/browser/base/content/test/webrtc/get_user_media_in_frame.html
@@ -20,17 +20,33 @@ try {
 function message(m) {
   // eslint-disable-next-line no-unsanitized/property
   document.getElementById("message").innerHTML = m;
   window.parent.postMessage(m, "*");
 }
 
 var gStreams = [];
 
-function requestDevice(aAudio, aVideo, aShare) {
+function requestDevice(aAudio, aVideo, aShare, aBadDevice = false, aFrameId) {
+  if (aFrameId) {
+    let frame = document.getElementById(aFrameId);
+    // Avoid cross-origin restrictions for our frame.
+    let win = SpecialPowers.wrap(frame).wrappedJSObject.contentWindow;
+    // FIXME: For some reason this intermittently fails with
+    // "global.requestDevice is not a function" without this hack.
+    (function checkRequestDevice() {
+      if (win.requestDevice) {
+        win.requestDevice(aAudio, aVideo, aShare, aBadDevice);
+      } else {
+        setTimeout(checkRequestDevice, 10);
+      }
+    })();
+    return;
+  }
+
   var opts = {video: aVideo, audio: aAudio};
   if (aShare) {
     opts.video = {
       mozMediaSource: aShare,
       mediaSource: aShare
     };
   } else if (useFakeStreams) {
     opts.fake = true;
@@ -39,23 +55,30 @@ function requestDevice(aAudio, aVideo, a
   window.navigator.mediaDevices.getUserMedia(opts)
     .then(stream => {
       gStreams.push(stream);
       message("ok");
     }, err => message("error: " + err));
 }
 message("pending");
 
-function closeStream() {
+function closeStream(aFrameId) {
+  if (aFrameId) {
+    let frame = SpecialPowers.wrap(document.getElementById(aFrameId));
+    frame.wrappedJSObject.contentWindow.closeStream();
+    return;
+  }
+
   for (let stream of gStreams) {
     if (stream) {
       stream.getTracks().forEach(t => t.stop());
       stream = null;
     }
   }
   gStreams = [];
   message("closed");
 }
 </script>
 <iframe id="frame1" src="get_user_media.html"></iframe>
 <iframe id="frame2" src="get_user_media.html"></iframe>
+<iframe id="frame3" src="https://example.org/browser/browser/base/content/test/webrtc/get_user_media.html"></iframe>
 </body>
 </html>
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -388,37 +388,32 @@ async function stopSharing(aType = "came
 function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType,
                               aBrowser = gBrowser.selectedBrowser,
                               aBadDevice = false) {
   info("requesting devices");
   return ContentTask.spawn(aBrowser,
                            {aRequestAudio, aRequestVideo, aFrameId, aType, aBadDevice},
                            async function(args) {
     let global = content.wrappedJSObject;
-    if (args.aFrameId)
-      global = global.document.getElementById(args.aFrameId).contentWindow;
-    global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType, args.aBadDevice);
+    global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType, args.aBadDevice, args.aFrameId);
   });
 }
 
 async function closeStream(aAlreadyClosed, aFrameId) {
   await expectNoObserverCalled();
 
   let promises;
   if (!aAlreadyClosed) {
     promises = [promiseObserverCalled("recording-device-events"),
                 promiseObserverCalled("recording-window-ended")];
   }
 
   info("closing the stream");
   await ContentTask.spawn(gBrowser.selectedBrowser, aFrameId, async function(contentFrameId) {
-    let global = content.wrappedJSObject;
-    if (contentFrameId)
-      global = global.document.getElementById(contentFrameId).contentWindow;
-    global.closeStream();
+    content.wrappedJSObject.closeStream(contentFrameId);
   });
 
   if (promises)
     await Promise.all(promises);
 
   await assertWebRTCIndicatorStatus(null);
 }
 
@@ -467,17 +462,20 @@ async function checkSharingUI(aExpected,
   if (aExpected.screen)
     is(sharing, "screen", "showing screen icon on the control center icon");
   else if (aExpected.video)
     is(sharing, "camera", "showing camera icon on the control center icon");
   else if (aExpected.audio)
     is(sharing, "microphone", "showing mic icon on the control center icon");
 
   // Then check the sharing indicators inside the control center panel.
+  let popupshown = BrowserTestUtils.waitForEvent(aWin.gIdentityHandler._identityPopup, "popupshown");
   identityBox.click();
+  await popupshown;
+
   let permissions = doc.getElementById("identity-popup-permission-list");
   for (let id of ["microphone", "camera", "screen"]) {
     let convertId = idToConvert => {
       if (idToConvert == "camera")
         return "video";
       if (idToConvert == "microphone")
         return "audio";
       return idToConvert;