Bug 1456899 - Consider the page URI when checking WebRTC permissions in webrtcUI.jsm. r=nhnt11

While we already do a permission check in native webrtc code, the one
in webrtcUI is needed to provide extra protection against permission spam
by checking if there's a temporarily denied permission on the tab.

Since we introduced custom default permission values it is necessary to
also pass the URI to make sure we catch exceptions added by the user.

MozReview-Commit-ID: C8r6ymbKE3a

--HG--
extra : rebase_source : 8f0b3486f2f33bee75536ae2cf7dd091c5a9c568
This commit is contained in:
Johann Hofmann 2018-05-08 15:31:49 +02:00
Родитель c8e987256f
Коммит ed9da495e6
4 изменённых файлов: 176 добавлений и 11 удалений

Просмотреть файл

@ -8,6 +8,7 @@ support-files =
[browser_devices_get_user_media.js]
skip-if = (os == "linux" && debug) # linux: bug 976544
[browser_devices_get_user_media_anim.js]
[browser_devices_get_user_media_default_permissions.js]
[browser_devices_get_user_media_in_frame.js]
skip-if = debug # bug 1369731
[browser_devices_get_user_media_multi_process.js]

Просмотреть файл

@ -0,0 +1,164 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
const CAMERA_PREF = "permissions.default.camera";
const MICROPHONE_PREF = "permissions.default.microphone";
var gTests = [
{
desc: "getUserMedia audio+video: globally blocking camera",
run: async function checkAudioVideo() {
Services.prefs.setIntPref(CAMERA_PREF, SitePermissions.BLOCK);
// Requesting audio+video shouldn't work.
let promise = promiseMessage(permissionError);
await promiseRequestDevice(true, true);
await promise;
await expectObserverCalled("recording-window-ended");
await checkNotSharing();
// Requesting only video shouldn't work.
promise = promiseMessage(permissionError);
await promiseRequestDevice(false, true);
await promise;
await expectObserverCalled("recording-window-ended");
await checkNotSharing();
// Requesting audio should work.
promise = promisePopupNotificationShown("webRTC-shareDevices");
await promiseRequestDevice(true);
await promise;
await expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareMicrophone-notification-icon", "anchored to mic icon");
checkDeviceSelectors(true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("microphone-icon"), "panel using microphone icon");
let indicator = promiseIndicatorWindow();
await promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
Assert.deepEqual((await getMediaCaptureState()), {audio: true},
"expected microphone to be shared");
await indicator;
await checkSharingUI({audio: true});
await closeStream();
Services.prefs.clearUserPref(CAMERA_PREF);
}
},
{
desc: "getUserMedia video: globally blocking camera + local exception",
run: async function checkAudioVideo() {
let browser = gBrowser.selectedBrowser;
Services.prefs.setIntPref(CAMERA_PREF, SitePermissions.BLOCK);
// Overwrite the permission for that URI, requesting video should work again.
SitePermissions.set(browser.currentURI, "camera", SitePermissions.ALLOW);
// Requesting video should work.
let indicator = promiseIndicatorWindow();
let promise = promiseMessage("ok");
await promiseRequestDevice(false, true);
await promise;
await expectObserverCalled("getUserMedia:request");
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
await indicator;
await checkSharingUI({video: true});
await closeStream();
SitePermissions.remove(browser.currentURI, "camera");
Services.prefs.clearUserPref(CAMERA_PREF);
}
},
{
desc: "getUserMedia audio+video: globally blocking microphone",
run: async function checkAudioVideo() {
Services.prefs.setIntPref(MICROPHONE_PREF, SitePermissions.BLOCK);
// Requesting audio+video shouldn't work.
let promise = promiseMessage(permissionError);
await promiseRequestDevice(true, true);
await promise;
await expectObserverCalled("recording-window-ended");
await checkNotSharing();
// Requesting only audio shouldn't work.
promise = promiseMessage(permissionError);
await promiseRequestDevice(true);
await promise;
await expectObserverCalled("recording-window-ended");
await checkNotSharing();
// Requesting video should work.
promise = promisePopupNotificationShown("webRTC-shareDevices");
await promiseRequestDevice(false, true);
await promise;
await expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareDevices-notification-icon", "anchored to device icon");
checkDeviceSelectors(false, true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("camera-icon"), "panel using devices icon");
let indicator = promiseIndicatorWindow();
await promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
Assert.deepEqual((await getMediaCaptureState()), {video: true},
"expected camera to be shared");
await indicator;
await checkSharingUI({video: true});
await closeStream();
Services.prefs.clearUserPref(MICROPHONE_PREF);
}
},
{
desc: "getUserMedia audio: globally blocking microphone + local exception",
run: async function checkAudioVideo() {
let browser = gBrowser.selectedBrowser;
Services.prefs.setIntPref(MICROPHONE_PREF, SitePermissions.BLOCK);
// Overwrite the permission for that URI, requesting video should work again.
SitePermissions.set(browser.currentURI, "microphone", SitePermissions.ALLOW);
// Requesting audio should work.
let indicator = promiseIndicatorWindow();
let promise = promiseMessage("ok");
await promiseRequestDevice(true);
await promise;
await expectObserverCalled("getUserMedia:request");
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
await indicator;
await checkSharingUI({audio: true});
await closeStream();
SitePermissions.remove(browser.currentURI, "microphone");
Services.prefs.clearUserPref(MICROPHONE_PREF);
}
},
];
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -114,7 +114,7 @@ async function assertWebRTCIndicatorStatus(expected) {
while (windows.hasMoreElements()) {
let win = windows.getNext();
let menu = win.document.getElementById("tabSharingMenu");
is(menu && !menu.hidden, !!expected, "WebRTC menu should be " + expectedState);
is(!!menu && !menu.hidden, !!expected, "WebRTC menu should be " + expectedState);
}
if (!("nsISystemStatusBar" in Ci)) {

Просмотреть файл

@ -341,12 +341,20 @@ function prompt(aBrowser, aRequest) {
let { audioDevices, videoDevices, sharingScreen, sharingAudio,
requestTypes } = aRequest;
let uri;
try {
// This fails for principals that serialize to "null", e.g. file URIs.
uri = Services.io.newURI(aRequest.origin);
} catch (e) {
uri = Services.io.newURI(aRequest.documentURI);
}
// If the user has already denied access once in this tab,
// deny again without even showing the notification icon.
if ((audioDevices.length && SitePermissions
.get(null, "microphone", aBrowser).state == SitePermissions.BLOCK) ||
.get(uri, "microphone", aBrowser).state == SitePermissions.BLOCK) ||
(videoDevices.length && SitePermissions
.get(null, sharingScreen ? "screen" : "camera", aBrowser).state == SitePermissions.BLOCK)) {
.get(uri, sharingScreen ? "screen" : "camera", aBrowser).state == SitePermissions.BLOCK)) {
denyRequest(aBrowser, aRequest);
return;
}
@ -356,14 +364,6 @@ function prompt(aBrowser, aRequest) {
aBrowser.dispatchEvent(new aBrowser.ownerGlobal
.CustomEvent("PermissionStateChange"));
let uri;
try {
// This fails for principals that serialize to "null", e.g. file URIs.
uri = Services.io.newURI(aRequest.origin);
} catch (e) {
uri = Services.io.newURI(aRequest.documentURI);
}
let chromeDoc = aBrowser.ownerDocument;
let stringBundle = chromeDoc.defaultView.gNavigatorBundle;