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.

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

mercurial