browser/base/content/test/general/browser_devices_get_user_media.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 const kObservedTopics = [
     6   "getUserMedia:response:allow",
     7   "getUserMedia:revoke",
     8   "getUserMedia:response:deny",
     9   "getUserMedia:request",
    10   "recording-device-events",
    11   "recording-window-ended"
    12 ];
    14 const PREF_PERMISSION_FAKE = "media.navigator.permission.fake";
    16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    17 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
    18                                    "@mozilla.org/mediaManagerService;1",
    19                                    "nsIMediaManagerService");
    21 var gObservedTopics = {};
    22 function observer(aSubject, aTopic, aData) {
    23   if (!(aTopic in gObservedTopics))
    24     gObservedTopics[aTopic] = 1;
    25   else
    26     ++gObservedTopics[aTopic];
    27 }
    29 function promiseObserverCalled(aTopic, aAction) {
    30   let deferred = Promise.defer();
    32   Services.obs.addObserver(function observer() {
    33     ok(true, "got " + aTopic + " notification");
    34     Services.obs.removeObserver(observer, aTopic);
    36     if (kObservedTopics.indexOf(aTopic) != -1) {
    37       if (!(aTopic in gObservedTopics))
    38         gObservedTopics[aTopic] = -1;
    39       else
    40         --gObservedTopics[aTopic];
    41     }
    43     deferred.resolve();
    44   }, aTopic, false);
    46   if (aAction)
    47     aAction();
    49   return deferred.promise;
    50 }
    52 function expectObserverCalled(aTopic) {
    53   is(gObservedTopics[aTopic], 1, "expected notification " + aTopic);
    54   if (aTopic in gObservedTopics)
    55     --gObservedTopics[aTopic];
    56 }
    58 function expectNoObserverCalled() {
    59   for (let topic in gObservedTopics) {
    60     if (gObservedTopics[topic])
    61       is(gObservedTopics[topic], 0, topic + " notification unexpected");
    62   }
    63   gObservedTopics = {}
    64 }
    66 function promiseMessage(aMessage, aAction) {
    67   let deferred = Promise.defer();
    69   content.addEventListener("message", function messageListener(event) {
    70     content.removeEventListener("message", messageListener);
    71     is(event.data, aMessage, "received " + aMessage);
    72     if (event.data == aMessage)
    73       deferred.resolve();
    74     else
    75       deferred.reject();
    76   });
    78   if (aAction)
    79     aAction();
    81   return deferred.promise;
    82 }
    84 function promisePopupNotificationShown(aName, aAction) {
    85   let deferred = Promise.defer();
    87   PopupNotifications.panel.addEventListener("popupshown", function popupNotifShown() {
    88     PopupNotifications.panel.removeEventListener("popupshown", popupNotifShown);
    90     ok(!!PopupNotifications.getNotification(aName), aName + " notification shown");
    91     ok(PopupNotifications.isPanelOpen, "notification panel open");
    92     ok(!!PopupNotifications.panel.firstChild, "notification panel populated");
    94     deferred.resolve();
    95   });
    97   if (aAction)
    98     aAction();
   100   return deferred.promise;
   101 }
   103 function promisePopupNotification(aName) {
   104   let deferred = Promise.defer();
   106   waitForCondition(() => PopupNotifications.getNotification(aName),
   107                    () => {
   108     ok(!!PopupNotifications.getNotification(aName),
   109        aName + " notification appeared");
   111     deferred.resolve();
   112   }, "timeout waiting for popup notification " + aName);
   114   return deferred.promise;
   115 }
   117 function promiseNoPopupNotification(aName) {
   118   let deferred = Promise.defer();
   120   waitForCondition(() => !PopupNotifications.getNotification(aName),
   121                    () => {
   122     ok(!PopupNotifications.getNotification(aName),
   123        aName + " notification removed");
   124     deferred.resolve();
   125   }, "timeout waiting for popup notification " + aName + " to disappear");
   127   return deferred.promise;
   128 }
   130 const kActionAlways = 1;
   131 const kActionDeny = 2;
   132 const kActionNever = 3;
   134 function activateSecondaryAction(aAction) {
   135   let notification = PopupNotifications.panel.firstChild;
   136   notification.button.focus();
   137   let popup = notification.menupopup;
   138   popup.addEventListener("popupshown", function () {
   139     popup.removeEventListener("popupshown", arguments.callee, false);
   141     // Press 'down' as many time as needed to select the requested action.
   142     while (aAction--)
   143       EventUtils.synthesizeKey("VK_DOWN", {});
   145     // Activate
   146     EventUtils.synthesizeKey("VK_RETURN", {});
   147   }, false);
   149   // One down event to open the popup
   150   EventUtils.synthesizeKey("VK_DOWN",
   151                            { altKey: !navigator.platform.contains("Mac") });
   152 }
   154 registerCleanupFunction(function() {
   155   gBrowser.removeCurrentTab();
   156   kObservedTopics.forEach(topic => {
   157     Services.obs.removeObserver(observer, topic);
   158   });
   159   Services.prefs.clearUserPref(PREF_PERMISSION_FAKE);
   160 });
   162 function getMediaCaptureState() {
   163   let hasVideo = {};
   164   let hasAudio = {};
   165   MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio);
   166   if (hasVideo.value && hasAudio.value)
   167     return "CameraAndMicrophone";
   168   if (hasVideo.value)
   169     return "Camera";
   170   if (hasAudio.value)
   171     return "Microphone";
   172   return "none";
   173 }
   175 function closeStream(aAlreadyClosed) {
   176   expectNoObserverCalled();
   178   info("closing the stream");
   179   content.wrappedJSObject.closeStream();
   181   if (!aAlreadyClosed)
   182     yield promiseObserverCalled("recording-device-events");
   184   yield promiseNoPopupNotification("webRTC-sharingDevices");
   185   if (!aAlreadyClosed)
   186     expectObserverCalled("recording-window-ended");
   188   let statusButton = document.getElementById("webrtc-status-button");
   189   ok(statusButton.hidden, "WebRTC status button hidden");
   190 }
   192 function checkDeviceSelectors(aAudio, aVideo) {
   193   let micSelector = document.getElementById("webRTC-selectMicrophone");
   194   if (aAudio)
   195     ok(!micSelector.hidden, "microphone selector visible");
   196   else
   197     ok(micSelector.hidden, "microphone selector hidden");
   199   let cameraSelector = document.getElementById("webRTC-selectCamera");
   200   if (aVideo)
   201     ok(!cameraSelector.hidden, "camera selector visible");
   202   else
   203     ok(cameraSelector.hidden, "camera selector hidden");
   204 }
   206 function checkSharingUI() {
   207   yield promisePopupNotification("webRTC-sharingDevices");
   208   let statusButton = document.getElementById("webrtc-status-button");
   209   ok(!statusButton.hidden, "WebRTC status button visible");
   210 }
   212 function checkNotSharing() {
   213   is(getMediaCaptureState(), "none", "expected nothing to be shared");
   215   ok(!PopupNotifications.getNotification("webRTC-sharingDevices"),
   216      "no webRTC-sharingDevices popup notification");
   218   let statusButton = document.getElementById("webrtc-status-button");
   219   ok(statusButton.hidden, "WebRTC status button hidden");
   220 }
   222 let gTests = [
   224 {
   225   desc: "getUserMedia audio+video",
   226   run: function checkAudioVideo() {
   227     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   228       info("requesting devices");
   229       content.wrappedJSObject.requestDevice(true, true);
   230     });
   231     expectObserverCalled("getUserMedia:request");
   233     is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
   234        "webRTC-shareDevices-notification-icon", "anchored to device icon");
   235     checkDeviceSelectors(true, true);
   236     is(PopupNotifications.panel.firstChild.getAttribute("popupid"),
   237        "webRTC-shareDevices", "panel using devices icon");
   239     yield promiseMessage("ok", () => {
   240       PopupNotifications.panel.firstChild.button.click();
   241     });
   242     expectObserverCalled("getUserMedia:response:allow");
   243     expectObserverCalled("recording-device-events");
   244     is(getMediaCaptureState(), "CameraAndMicrophone",
   245        "expected camera and microphone to be shared");
   247     yield checkSharingUI();
   248     yield closeStream();
   249   }
   250 },
   252 {
   253   desc: "getUserMedia audio only",
   254   run: function checkAudioOnly() {
   255     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   256       info("requesting devices");
   257       content.wrappedJSObject.requestDevice(true);
   258     });
   259     expectObserverCalled("getUserMedia:request");
   261     is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
   262        "webRTC-shareMicrophone-notification-icon", "anchored to mic icon");
   263     checkDeviceSelectors(true);
   264     is(PopupNotifications.panel.firstChild.getAttribute("popupid"),
   265        "webRTC-shareMicrophone", "panel using microphone icon");
   267     yield promiseMessage("ok", () => {
   268       PopupNotifications.panel.firstChild.button.click();
   269     });
   270     expectObserverCalled("getUserMedia:response:allow");
   271     expectObserverCalled("recording-device-events");
   272     is(getMediaCaptureState(), "Microphone", "expected microphone to be shared");
   274     yield checkSharingUI();
   275     yield closeStream();
   276   }
   277 },
   279 {
   280   desc: "getUserMedia video only",
   281   run: function checkVideoOnly() {
   282     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   283       info("requesting devices");
   284       content.wrappedJSObject.requestDevice(false, true);
   285     });
   286     expectObserverCalled("getUserMedia:request");
   288     is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
   289        "webRTC-shareDevices-notification-icon", "anchored to device icon");
   290     checkDeviceSelectors(false, true);
   291     is(PopupNotifications.panel.firstChild.getAttribute("popupid"),
   292        "webRTC-shareDevices", "panel using devices icon");
   294     yield promiseMessage("ok", () => {
   295       PopupNotifications.panel.firstChild.button.click();
   296     });
   297     expectObserverCalled("getUserMedia:response:allow");
   298     expectObserverCalled("recording-device-events");
   299     is(getMediaCaptureState(), "Camera", "expected camera to be shared");
   301     yield checkSharingUI();
   302     yield closeStream();
   303   }
   304 },
   306 {
   307   desc: "getUserMedia audio+video, user disables video",
   308   run: function checkDisableVideo() {
   309     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   310       info("requesting devices");
   311       content.wrappedJSObject.requestDevice(true, true);
   312     });
   313     expectObserverCalled("getUserMedia:request");
   314     checkDeviceSelectors(true, true);
   316     // disable the camera
   317     document.getElementById("webRTC-selectCamera-menulist").value = -1;
   319     yield promiseMessage("ok", () => {
   320       PopupNotifications.panel.firstChild.button.click();
   321     });
   323     // reset the menuitem to have no impact on the following tests.
   324     document.getElementById("webRTC-selectCamera-menulist").value = 0;
   326     expectObserverCalled("getUserMedia:response:allow");
   327     expectObserverCalled("recording-device-events");
   328     is(getMediaCaptureState(), "Microphone",
   329        "expected microphone to be shared");
   331     yield checkSharingUI();
   332     yield closeStream();
   333   }
   334 },
   336 {
   337   desc: "getUserMedia audio+video, user disables audio",
   338   run: function checkDisableAudio() {
   339     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   340       info("requesting devices");
   341       content.wrappedJSObject.requestDevice(true, true);
   342     });
   343     expectObserverCalled("getUserMedia:request");
   344     checkDeviceSelectors(true, true);
   346     // disable the microphone
   347     document.getElementById("webRTC-selectMicrophone-menulist").value = -1;
   349     yield promiseMessage("ok", () => {
   350       PopupNotifications.panel.firstChild.button.click();
   351     });
   353     // reset the menuitem to have no impact on the following tests.
   354     document.getElementById("webRTC-selectMicrophone-menulist").value = 0;
   356     expectObserverCalled("getUserMedia:response:allow");
   357     expectObserverCalled("recording-device-events");
   358     is(getMediaCaptureState(), "Camera",
   359        "expected microphone to be shared");
   361     yield checkSharingUI();
   362     yield closeStream();
   363   }
   364 },
   366 {
   367   desc: "getUserMedia audio+video, user disables both audio and video",
   368   run: function checkDisableAudioVideo() {
   369     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   370       info("requesting devices");
   371       content.wrappedJSObject.requestDevice(true, true);
   372     });
   373     expectObserverCalled("getUserMedia:request");
   374     checkDeviceSelectors(true, true);
   376     // disable the camera and microphone
   377     document.getElementById("webRTC-selectCamera-menulist").value = -1;
   378     document.getElementById("webRTC-selectMicrophone-menulist").value = -1;
   380     yield promiseMessage("error: PERMISSION_DENIED", () => {
   381       PopupNotifications.panel.firstChild.button.click();
   382     });
   384     // reset the menuitems to have no impact on the following tests.
   385     document.getElementById("webRTC-selectCamera-menulist").value = 0;
   386     document.getElementById("webRTC-selectMicrophone-menulist").value = 0;
   388     expectObserverCalled("getUserMedia:response:deny");
   389     expectObserverCalled("recording-window-ended");
   390     checkNotSharing();
   391   }
   392 },
   394 {
   395   desc: "getUserMedia audio+video, user clicks \"Don't Share\"",
   396   run: function checkDontShare() {
   397     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   398       info("requesting devices");
   399       content.wrappedJSObject.requestDevice(true, true);
   400     });
   401     expectObserverCalled("getUserMedia:request");
   402     checkDeviceSelectors(true, true);
   404     yield promiseMessage("error: PERMISSION_DENIED", () => {
   405       activateSecondaryAction(kActionDeny);
   406     });
   408     expectObserverCalled("getUserMedia:response:deny");
   409     expectObserverCalled("recording-window-ended");
   410     checkNotSharing();
   411   }
   412 },
   414 {
   415   desc: "getUserMedia audio+video: stop sharing",
   416   run: function checkStopSharing() {
   417     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   418       info("requesting devices");
   419       content.wrappedJSObject.requestDevice(true, true);
   420     });
   421     expectObserverCalled("getUserMedia:request");
   422     checkDeviceSelectors(true, true);
   424     yield promiseMessage("ok", () => {
   425       PopupNotifications.panel.firstChild.button.click();
   426     });
   427     expectObserverCalled("getUserMedia:response:allow");
   428     expectObserverCalled("recording-device-events");
   429     is(getMediaCaptureState(), "CameraAndMicrophone",
   430        "expected camera and microphone to be shared");
   432     yield checkSharingUI();
   434     PopupNotifications.getNotification("webRTC-sharingDevices").reshow();
   435     activateSecondaryAction(kActionDeny);
   437     yield promiseObserverCalled("recording-device-events");
   438     expectObserverCalled("getUserMedia:revoke");
   440     yield promiseNoPopupNotification("webRTC-sharingDevices");
   442     if (gObservedTopics["recording-device-events"] == 1) {
   443       todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
   444       gObservedTopics["recording-device-events"] = 0;
   445     }
   447     expectNoObserverCalled();
   448     checkNotSharing();
   450     // the stream is already closed, but this will do some cleanup anyway
   451     yield closeStream(true);
   452   }
   453 },
   455 {
   456   desc: "getUserMedia prompt: Always/Never Share",
   457   run: function checkRememberCheckbox() {
   458     let elt = id => document.getElementById(id);
   460     function checkPerm(aRequestAudio, aRequestVideo, aAllowAudio, aAllowVideo,
   461                        aExpectedAudioPerm, aExpectedVideoPerm, aNever) {
   462       yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   463         content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
   464       });
   465       expectObserverCalled("getUserMedia:request");
   467       let noAudio = aAllowAudio === undefined;
   468       is(elt("webRTC-selectMicrophone").hidden, noAudio,
   469          "microphone selector expected to be " + (noAudio ? "hidden" : "visible"));
   470       if (!noAudio)
   471         elt("webRTC-selectMicrophone-menulist").value = (aAllowAudio || aNever) ? 0 : -1;
   473       let noVideo = aAllowVideo === undefined;
   474       is(elt("webRTC-selectCamera").hidden, noVideo,
   475          "camera selector expected to be " + (noVideo ? "hidden" : "visible"));
   476       if (!noVideo)
   477         elt("webRTC-selectCamera-menulist").value = (aAllowVideo || aNever) ? 0 : -1;
   479       let expectedMessage =
   480         (aAllowVideo || aAllowAudio) ? "ok" : "error: PERMISSION_DENIED";
   481       yield promiseMessage(expectedMessage, () => {
   482         activateSecondaryAction(aNever ? kActionNever : kActionAlways);
   483       });
   484       let expected = [];
   485       if (expectedMessage == "ok") {
   486         expectObserverCalled("getUserMedia:response:allow");
   487         expectObserverCalled("recording-device-events");
   488         if (aAllowVideo)
   489           expected.push("Camera");
   490         if (aAllowAudio)
   491           expected.push("Microphone");
   492         expected = expected.join("And");
   493       }
   494       else {
   495         expectObserverCalled("getUserMedia:response:deny");
   496         expectObserverCalled("recording-window-ended");
   497         expected = "none";
   498       }
   499       is(getMediaCaptureState(), expected,
   500          "expected " + expected + " to be shared");
   502       function checkDevicePermissions(aDevice, aExpected) {
   503         let Perms = Services.perms;
   504         let uri = content.document.documentURIObject;
   505         let devicePerms = Perms.testExactPermission(uri, aDevice);
   506         if (aExpected === undefined)
   507           is(devicePerms, Perms.UNKNOWN_ACTION, "no " + aDevice + " persistent permissions");
   508         else {
   509           is(devicePerms, aExpected ? Perms.ALLOW_ACTION : Perms.DENY_ACTION,
   510              aDevice + " persistently " + (aExpected ? "allowed" : "denied"));
   511         }
   512         Perms.remove(uri.host, aDevice);
   513       }
   514       checkDevicePermissions("microphone", aExpectedAudioPerm);
   515       checkDevicePermissions("camera", aExpectedVideoPerm);
   517       if (expectedMessage == "ok")
   518         yield closeStream();
   519     }
   521     // 3 cases where the user accepts the device prompt.
   522     info("audio+video, user grants, expect both perms set to allow");
   523     yield checkPerm(true, true, true, true, true, true);
   524     info("audio only, user grants, check audio perm set to allow, video perm not set");
   525     yield checkPerm(true, false, true, undefined, true, undefined);
   526     info("video only, user grants, check video perm set to allow, audio perm not set");
   527     yield checkPerm(false, true, undefined, true, undefined, true);
   529     // 3 cases where the user rejects the device request.
   530     // First test these cases by setting the device to 'No Audio'/'No Video'
   531     info("audio+video, user denies, expect both perms set to deny");
   532     yield checkPerm(true, true, false, false, false, false);
   533     info("audio only, user denies, expect audio perm set to deny, video not set");
   534     yield checkPerm(true, false, false, undefined, false, undefined);
   535     info("video only, user denies, expect video perm set to deny, audio perm not set");
   536     yield checkPerm(false, true, undefined, false, undefined, false);
   537     // Now test these 3 cases again by using the 'Never Share' action.
   538     info("audio+video, user denies, expect both perms set to deny");
   539     yield checkPerm(true, true, false, false, false, false, true);
   540     info("audio only, user denies, expect audio perm set to deny, video not set");
   541     yield checkPerm(true, false, false, undefined, false, undefined, true);
   542     info("video only, user denies, expect video perm set to deny, audio perm not set");
   543     yield checkPerm(false, true, undefined, false, undefined, false, true);
   545     // 2 cases where the user allows half of what's requested.
   546     info("audio+video, user denies video, grants audio, " +
   547          "expect video perm set to deny, audio perm set to allow.");
   548     yield checkPerm(true, true, true, false, true, false);
   549     info("audio+video, user denies audio, grants video, " +
   550          "expect video perm set to allow, audio perm set to deny.");
   551     yield checkPerm(true, true, false, true, false, true);
   553     // reset the menuitems to have no impact on the following tests.
   554     elt("webRTC-selectMicrophone-menulist").value = 0;
   555     elt("webRTC-selectCamera-menulist").value = 0;
   556   }
   557 },
   559 {
   560   desc: "getUserMedia without prompt: use persistent permissions",
   561   run: function checkUsePersistentPermissions() {
   562     function usePerm(aAllowAudio, aAllowVideo, aRequestAudio, aRequestVideo,
   563                      aExpectStream) {
   564       let Perms = Services.perms;
   565       let uri = content.document.documentURIObject;
   566       if (aAllowAudio !== undefined) {
   567         Perms.add(uri, "microphone", aAllowAudio ? Perms.ALLOW_ACTION
   568                                                  : Perms.DENY_ACTION);
   569       }
   570       if (aAllowVideo !== undefined) {
   571         Perms.add(uri, "camera", aAllowVideo ? Perms.ALLOW_ACTION
   572                                              : Perms.DENY_ACTION);
   573       }
   575       let gum = function() {
   576         content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
   577       };
   579       if (aExpectStream === undefined) {
   580         // Check that we get a prompt.
   581         yield promisePopupNotificationShown("webRTC-shareDevices", gum);
   582         expectObserverCalled("getUserMedia:request");
   584         // Deny the request to cleanup...
   585         yield promiseMessage("error: PERMISSION_DENIED", () => {
   586           activateSecondaryAction(kActionDeny);
   587         });
   588         expectObserverCalled("getUserMedia:response:deny");
   589         expectObserverCalled("recording-window-ended");
   590       }
   591       else {
   592         let expectedMessage = aExpectStream ? "ok" : "error: PERMISSION_DENIED";
   593         yield promiseMessage(expectedMessage, gum);
   595         if (expectedMessage == "ok") {
   596           expectObserverCalled("getUserMedia:request");
   597           yield promiseNoPopupNotification("webRTC-shareDevices");
   598           expectObserverCalled("getUserMedia:response:allow");
   599           expectObserverCalled("recording-device-events");
   601           // Check what's actually shared.
   602           let expected = [];
   603           if (aAllowVideo && aRequestVideo)
   604             expected.push("Camera");
   605           if (aAllowAudio && aRequestAudio)
   606             expected.push("Microphone");
   607           expected = expected.join("And");
   608           is(getMediaCaptureState(), expected,
   609              "expected " + expected + " to be shared");
   611           yield closeStream();
   612         }
   613         else {
   614           expectObserverCalled("recording-window-ended");
   615         }
   616       }
   618       Perms.remove(uri.host, "camera");
   619       Perms.remove(uri.host, "microphone");
   620     }
   622     // Set both permissions identically
   623     info("allow audio+video, request audio+video, expect ok (audio+video)");
   624     yield usePerm(true, true, true, true, true);
   625     info("deny audio+video, request audio+video, expect denied");
   626     yield usePerm(false, false, true, true, false);
   628     // Allow audio, deny video.
   629     info("allow audio, deny video, request audio+video, expect ok (audio)");
   630     yield usePerm(true, false, true, true, true);
   631     info("allow audio, deny video, request audio, expect ok (audio)");
   632     yield usePerm(true, false, true, false, true);
   633     info("allow audio, deny video, request video, expect denied");
   634     yield usePerm(true, false, false, true, false);
   636     // Deny audio, allow video.
   637     info("deny audio, allow video, request audio+video, expect ok (video)");
   638     yield usePerm(false, true, true, true, true);
   639     info("deny audio, allow video, request audio, expect denied");
   640     yield usePerm(false, true, true, false, false);
   641     info("deny audio, allow video, request video, expect ok (video)");
   642     yield usePerm(false, true, false, true, true);
   644     // Allow audio, video not set.
   645     info("allow audio, request audio+video, expect prompt");
   646     yield usePerm(true, undefined, true, true, undefined);
   647     info("allow audio, request audio, expect ok (audio)");
   648     yield usePerm(true, undefined, true, false, true);
   649     info("allow audio, request video, expect prompt");
   650     yield usePerm(true, undefined, false, true, undefined);
   652     // Deny audio, video not set.
   653     info("deny audio, request audio+video, expect prompt");
   654     yield usePerm(false, undefined, true, true, undefined);
   655     info("deny audio, request audio, expect denied");
   656     yield usePerm(false, undefined, true, false, false);
   657     info("deny audio, request video, expect prompt");
   658     yield usePerm(false, undefined, false, true, undefined);
   660     // Allow video, video not set.
   661     info("allow video, request audio+video, expect prompt");
   662     yield usePerm(undefined, true, true, true, undefined);
   663     info("allow video, request audio, expect prompt");
   664     yield usePerm(undefined, true, true, false, undefined);
   665     info("allow video, request video, expect ok (video)");
   666     yield usePerm(undefined, true, false, true, true);
   668     // Deny video, video not set.
   669     info("deny video, request audio+video, expect prompt");
   670     yield usePerm(undefined, false, true, true, undefined);
   671     info("deny video, request audio, expect prompt");
   672     yield usePerm(undefined, false, true, false, undefined);
   673     info("deny video, request video, expect denied");
   674     yield usePerm(undefined, false, false, true, false);
   675   }
   676 },
   678 {
   679   desc: "Stop Sharing removes persistent permissions",
   680   run: function checkStopSharingRemovesPersistentPermissions() {
   681     function stopAndCheckPerm(aRequestAudio, aRequestVideo) {
   682       let Perms = Services.perms;
   683       let uri = content.document.documentURIObject;
   685       // Initially set both permissions to 'allow'.
   686       Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
   687       Perms.add(uri, "camera", Perms.ALLOW_ACTION);
   689       // Start sharing what's been requested.
   690       yield promiseMessage("ok", () => {
   691         content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
   692       });
   693       expectObserverCalled("getUserMedia:request");
   694       expectObserverCalled("getUserMedia:response:allow");
   695       expectObserverCalled("recording-device-events");
   696       yield checkSharingUI();
   698       PopupNotifications.getNotification("webRTC-sharingDevices").reshow();
   699       let expectedIcon = "webRTC-sharingDevices";
   700       if (aRequestAudio && !aRequestVideo)
   701         expectedIcon = "webRTC-sharingMicrophone";
   702       is(PopupNotifications.getNotification("webRTC-sharingDevices").anchorID,
   703          expectedIcon + "-notification-icon", "anchored to correct icon");
   704       is(PopupNotifications.panel.firstChild.getAttribute("popupid"), expectedIcon,
   705          "panel using correct icon");
   707       // Stop sharing.
   708       activateSecondaryAction(kActionDeny);
   710       yield promiseObserverCalled("recording-device-events");
   711       expectObserverCalled("getUserMedia:revoke");
   713       yield promiseNoPopupNotification("webRTC-sharingDevices");
   715       if (gObservedTopics["recording-device-events"] == 1) {
   716         todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
   717         gObservedTopics["recording-device-events"] = 0;
   718       }
   720       // Check that permissions have been removed as expected.
   721       let audioPerm = Perms.testExactPermission(uri, "microphone");
   722       if (aRequestAudio)
   723         is(audioPerm, Perms.UNKNOWN_ACTION, "microphone permissions removed");
   724       else
   725         is(audioPerm, Perms.ALLOW_ACTION, "microphone permissions untouched");
   727       let videoPerm = Perms.testExactPermission(uri, "camera");
   728       if (aRequestVideo)
   729         is(videoPerm, Perms.UNKNOWN_ACTION, "camera permissions removed");
   730       else
   731         is(videoPerm, Perms.ALLOW_ACTION, "camera permissions untouched");
   733       // Cleanup.
   734       yield closeStream(true);
   736       Perms.remove(uri.host, "camera");
   737       Perms.remove(uri.host, "microphone");
   738     }
   740     info("request audio+video, stop sharing resets both");
   741     yield stopAndCheckPerm(true, true);
   742     info("request audio, stop sharing resets audio only");
   743     yield stopAndCheckPerm(true, false);
   744     info("request video, stop sharing resets video only");
   745     yield stopAndCheckPerm(false, true);
   746   }
   747 },
   749 {
   750   desc: "'Always Allow' ignored and not shown on http pages",
   751   run: function checkNoAlwaysOnHttp() {
   752     // Load an http page instead of the https version.
   753     let deferred = Promise.defer();
   754     let browser = gBrowser.selectedTab.linkedBrowser;
   755     browser.addEventListener("load", function onload() {
   756       browser.removeEventListener("load", onload, true);
   757       deferred.resolve();
   758     }, true);
   759     content.location = content.location.href.replace("https://", "http://");
   760     yield deferred.promise;
   762     // Initially set both permissions to 'allow'.
   763     let Perms = Services.perms;
   764     let uri = content.document.documentURIObject;
   765     Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
   766     Perms.add(uri, "camera", Perms.ALLOW_ACTION);
   768     // Request devices and expect a prompt despite the saved 'Allow' permission,
   769     // because the connection isn't secure.
   770     yield promisePopupNotificationShown("webRTC-shareDevices", () => {
   771       content.wrappedJSObject.requestDevice(true, true);
   772     });
   773     expectObserverCalled("getUserMedia:request");
   775     // Ensure that the 'Always Allow' action isn't shown.
   776     let alwaysLabel = gNavigatorBundle.getString("getUserMedia.always.label");
   777     ok(!!alwaysLabel, "found the 'Always Allow' localized label");
   778     let labels = [];
   779     let notification = PopupNotifications.panel.firstChild;
   780     for (let node of notification.childNodes) {
   781       if (node.localName == "menuitem")
   782         labels.push(node.getAttribute("label"));
   783     }
   784     is(labels.indexOf(alwaysLabel), -1, "The 'Always Allow' item isn't shown");
   786     // Cleanup.
   787     yield closeStream(true);
   788     Perms.remove(uri.host, "camera");
   789     Perms.remove(uri.host, "microphone");
   790   }
   791 }
   793 ];
   795 function test() {
   796   waitForExplicitFinish();
   798   let tab = gBrowser.addTab();
   799   gBrowser.selectedTab = tab;
   800   tab.linkedBrowser.addEventListener("load", function onload() {
   801     tab.linkedBrowser.removeEventListener("load", onload, true);
   803     kObservedTopics.forEach(topic => {
   804       Services.obs.addObserver(observer, topic, false);
   805     });
   806     Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true);
   808     is(PopupNotifications._currentNotifications.length, 0,
   809        "should start the test without any prior popup notification");
   811     Task.spawn(function () {
   812       for (let test of gTests) {
   813         info(test.desc);
   814         yield test.run();
   816         // Cleanup before the next test
   817         expectNoObserverCalled();
   818       }
   819     }).then(finish, ex => {
   820      ok(false, "Unexpected Exception: " + ex);
   821      finish();
   822     });
   823   }, true);
   824   let rootDir = getRootDirectory(gTestPath)
   825   rootDir = rootDir.replace("chrome://mochitests/content/",
   826                             "https://example.com/");
   827   content.location = rootDir + "get_user_media.html";
   828 }
   831 function wait(time) {
   832   let deferred = Promise.defer();
   833   setTimeout(deferred.resolve, time);
   834   return deferred.promise;
   835 }

mercurial