Wed, 31 Dec 2014 06:09:35 +0100
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 }