mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1483631
- Restrict nested permission requests in webrtc with permission delegate r=jib
Differential Revision: https://phabricator.services.mozilla.com/D47417 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
fd744cea01
commit
a0b817ac65
@ -284,14 +284,40 @@ function prompt(
|
||||
// So, what you are looking at here is not a real nsIContentPermissionRequest, but
|
||||
// something that looks really similar and will be transmitted to webrtcUI.jsm
|
||||
// for showing the prompt.
|
||||
// Note that we basically do the permission delegate check in
|
||||
// nsIContentPermissionRequest, but because webrtc uses their own prompting
|
||||
// system, we should manually apply the delegate policy here. Permission
|
||||
// should be delegated using Feature Policy and top principal
|
||||
const shouldDelegatePermission =
|
||||
Services.prefs.getBoolPref("permissions.delegation.enabled", false) &&
|
||||
Services.prefs.getBoolPref("dom.security.featurePolicy.enabled", false);
|
||||
|
||||
const origin = shouldDelegatePermission
|
||||
? aContentWindow.top.document.nodePrincipal.origin
|
||||
: aContentWindow.document.nodePrincipal.origin;
|
||||
|
||||
let secondOrigin = undefined;
|
||||
if (shouldDelegatePermission) {
|
||||
const permDelegateHandler = aContentWindow.document.permDelegateHandler.QueryInterface(
|
||||
Ci.nsIPermissionDelegateHandler
|
||||
);
|
||||
if (permDelegateHandler.maybeUnsafePermissionDelegate(requestTypes)) {
|
||||
// We are going to prompt both first party and third party origin.
|
||||
// SecondOrigin should be third party
|
||||
secondOrigin = aContentWindow.document.nodePrincipal.origin;
|
||||
}
|
||||
}
|
||||
|
||||
let request = {
|
||||
callID: aCallID,
|
||||
windowID: aWindowID,
|
||||
origin: aContentWindow.document.nodePrincipal.origin,
|
||||
origin,
|
||||
secondOrigin,
|
||||
documentURI: aContentWindow.document.documentURI,
|
||||
secure: aSecure,
|
||||
isHandlingUserInput: aIsHandlingUserInput,
|
||||
isThirdPartyOrigin,
|
||||
shouldDelegatePermission,
|
||||
requestTypes,
|
||||
sharingScreen,
|
||||
sharingAudio,
|
||||
|
@ -427,9 +427,9 @@ pref("permissions.postPrompt.animate", true);
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("permissions.delegation.enable", true);
|
||||
pref("permissions.delegation.enabled", true);
|
||||
#else
|
||||
pref("permissions.delegation.enable", false);
|
||||
pref("permissions.delegation.enabled", false);
|
||||
#endif
|
||||
|
||||
// handle links targeting new windows
|
||||
|
@ -14,7 +14,7 @@ add_task(async function testNoPermissionPrompt() {
|
||||
{
|
||||
set: [
|
||||
["dom.security.featurePolicy.enabled", true],
|
||||
["permissions.delegation.enable", true],
|
||||
["permissions.delegation.enabled", true],
|
||||
["dom.vibrator.enabled", true],
|
||||
["dom.security.featurePolicy.header.enabled", true],
|
||||
["dom.security.featurePolicy.webidl.enabled", true],
|
||||
|
@ -3,6 +3,7 @@ support-files =
|
||||
get_user_media.html
|
||||
get_user_media_in_frame.html
|
||||
get_user_media_in_xorigin_frame.html
|
||||
get_user_media_in_xorigin_frame_ancestor.html
|
||||
head.js
|
||||
|
||||
[browser_devices_get_user_media.js]
|
||||
@ -13,6 +14,7 @@ skip-if = (os == "linux" && debug) # linux: bug 976544
|
||||
skip-if = debug # bug 1369731
|
||||
[browser_devices_get_user_media_in_xorigin_frame.js]
|
||||
skip-if = debug # bug 1369731
|
||||
[browser_devices_get_user_media_in_xorigin_frame_chain.js]
|
||||
[browser_devices_get_user_media_multi_process.js]
|
||||
skip-if = (debug && os == "win") # bug 1393761
|
||||
[browser_devices_get_user_media_paused.js]
|
||||
|
@ -1,41 +1,175 @@
|
||||
/* 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 PromptResult = {
|
||||
ALLOW: "allow",
|
||||
DENY: "deny",
|
||||
PROMPT: "prompt",
|
||||
};
|
||||
|
||||
const Perms = Services.perms;
|
||||
|
||||
async function promptNoDelegate(aThirdPartyOrgin) {
|
||||
// Persistent allowed first party origin
|
||||
const uri = gBrowser.selectedBrowser.documentURI;
|
||||
PermissionTestUtils.add(uri, "microphone", Services.perms.ALLOW_ACTION);
|
||||
PermissionTestUtils.add(uri, "camera", Services.perms.ALLOW_ACTION);
|
||||
|
||||
// Check that we get a prompt.
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
await promiseRequestDevice(true, true, "frame4");
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
// The 'Remember this decision' checkbox is hidden.
|
||||
const notification = PopupNotifications.panel.firstElementChild;
|
||||
const checkbox = notification.checkbox;
|
||||
ok(!!checkbox, "checkbox is present");
|
||||
ok(checkbox.hidden, "checkbox is not visible");
|
||||
ok(!checkbox.checked, "checkbox not checked");
|
||||
|
||||
// Check the label of the notification should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
// Check the secondName of the notification should be the third party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options
|
||||
.secondName,
|
||||
aThirdPartyOrgin,
|
||||
"Use third party's origin as secondName"
|
||||
);
|
||||
|
||||
let indicator = promiseIndicatorWindow();
|
||||
let observerPromise1 = expectObserverCalled("getUserMedia:response:allow");
|
||||
let observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
await promiseMessage("ok", () =>
|
||||
EventUtils.synthesizeMouseAtCenter(notification.button, {})
|
||||
);
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
Assert.deepEqual(
|
||||
await getMediaCaptureState(),
|
||||
{ audio: true, video: true },
|
||||
"expected camera and microphone to be shared"
|
||||
);
|
||||
await indicator;
|
||||
await checkSharingUI({ audio: true, video: true });
|
||||
|
||||
// Cleanup.
|
||||
await closeStream(false, "frame4");
|
||||
|
||||
PermissionTestUtils.remove(uri, "camera");
|
||||
PermissionTestUtils.remove(uri, "microphone");
|
||||
}
|
||||
|
||||
async function promptNoDelegateScreenSharing(aThirdPartyOrgin) {
|
||||
// Persistent allow screen sharing
|
||||
const uri = gBrowser.selectedBrowser.documentURI;
|
||||
PermissionTestUtils.add(uri, "screen", Services.perms.ALLOW_ACTION);
|
||||
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
await promiseRequestDevice(false, true, "frame4", "screen");
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
checkDeviceSelectors(false, false, true);
|
||||
const notification = PopupNotifications.panel.firstElementChild;
|
||||
const iconclass = notification.getAttribute("iconclass");
|
||||
ok(iconclass.includes("screen-icon"), "panel using screen icon");
|
||||
|
||||
// The 'Remember this decision' checkbox is hidden.
|
||||
const checkbox = notification.checkbox;
|
||||
ok(!!checkbox, "checkbox is present");
|
||||
ok(checkbox.hidden, "checkbox is not visible");
|
||||
ok(!checkbox.checked, "checkbox not checked");
|
||||
|
||||
// Check the label of the notification should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
// Check the secondName of the notification should be the third party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options
|
||||
.secondName,
|
||||
aThirdPartyOrgin,
|
||||
"Use third party's origin as secondName"
|
||||
);
|
||||
|
||||
const menulist = document.getElementById("webRTC-selectWindow-menulist");
|
||||
const count = menulist.itemCount;
|
||||
menulist.getItemAtIndex(count - 1).doCommand();
|
||||
ok(!notification.button.disabled, "Allow button is enabled");
|
||||
|
||||
const indicator = promiseIndicatorWindow();
|
||||
const observerPromise1 = expectObserverCalled("getUserMedia:response:allow");
|
||||
const observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
await promiseMessage("ok", () =>
|
||||
EventUtils.synthesizeMouseAtCenter(notification.button, {})
|
||||
);
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
Assert.deepEqual(
|
||||
await getMediaCaptureState(),
|
||||
{ screen: "Screen" },
|
||||
"expected screen to be shared"
|
||||
);
|
||||
|
||||
await indicator;
|
||||
await checkSharingUI({ screen: "Screen" });
|
||||
await closeStream(false, "frame4");
|
||||
|
||||
PermissionTestUtils.remove(uri, "screen");
|
||||
}
|
||||
|
||||
var gTests = [
|
||||
{
|
||||
desc: "'Always Allow' disabled on third party pages",
|
||||
desc:
|
||||
"'Always Allow' enabled on third party pages, when origin is explicitly allowed",
|
||||
run: async function checkNoAlwaysOnThirdParty() {
|
||||
// Initially set both permissions to 'allow'.
|
||||
let origin = "https://test1.example.com/";
|
||||
PermissionTestUtils.add(
|
||||
origin,
|
||||
"microphone",
|
||||
Services.perms.ALLOW_ACTION
|
||||
);
|
||||
PermissionTestUtils.add(origin, "camera", Services.perms.ALLOW_ACTION);
|
||||
// Initially set both permissions to 'prompt'.
|
||||
const uri = gBrowser.selectedBrowser.documentURI;
|
||||
PermissionTestUtils.add(uri, "microphone", Services.perms.PROMPT_ACTION);
|
||||
PermissionTestUtils.add(uri, "camera", Services.perms.PROMPT_ACTION);
|
||||
|
||||
// Request devices and expect a prompt despite the saved 'Allow' permission,
|
||||
// because we're a third party.
|
||||
let observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
let promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
await promiseRequestDevice(true, true, "frame1");
|
||||
await promise;
|
||||
await observerPromise;
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
// Ensure that the 'Remember this decision' checkbox is absent.
|
||||
let notification = PopupNotifications.panel.firstElementChild;
|
||||
let checkbox = notification.checkbox;
|
||||
// The 'Remember this decision' checkbox is visible.
|
||||
const notification = PopupNotifications.panel.firstElementChild;
|
||||
const checkbox = notification.checkbox;
|
||||
ok(!!checkbox, "checkbox is present");
|
||||
ok(checkbox.hidden, "checkbox is not visible");
|
||||
ok(!checkbox.hidden, "checkbox is visible");
|
||||
ok(!checkbox.checked, "checkbox not checked");
|
||||
|
||||
let indicator = promiseIndicatorWindow();
|
||||
let observerPromise1 = expectObserverCalled(
|
||||
// Check the label of the notification should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
const indicator = promiseIndicatorWindow();
|
||||
const observerPromise1 = expectObserverCalled(
|
||||
"getUserMedia:response:allow"
|
||||
);
|
||||
let observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
const observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
await promiseMessage("ok", () =>
|
||||
EventUtils.synthesizeMouseAtCenter(notification.button, {})
|
||||
);
|
||||
@ -51,38 +185,39 @@ var gTests = [
|
||||
|
||||
// Cleanup.
|
||||
await closeStream(false, "frame1");
|
||||
PermissionTestUtils.remove(origin, "camera");
|
||||
PermissionTestUtils.remove(origin, "microphone");
|
||||
PermissionTestUtils.remove(uri, "camera");
|
||||
PermissionTestUtils.remove(uri, "microphone");
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "'Always Allow' disabled when sharing screen in third party iframes",
|
||||
desc:
|
||||
"'Always Allow' disabled when sharing screen in third party iframes, when origin is explicitly allowed",
|
||||
run: async function checkScreenSharing() {
|
||||
let observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
let promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
await promiseRequestDevice(false, true, "frame1", "screen");
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
checkDeviceSelectors(false, false, true);
|
||||
let notification = PopupNotifications.panel.firstElementChild;
|
||||
let iconclass = notification.getAttribute("iconclass");
|
||||
const notification = PopupNotifications.panel.firstElementChild;
|
||||
const iconclass = notification.getAttribute("iconclass");
|
||||
ok(iconclass.includes("screen-icon"), "panel using screen icon");
|
||||
|
||||
// Ensure that the 'Remember this decision' checkbox is absent.
|
||||
let checkbox = notification.checkbox;
|
||||
// The 'Remember this decision' checkbox is visible.
|
||||
const checkbox = notification.checkbox;
|
||||
ok(!!checkbox, "checkbox is present");
|
||||
ok(checkbox.hidden, "checkbox is not visible");
|
||||
ok(!checkbox.hidden, "checkbox is visible");
|
||||
ok(!checkbox.checked, "checkbox not checked");
|
||||
|
||||
let menulist = document.getElementById("webRTC-selectWindow-menulist");
|
||||
let count = menulist.itemCount;
|
||||
const menulist = document.getElementById("webRTC-selectWindow-menulist");
|
||||
const count = menulist.itemCount;
|
||||
ok(
|
||||
count >= 4,
|
||||
"There should be the 'Select Window or Screen' item, a separator and at least one window and one screen"
|
||||
);
|
||||
|
||||
let noWindowOrScreenItem = menulist.getItemAtIndex(0);
|
||||
const noWindowOrScreenItem = menulist.getItemAtIndex(0);
|
||||
ok(
|
||||
noWindowOrScreenItem.hasAttribute("selected"),
|
||||
"the 'Select Window or Screen' item is selected"
|
||||
@ -106,11 +241,11 @@ var gTests = [
|
||||
menulist.getItemAtIndex(count - 1).doCommand();
|
||||
ok(!notification.button.disabled, "Allow button is enabled");
|
||||
|
||||
let indicator = promiseIndicatorWindow();
|
||||
let observerPromise1 = expectObserverCalled(
|
||||
const indicator = promiseIndicatorWindow();
|
||||
const observerPromise1 = expectObserverCalled(
|
||||
"getUserMedia:response:allow"
|
||||
);
|
||||
let observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
const observerPromise2 = expectObserverCalled("recording-device-events");
|
||||
await promiseMessage("ok", () =>
|
||||
EventUtils.synthesizeMouseAtCenter(notification.button, {})
|
||||
);
|
||||
@ -127,9 +262,325 @@ var gTests = [
|
||||
await closeStream(false, "frame1");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia use persistent permissions from first party",
|
||||
run: async function checkUsePersistentPermissionsFirstParty() {
|
||||
async function checkPersistentPermission(
|
||||
aPermission,
|
||||
aRequestType,
|
||||
aIframeId,
|
||||
aExpect
|
||||
) {
|
||||
info(
|
||||
`Test persistent permission ${aPermission} type ${aRequestType} expect ${aExpect}`
|
||||
);
|
||||
const uri = gBrowser.selectedBrowser.documentURI;
|
||||
// Persistent allow/deny for first party uri
|
||||
PermissionTestUtils.add(uri, aRequestType, aPermission);
|
||||
|
||||
let audio = aRequestType == "microphone";
|
||||
let video = aRequestType == "camera";
|
||||
const screen = aRequestType == "screen" ? "screen" : undefined;
|
||||
if (screen) {
|
||||
audio = false;
|
||||
video = true;
|
||||
}
|
||||
if (aExpect == PromptResult.PROMPT) {
|
||||
// Check that we get a prompt.
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const observerPromise1 = expectObserverCalled(
|
||||
"getUserMedia:response:deny"
|
||||
);
|
||||
const observerPromise2 = expectObserverCalled(
|
||||
"recording-window-ended"
|
||||
);
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
await promiseRequestDevice(audio, video, aIframeId, screen);
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
// Check the label of the notification should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options
|
||||
.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
// Deny the request to cleanup...
|
||||
await promiseMessage(permissionError, () => {
|
||||
activateSecondaryAction(kActionDeny);
|
||||
});
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
SitePermissions.removeFromPrincipal(null, aRequestType, browser);
|
||||
} else if (aExpect == PromptResult.ALLOW) {
|
||||
const observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
const observerPromise1 = expectObserverCalled(
|
||||
"getUserMedia:response:allow"
|
||||
);
|
||||
const observerPromise2 = expectObserverCalled(
|
||||
"recording-device-events"
|
||||
);
|
||||
const promise = promiseMessage("ok");
|
||||
await promiseRequestDevice(audio, video, aIframeId, screen);
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
await promiseNoPopupNotification("webRTC-shareDevices");
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
|
||||
let expected = {};
|
||||
if (audio) {
|
||||
expected.audio = audio;
|
||||
}
|
||||
if (video) {
|
||||
expected.video = video;
|
||||
}
|
||||
|
||||
Assert.deepEqual(
|
||||
await getMediaCaptureState(),
|
||||
expected,
|
||||
"expected " + Object.keys(expected).join(" and ") + " to be shared"
|
||||
);
|
||||
|
||||
await closeStream(false, "frame1");
|
||||
} else if (aExpect == PromptResult.DENY) {
|
||||
const observerPromise = expectObserverCalled(
|
||||
"recording-window-ended"
|
||||
);
|
||||
const promise = promiseMessage(permissionError);
|
||||
await promiseRequestDevice(audio, video, aIframeId, screen);
|
||||
await promise;
|
||||
await observerPromise;
|
||||
}
|
||||
|
||||
PermissionTestUtils.remove(uri, aRequestType);
|
||||
}
|
||||
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"camera",
|
||||
"frame1",
|
||||
PromptResult.PROMPT
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"camera",
|
||||
"frame1",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"camera",
|
||||
"frame1",
|
||||
PromptResult.ALLOW
|
||||
);
|
||||
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"microphone",
|
||||
"frame1",
|
||||
PromptResult.PROMPT
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"microphone",
|
||||
"frame1",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"microphone",
|
||||
"frame1",
|
||||
PromptResult.ALLOW
|
||||
);
|
||||
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"screen",
|
||||
"frame1",
|
||||
PromptResult.PROMPT
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"screen",
|
||||
"frame1",
|
||||
PromptResult.DENY
|
||||
);
|
||||
// Always prompt screen sharing
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"screen",
|
||||
"frame1",
|
||||
PromptResult.PROMPT
|
||||
);
|
||||
|
||||
// Denied by default if allow is not defined
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"camera",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"camera",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"camera",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"microphone",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"microphone",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"microphone",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
|
||||
await checkPersistentPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"screen",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.DENY_ACTION,
|
||||
"screen",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
await checkPersistentPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"screen",
|
||||
"frame3",
|
||||
PromptResult.DENY
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia use temporary blocked permissions from first party",
|
||||
run: async function checkUseTempPermissionsBlockFirstParty() {
|
||||
async function checkTempPermission(aRequestType) {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
let observerPromise1 = expectObserverCalled(
|
||||
"getUserMedia:response:deny"
|
||||
);
|
||||
let observerPromise2 = expectObserverCalled("recording-window-ended");
|
||||
let promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
let audio = aRequestType == "microphone";
|
||||
let video = aRequestType == "camera";
|
||||
const screen = aRequestType == "screen" ? "screen" : undefined;
|
||||
if (screen) {
|
||||
audio = false;
|
||||
video = true;
|
||||
}
|
||||
|
||||
await promiseRequestDevice(audio, video, null, screen);
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
// Temporarily grant/deny from top level
|
||||
// Only need to check allow and deny temporary permissions
|
||||
await promiseMessage(permissionError, () => {
|
||||
activateSecondaryAction(kActionDeny);
|
||||
});
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
await checkNotSharing();
|
||||
|
||||
observerPromise = expectObserverCalled("getUserMedia:request");
|
||||
observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
|
||||
observerPromise2 = expectObserverCalled("recording-window-ended");
|
||||
promise = promiseMessage(permissionError);
|
||||
await promiseRequestDevice(audio, video, "frame1", screen);
|
||||
await promise;
|
||||
|
||||
await observerPromise;
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
|
||||
SitePermissions.removeFromPrincipal(null, aRequestType, browser);
|
||||
}
|
||||
|
||||
// At the moment we only save temporary deny
|
||||
await checkTempPermission("camera");
|
||||
await checkTempPermission("microphone");
|
||||
await checkTempPermission("screen");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
desc:
|
||||
"Prompt and display both first party and third party origin in maybe unsafe permission delegation",
|
||||
run: async function checkPromptNoDelegate() {
|
||||
await promptNoDelegate("test1.example.com");
|
||||
},
|
||||
},
|
||||
{
|
||||
desc:
|
||||
"Prompt and display both first party and third party origin when sharing screen in unsafe permission delegation",
|
||||
run: async function checkPromptNoDelegateScreenSharing() {
|
||||
await promptNoDelegateScreenSharing("test1.example.com");
|
||||
},
|
||||
},
|
||||
{
|
||||
desc:
|
||||
"Change location, prompt and display both first party and third party origin in maybe unsafe permission delegation",
|
||||
run: async function checkPromptNoDelegateChangeLoxation() {
|
||||
await promiseChangeLocationFrame(
|
||||
"frame4",
|
||||
"https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"
|
||||
);
|
||||
await promptNoDelegate("test2.example.com");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
desc:
|
||||
"Change location, prompt and display both first party and third party origin when sharing screen in unsafe permission delegation",
|
||||
run: async function checkPromptNoDelegateScreenSharingChangeLocation() {
|
||||
await promiseChangeLocationFrame(
|
||||
"frame4",
|
||||
"https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"
|
||||
);
|
||||
await promptNoDelegateScreenSharing("test2.example.com");
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
add_task(async function test() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["permissions.delegation.enabled", true],
|
||||
["dom.security.featurePolicy.enabled", true],
|
||||
["dom.security.featurePolicy.header.enabled", true],
|
||||
["dom.security.featurePolicy.webidl.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
await runTests(gTests, {
|
||||
relativeURI: "get_user_media_in_xorigin_frame.html",
|
||||
});
|
||||
|
@ -0,0 +1,253 @@
|
||||
/* 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 PromptResult = {
|
||||
ALLOW: "allow",
|
||||
DENY: "deny",
|
||||
PROMPT: "prompt",
|
||||
};
|
||||
|
||||
const Perms = Services.perms;
|
||||
|
||||
function expectObserverCalledAncestor(aTopic, browsingContext) {
|
||||
if (!gMultiProcessBrowser) {
|
||||
return expectObserverCalledInProcess(aTopic);
|
||||
}
|
||||
|
||||
return BrowserTestUtils.contentTopicObserved(browsingContext, aTopic);
|
||||
}
|
||||
|
||||
function enableObserverVerificationAncestor(browsingContext) {
|
||||
// Skip these checks in single process mode as it isn't worth implementing it.
|
||||
if (!gMultiProcessBrowser) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return BrowserTestUtils.startObservingTopics(browsingContext, observerTopics);
|
||||
}
|
||||
|
||||
function disableObserverVerificationAncestor(browsingContextt) {
|
||||
if (!gMultiProcessBrowser) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return BrowserTestUtils.stopObservingTopics(
|
||||
browsingContextt,
|
||||
observerTopics
|
||||
).catch(reason => {
|
||||
ok(false, "Failed " + reason);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseRequestDeviceAncestor(
|
||||
aRequestAudio,
|
||||
aRequestVideo,
|
||||
aType,
|
||||
aBrowser,
|
||||
aBadDevice = false
|
||||
) {
|
||||
info("requesting devices");
|
||||
return SpecialPowers.spawn(
|
||||
aBrowser,
|
||||
[{ aRequestAudio, aRequestVideo, aType, aBadDevice }],
|
||||
async function(args) {
|
||||
let global = content.wrappedJSObject.document.getElementById("frame4")
|
||||
.contentWindow;
|
||||
global.requestDevice(
|
||||
args.aRequestAudio,
|
||||
args.aRequestVideo,
|
||||
args.aType,
|
||||
args.aBadDevice
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function closeStreamAncestor(browser) {
|
||||
let observerPromises = [];
|
||||
observerPromises.push(
|
||||
expectObserverCalledAncestor("recording-device-events", browser)
|
||||
);
|
||||
observerPromises.push(
|
||||
expectObserverCalledAncestor("recording-window-ended", browser)
|
||||
);
|
||||
|
||||
info("closing the stream");
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
let global = content.wrappedJSObject.document.getElementById("frame4")
|
||||
.contentWindow;
|
||||
global.closeStream();
|
||||
});
|
||||
|
||||
await Promise.all(observerPromises);
|
||||
|
||||
await assertWebRTCIndicatorStatus(null);
|
||||
}
|
||||
|
||||
var gTests = [
|
||||
{
|
||||
desc:
|
||||
"getUserMedia use persistent permissions from first party if third party is explicitly trusted",
|
||||
skipObserverVerification: true,
|
||||
run: async function checkPermissionsAncestorChain() {
|
||||
async function checkPermission(aPermission, aRequestType, aExpect) {
|
||||
info(
|
||||
`Test persistent permission ${aPermission} type ${aRequestType} expect ${aExpect}`
|
||||
);
|
||||
const uri = gBrowser.selectedBrowser.documentURI;
|
||||
// Persistent allow/deny for first party uri
|
||||
PermissionTestUtils.add(uri, aRequestType, aPermission);
|
||||
|
||||
let audio = aRequestType == "microphone";
|
||||
let video = aRequestType == "camera";
|
||||
const screen = aRequestType == "screen" ? "screen" : undefined;
|
||||
if (screen) {
|
||||
audio = false;
|
||||
video = true;
|
||||
}
|
||||
const iframeAncestor = await SpecialPowers.spawn(
|
||||
gBrowser.selectedBrowser,
|
||||
[],
|
||||
() => {
|
||||
return content.document.getElementById("frameAncestor")
|
||||
.browsingContext;
|
||||
}
|
||||
);
|
||||
|
||||
if (aExpect == PromptResult.PROMPT) {
|
||||
// Check that we get a prompt.
|
||||
const observerPromise = expectObserverCalledAncestor(
|
||||
"getUserMedia:request",
|
||||
iframeAncestor
|
||||
);
|
||||
const promise = promisePopupNotificationShown("webRTC-shareDevices");
|
||||
|
||||
await promiseRequestDeviceAncestor(
|
||||
audio,
|
||||
video,
|
||||
screen,
|
||||
iframeAncestor
|
||||
);
|
||||
await promise;
|
||||
await observerPromise;
|
||||
|
||||
// Check the label of the notification should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("webRTC-shareDevices").options
|
||||
.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
const observerPromise1 = expectObserverCalledAncestor(
|
||||
"getUserMedia:response:deny",
|
||||
iframeAncestor
|
||||
);
|
||||
const observerPromise2 = expectObserverCalledAncestor(
|
||||
"recording-window-ended",
|
||||
iframeAncestor
|
||||
);
|
||||
// Deny the request to cleanup...
|
||||
activateSecondaryAction(kActionDeny);
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
SitePermissions.removeFromPrincipal(null, aRequestType, browser);
|
||||
} else if (aExpect == PromptResult.ALLOW) {
|
||||
const observerPromise = expectObserverCalledAncestor(
|
||||
"getUserMedia:request",
|
||||
iframeAncestor
|
||||
);
|
||||
const observerPromise1 = expectObserverCalledAncestor(
|
||||
"getUserMedia:response:allow",
|
||||
iframeAncestor
|
||||
);
|
||||
const observerPromise2 = expectObserverCalledAncestor(
|
||||
"recording-device-events",
|
||||
iframeAncestor
|
||||
);
|
||||
await promiseRequestDeviceAncestor(
|
||||
audio,
|
||||
video,
|
||||
screen,
|
||||
iframeAncestor
|
||||
);
|
||||
await observerPromise;
|
||||
|
||||
await promiseNoPopupNotification("webRTC-shareDevices");
|
||||
await observerPromise1;
|
||||
await observerPromise2;
|
||||
|
||||
let expected = {};
|
||||
if (audio) {
|
||||
expected.audio = audio;
|
||||
}
|
||||
if (video) {
|
||||
expected.video = video;
|
||||
}
|
||||
|
||||
Assert.deepEqual(
|
||||
await getMediaCaptureState(),
|
||||
expected,
|
||||
"expected " + Object.keys(expected).join(" and ") + " to be shared"
|
||||
);
|
||||
|
||||
await closeStreamAncestor(iframeAncestor);
|
||||
} else if (aExpect == PromptResult.DENY) {
|
||||
const observerPromise = expectObserverCalledAncestor(
|
||||
"recording-window-ended",
|
||||
iframeAncestor
|
||||
);
|
||||
await promiseRequestDeviceAncestor(
|
||||
audio,
|
||||
video,
|
||||
screen,
|
||||
iframeAncestor
|
||||
);
|
||||
await observerPromise;
|
||||
}
|
||||
|
||||
PermissionTestUtils.remove(uri, aRequestType);
|
||||
}
|
||||
|
||||
await checkPermission(Perms.PROMPT_ACTION, "camera", PromptResult.PROMPT);
|
||||
await checkPermission(Perms.DENY_ACTION, "camera", PromptResult.DENY);
|
||||
await checkPermission(Perms.ALLOW_ACTION, "camera", PromptResult.ALLOW);
|
||||
|
||||
await checkPermission(
|
||||
Perms.PROMPT_ACTION,
|
||||
"microphone",
|
||||
PromptResult.PROMPT
|
||||
);
|
||||
await checkPermission(Perms.DENY_ACTION, "microphone", PromptResult.DENY);
|
||||
await checkPermission(
|
||||
Perms.ALLOW_ACTION,
|
||||
"microphone",
|
||||
PromptResult.ALLOW
|
||||
);
|
||||
|
||||
await checkPermission(Perms.PROMPT_ACTION, "screen", PromptResult.PROMPT);
|
||||
await checkPermission(Perms.DENY_ACTION, "screen", PromptResult.DENY);
|
||||
// Always prompt screen sharing
|
||||
await checkPermission(Perms.ALLOW_ACTION, "screen", PromptResult.PROMPT);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
add_task(async function test() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["permissions.delegation.enabled", true],
|
||||
["dom.security.featurePolicy.enabled", true],
|
||||
["dom.security.featurePolicy.header.enabled", true],
|
||||
["dom.security.featurePolicy.webidl.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
await runTests(gTests, {
|
||||
relativeURI: "get_user_media_in_xorigin_frame_ancestor.html",
|
||||
});
|
||||
});
|
@ -20,7 +20,7 @@ try {
|
||||
function message(m) {
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
document.getElementById("message").innerHTML = m;
|
||||
window.parent.postMessage(m, "*");
|
||||
parent.postMessage(m, "*");
|
||||
}
|
||||
|
||||
var gStreams = [];
|
||||
@ -51,7 +51,7 @@ function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) {
|
||||
opts.fake = true;
|
||||
}
|
||||
|
||||
window.navigator.mediaDevices.getUserMedia(opts)
|
||||
navigator.mediaDevices.getUserMedia(opts)
|
||||
.then(stream => {
|
||||
gStreams.push(stream);
|
||||
message("ok");
|
||||
|
@ -57,5 +57,7 @@ function closeStream() {
|
||||
</script>
|
||||
<iframe id="frame1" allow="camera;microphone;display-capture" src="https://test1.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"></iframe>
|
||||
<iframe id="frame2" allow="camera;microphone" src="https://test1.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"></iframe>
|
||||
<iframe id="frame3" src="https://test1.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"></iframe>
|
||||
<iframe id="frame4" allow="camera *;microphone *;display-capture *" src="https://test1.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Permissions Test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frameAncestor"
|
||||
src="https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media_in_xorigin_frame.html"
|
||||
allow="camera 'src' https://test1.example.com;microphone 'src' https://test1.example.com;display-capture 'src' https://test1.example.com"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -665,6 +665,29 @@ function promiseReloadFrame(aFrameId) {
|
||||
);
|
||||
}
|
||||
|
||||
function promiseChangeLocationFrame(aFrameId, aNewLocation) {
|
||||
return SpecialPowers.spawn(
|
||||
gBrowser.selectedBrowser.browsingContext,
|
||||
[{ aFrameId, aNewLocation }],
|
||||
async function(args) {
|
||||
let frame = content.wrappedJSObject.document.getElementById(
|
||||
args.aFrameId
|
||||
);
|
||||
return new Promise(resolve => {
|
||||
function listener() {
|
||||
frame.removeEventListener("load", listener, true);
|
||||
resolve();
|
||||
}
|
||||
frame.addEventListener("load", listener, true);
|
||||
|
||||
content.wrappedJSObject.document.getElementById(
|
||||
args.aFrameId
|
||||
).contentWindow.location = args.aNewLocation;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function openNewTestTab(leaf = "get_user_media.html") {
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
rootDir = rootDir.replace(
|
||||
|
@ -726,6 +726,24 @@ getUserMedia.shareCameraAndAudioCapture2.message = Will you allow %S to use your
|
||||
getUserMedia.shareScreenAndMicrophone3.message = Will you allow %S to use your microphone and see your screen?
|
||||
getUserMedia.shareScreenAndAudioCapture3.message = Will you allow %S to listen to this tab’s audio and see your screen?
|
||||
getUserMedia.shareAudioCapture2.message = Will you allow %S to listen to this tab’s audio?
|
||||
|
||||
# LOCALIZATION NOTE (getUserMedia.shareCameraUnsafeDelegation.message,
|
||||
# getUserMedia.shareMicrophoneUnsafeDelegation.message,
|
||||
# getUserMedia.shareScreenUnsafeDelegation.message,
|
||||
# getUserMedia.shareCameraAndMicrophoneUnsafeDelegation.message,
|
||||
# getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation.message,
|
||||
# getUserMedia.shareScreenAndMicrophoneUnsafeDelegation.message,
|
||||
# getUserMedia.shareScreenAndAudioCaptureUnsafeDelegation.message,
|
||||
# %1$S is the first party origin.
|
||||
# %2$S is the third party origin.
|
||||
getUserMedia.shareCameraUnsafeDelegation.message = Will you allow %1$S to give %2$S access to your camera?
|
||||
getUserMedia.shareMicrophoneUnsafeDelegations.message = Will you allow %1$S to give %2$S access to your microphone?
|
||||
getUserMedia.shareScreenUnsafeDelegation.message = Will you allow %1$S to give %2$S permission to see your screen?
|
||||
getUserMedia.shareCameraAndMicrophoneUnsafeDelegation.message = Will you allow %1$S to give %2$S access to your camera and microphone?
|
||||
getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation.message = Will you allow %1$S to give %2$S access to your camera and listen to this tab’s audio?
|
||||
getUserMedia.shareScreenAndMicrophoneUnsafeDelegation.message = Will you allow %1$S to give %2$S access to your microphone and see your screen?
|
||||
getUserMedia.shareScreenAndAudioCaptureUnsafeDelegation.message = Will you allow %1$S to give %2$S permission to listen to this tab’s audio and see your screen?
|
||||
|
||||
# LOCALIZATION NOTE (getUserMedia.shareScreenWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
|
||||
# %S will be the 'learn more' link
|
||||
getUserMedia.shareScreenWarning.message = Only share screens with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %S
|
||||
|
@ -529,7 +529,7 @@ function checkRequestAllowed(aRequest, aPrincipal, aBrowser) {
|
||||
if (videoDevices.length && sharingScreen) {
|
||||
camAllowed = false;
|
||||
}
|
||||
if (aRequest.isThirdPartyOrigin) {
|
||||
if (aRequest.isThirdPartyOrigin && !aRequest.shouldDelegatePermission) {
|
||||
camAllowed = false;
|
||||
micAllowed = false;
|
||||
}
|
||||
@ -643,7 +643,10 @@ function prompt(aBrowser, aRequest) {
|
||||
// If the request comes from a popup, we don't want to show the prompt,
|
||||
// but we do want to allow the request if the user previously gave permission.
|
||||
if (isPopup) {
|
||||
if (!checkRequestAllowed(aRequest, principal, aBrowser)) {
|
||||
if (
|
||||
aRequest.secondOrigin ||
|
||||
!checkRequestAllowed(aRequest, principal, aBrowser)
|
||||
) {
|
||||
denyRequest(aBrowser, aRequest);
|
||||
}
|
||||
return;
|
||||
@ -680,20 +683,39 @@ function prompt(aBrowser, aRequest) {
|
||||
// "includes()". This allows the rotation of string identifiers. We list the
|
||||
// full identifiers here so they can be cross-referenced more easily.
|
||||
let joinedRequestTypes = requestTypes.join("And");
|
||||
let stringId = [
|
||||
// Individual request types first.
|
||||
"getUserMedia.shareCamera2.message",
|
||||
"getUserMedia.shareMicrophone2.message",
|
||||
"getUserMedia.shareScreen3.message",
|
||||
"getUserMedia.shareAudioCapture2.message",
|
||||
// Combinations of the above request types last.
|
||||
"getUserMedia.shareCameraAndMicrophone2.message",
|
||||
"getUserMedia.shareCameraAndAudioCapture2.message",
|
||||
"getUserMedia.shareScreenAndMicrophone3.message",
|
||||
"getUserMedia.shareScreenAndAudioCapture3.message",
|
||||
].find(id => id.includes(joinedRequestTypes));
|
||||
let requestMessages;
|
||||
if (aRequest.secondOrigin) {
|
||||
requestMessages = [
|
||||
// Individual request types first.
|
||||
"getUserMedia.shareCameraUnsafeDelegation.message",
|
||||
"getUserMedia.shareMicrophoneUnsafeDelegation.message",
|
||||
"getUserMedia.shareScreenUnsafeDelegation.message",
|
||||
"getUserMedia.shareAudioCaptureUnsafeDelegation.message",
|
||||
// Combinations of the above request types last.
|
||||
"getUserMedia.shareCameraAndMicrophoneUnsafeDelegation.message",
|
||||
"getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation.message",
|
||||
"getUserMedia.shareScreenAndMicrophoneUnsafeDelegation.message",
|
||||
"getUserMedia.shareScreenAndAudioCaptureUnsafeDelegation.message",
|
||||
];
|
||||
} else {
|
||||
requestMessages = [
|
||||
// Individual request types first.
|
||||
"getUserMedia.shareCamera2.message",
|
||||
"getUserMedia.shareMicrophone2.message",
|
||||
"getUserMedia.shareScreen3.message",
|
||||
"getUserMedia.shareAudioCapture2.message",
|
||||
// Combinations of the above request types last.
|
||||
"getUserMedia.shareCameraAndMicrophone2.message",
|
||||
"getUserMedia.shareCameraAndAudioCapture2.me ssage",
|
||||
"getUserMedia.shareScreenAndMicrophone3.message",
|
||||
"getUserMedia.shareScreenAndAudioCapture3.message",
|
||||
];
|
||||
}
|
||||
|
||||
let message = stringBundle.getFormattedString(stringId, ["<>"], 1);
|
||||
let stringId = requestMessages.find(id => id.includes(joinedRequestTypes));
|
||||
let message = aRequest.secondOrigin
|
||||
? stringBundle.getFormattedString(stringId, ["<>", "{}"])
|
||||
: stringBundle.getFormattedString(stringId, ["<>"]);
|
||||
|
||||
let notification; // Used by action callbacks.
|
||||
let mainAction = {
|
||||
@ -787,7 +809,12 @@ function prompt(aBrowser, aRequest) {
|
||||
// it is handled synchronously before we add the notification.
|
||||
// Handling of ALLOW is delayed until the popupshowing event,
|
||||
// to avoid granting permissions automatically to background tabs.
|
||||
if (checkRequestAllowed(aRequest, principal, aBrowser)) {
|
||||
// If we have a secondOrigin, it means this request is lacking explicit
|
||||
// trust, and we should always prompt even in with persistent permission.
|
||||
if (
|
||||
!aRequest.secondOrigin &&
|
||||
checkRequestAllowed(aRequest, principal, aBrowser)
|
||||
) {
|
||||
this.remove();
|
||||
return true;
|
||||
}
|
||||
@ -1181,11 +1208,28 @@ function prompt(aBrowser, aRequest) {
|
||||
},
|
||||
};
|
||||
|
||||
// Don't offer "always remember" action in PB mode or from third party
|
||||
if (
|
||||
!PrivateBrowsingUtils.isBrowserPrivate(aBrowser) &&
|
||||
!aRequest.isThirdPartyOrigin
|
||||
) {
|
||||
function shouldShowAlwaysRemember() {
|
||||
// Don't offer "always remember" action in PB mode
|
||||
if (PrivateBrowsingUtils.isBrowserPrivate(aBrowser)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't offer "always remember" action in third party with no permission
|
||||
// delegation
|
||||
if (aRequest.isThirdPartyOrigin && !aRequest.shouldDelegatePermission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't offer "always remember" action in maybe unsafe permission
|
||||
// delegation
|
||||
if (aRequest.shouldDelegatePermission && aRequest.secondOrigin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shouldShowAlwaysRemember()) {
|
||||
// Disable the permanent 'Allow' action if the connection isn't secure, or for
|
||||
// screen/audio sharing (because we can't guess which window the user wants to
|
||||
// share without prompting).
|
||||
@ -1234,6 +1278,10 @@ function prompt(aBrowser, aRequest) {
|
||||
}
|
||||
options.popupIconClass = iconClass + "-icon";
|
||||
|
||||
if (aRequest.secondOrigin) {
|
||||
options.secondName = getHostOrExtensionName(null, aRequest.secondOrigin);
|
||||
}
|
||||
|
||||
notification = chromeDoc.defaultView.PopupNotifications.show(
|
||||
aBrowser,
|
||||
"webRTC-shareDevices",
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nss.h"
|
||||
#include "nsVariant.h"
|
||||
#include "PermissionDelegateHandler.h"
|
||||
#include "pk11pub.h"
|
||||
#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
|
||||
#include "VideoStreamTrack.h"
|
||||
@ -2543,55 +2544,41 @@ RefPtr<MediaManager::StreamPromise> MediaManager::GetUserMedia(
|
||||
|
||||
if (!privileged) {
|
||||
// Check if this site has had persistent permissions denied.
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
RefPtr<PermissionDelegateHandler> permDelegate =
|
||||
doc->GetPermissionDelegateHandler();
|
||||
MOZ_RELEASE_ASSERT(permDelegate);
|
||||
|
||||
uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
if (IsOn(c.mAudio)) {
|
||||
if (audioType == MediaSourceEnum::Microphone) {
|
||||
if (Preferences::GetBool("media.getusermedia.microphone.deny", false) ||
|
||||
!FeaturePolicyUtils::IsFeatureAllowed(
|
||||
doc, NS_LITERAL_STRING("microphone"))) {
|
||||
if (Preferences::GetBool("media.getusermedia.microphone.deny", false)) {
|
||||
audioPerm = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("microphone"), &audioPerm);
|
||||
rv = permDelegate->GetPermission(NS_LITERAL_CSTRING("microphone"),
|
||||
&audioPerm, true);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
} else {
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(
|
||||
doc, NS_LITERAL_STRING("display-capture"))) {
|
||||
audioPerm = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("screen"), &audioPerm);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
rv = permDelegate->GetPermission(NS_LITERAL_CSTRING("screen"),
|
||||
&audioPerm, true);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
if (IsOn(c.mVideo)) {
|
||||
if (videoType == MediaSourceEnum::Camera) {
|
||||
if (Preferences::GetBool("media.getusermedia.camera.deny", false) ||
|
||||
!FeaturePolicyUtils::IsFeatureAllowed(
|
||||
doc, NS_LITERAL_STRING("camera"))) {
|
||||
if (Preferences::GetBool("media.getusermedia.camera.deny", false)) {
|
||||
videoPerm = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("camera"), &videoPerm);
|
||||
rv = permDelegate->GetPermission(NS_LITERAL_CSTRING("camera"),
|
||||
&videoPerm, true);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
} else {
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(
|
||||
doc, NS_LITERAL_STRING("display-capture"))) {
|
||||
videoPerm = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("screen"), &videoPerm);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
rv = permDelegate->GetPermission(NS_LITERAL_CSTRING("screen"),
|
||||
&videoPerm, true);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4001,35 +3988,24 @@ bool MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId) {
|
||||
|
||||
// Check if this site has persistent permissions.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPermissionManager> mgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false; // no permission manager no permissions!
|
||||
RefPtr<PermissionDelegateHandler> permDelegate =
|
||||
doc->GetPermissionDelegateHandler();
|
||||
if (NS_WARN_IF(!permDelegate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
{
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(
|
||||
doc, NS_LITERAL_STRING("microphone"))) {
|
||||
audio = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = mgr->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("microphone"), &audio);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv = permDelegate->GetPermission(NS_LITERAL_CSTRING("microphone"), &audio,
|
||||
true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(doc,
|
||||
NS_LITERAL_STRING("camera"))) {
|
||||
video = nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
rv = mgr->TestExactPermissionFromPrincipal(
|
||||
principal, NS_LITERAL_CSTRING("camera"), &video);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv =
|
||||
permDelegate->GetPermission(NS_LITERAL_CSTRING("camera"), &video, true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return audio == nsIPermissionManager::ALLOW_ACTION ||
|
||||
|
@ -21,7 +21,7 @@ add_task(async function test_notifications_permission() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// Set pref to exercise relevant code path for regression test.
|
||||
["permissions.delegation.enable", true],
|
||||
["permissions.delegation.enabled", true],
|
||||
// Automatically dismiss the permission request when it appears.
|
||||
["dom.webnotifications.requireuserinteraction", true],
|
||||
],
|
||||
|
@ -231,7 +231,7 @@
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["permissions.delegation.enable", true],
|
||||
["permissions.delegation.enabled", true],
|
||||
]}).then(nextTest);
|
||||
</script>
|
||||
</body>
|
||||
|
@ -24,7 +24,7 @@ add_task(async function testNoPermissionPrompt() {
|
||||
{
|
||||
set: [
|
||||
["dom.security.featurePolicy.enabled", true],
|
||||
["permissions.delegation.enable", true],
|
||||
["permissions.delegation.enabled", true],
|
||||
["dom.security.featurePolicy.header.enabled", true],
|
||||
["dom.security.featurePolicy.webidl.enabled", true],
|
||||
],
|
||||
|
@ -7002,7 +7002,7 @@
|
||||
# Prefs starting with "permissions."
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
- name: permissions.delegation.enable
|
||||
- name: permissions.delegation.enabled
|
||||
type: bool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
@ -1 +1 @@
|
||||
prefs: [permissions.delegation.enable:true, dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]
|
||||
prefs: [permissions.delegation.enabled:true, dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]
|
||||
|
@ -22,7 +22,7 @@
|
||||
".popup-notification-description > b:last-of-type":
|
||||
"text=secondname,popupid",
|
||||
".popup-notification-description > span:last-of-type":
|
||||
"secondendlabel,popupid",
|
||||
"text=secondendlabel,popupid",
|
||||
".popup-notification-closebutton":
|
||||
"oncommand=closebuttoncommand,hidden=closebuttonhidden",
|
||||
".popup-notification-learnmore-link":
|
||||
|
@ -978,6 +978,9 @@ PopupNotifications.prototype = {
|
||||
if ("secondName" in desc && "secondEnd" in desc) {
|
||||
popupnotification.setAttribute("secondname", desc.secondName);
|
||||
popupnotification.setAttribute("secondendlabel", desc.secondEnd);
|
||||
} else {
|
||||
popupnotification.removeAttribute("secondname");
|
||||
popupnotification.removeAttribute("secondendlabel");
|
||||
}
|
||||
|
||||
popupnotification.setAttribute("id", popupnotificationID);
|
||||
|
Loading…
Reference in New Issue
Block a user