mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1483631
- Restrict nested permission requests in ContentPermissionPrompt with permission delegate r=baku,mccr8
Differential Revision: https://phabricator.services.mozilla.com/D47416 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
b727e2abc1
commit
fd744cea01
@ -1727,6 +1727,12 @@ pref("view_source.tab", true);
|
||||
|
||||
pref("dom.serviceWorkers.enabled", true);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("dom.security.featurePolicy.enabled", true);
|
||||
#else
|
||||
pref("dom.security.featurePolicy.enabled", false);
|
||||
#endif
|
||||
|
||||
// Enable Push API.
|
||||
pref("dom.push.enabled", true);
|
||||
|
||||
|
@ -9,6 +9,8 @@ skip-if = debug || os == "linux" && asan # Bug 1522069
|
||||
[browser_permissions_delegate_vibrate.js]
|
||||
support-files=
|
||||
empty.html
|
||||
[browser_permission_delegate_geo.js]
|
||||
skip-if = fission # Bug 1587743
|
||||
[browser_permissions_event_telemetry.js]
|
||||
[browser_permissions_postPrompt.js]
|
||||
support-files=
|
||||
|
@ -0,0 +1,155 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const ORIGIN = "https://example.com";
|
||||
const CROSS_SUBFRAME_PAGE =
|
||||
getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) +
|
||||
"temporary_permissions_subframe.html";
|
||||
|
||||
const PromptResult = {
|
||||
ALLOW: "allow",
|
||||
DENY: "deny",
|
||||
PROMPT: "prompt",
|
||||
};
|
||||
|
||||
var Perms = Services.perms;
|
||||
var uri = NetUtil.newURI(ORIGIN);
|
||||
var principal = Services.scriptSecurityManager.createContentPrincipal(uri, {});
|
||||
|
||||
add_task(async function setup() {
|
||||
await new Promise(r => {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{
|
||||
set: [
|
||||
["dom.security.featurePolicy.enabled", true],
|
||||
["dom.security.featurePolicy.header.enabled", true],
|
||||
["dom.security.featurePolicy.webidl.enabled", true],
|
||||
["permissions.delegation.enabled", true],
|
||||
],
|
||||
},
|
||||
r
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Test that temp blocked permissions in first party affect the third party
|
||||
// iframe.
|
||||
add_task(async function testUseTempPermissionsFirstParty() {
|
||||
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
|
||||
browser
|
||||
) {
|
||||
SitePermissions.setForPrincipal(
|
||||
principal,
|
||||
"geo",
|
||||
SitePermissions.BLOCK,
|
||||
SitePermissions.SCOPE_TEMPORARY,
|
||||
browser
|
||||
);
|
||||
|
||||
// Request a permission.
|
||||
await ContentTask.spawn(browser, uri.host, async function(host0) {
|
||||
let frame = content.document.getElementById("frame");
|
||||
function onMessage(event) {
|
||||
// Check the result right here because there's no notification
|
||||
is(event.data, "deny", "Expected deny for third party");
|
||||
content.window.removeEventListener("message", onMessage);
|
||||
}
|
||||
|
||||
content.window.addEventListener("message", onMessage);
|
||||
|
||||
await content.SpecialPowers.spawn(frame, [host0], async function(host) {
|
||||
const { E10SUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/E10SUtils.jsm"
|
||||
);
|
||||
|
||||
E10SUtils.wrapHandlingUserInput(this.content, true, function() {
|
||||
let frameDoc = this.content.document;
|
||||
frameDoc.getElementById("geo").click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
SitePermissions.removeFromPrincipal(principal, "geo", browser);
|
||||
});
|
||||
});
|
||||
|
||||
// Test that persistent permissions in first party affect the third party
|
||||
// iframe.
|
||||
add_task(async function testUsePersistentPermissionsFirstParty() {
|
||||
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
|
||||
browser
|
||||
) {
|
||||
async function checkPermission(aPermission, aExpect) {
|
||||
PermissionTestUtils.add(uri, "geo", aPermission);
|
||||
|
||||
let waitForPrompt = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown"
|
||||
);
|
||||
|
||||
// Request a permission.
|
||||
await ContentTask.spawn(
|
||||
browser,
|
||||
{ host: uri.host, expect: aExpect },
|
||||
async function(args) {
|
||||
let frame = content.document.getElementById("frame");
|
||||
if (args.expect != "prompt") {
|
||||
function onMessage(event) {
|
||||
// Check the result right here because there's no notification
|
||||
is(
|
||||
event.data,
|
||||
args.expect,
|
||||
"Expected correct permission for third party"
|
||||
);
|
||||
content.window.removeEventListener("message", onMessage);
|
||||
}
|
||||
content.window.addEventListener("message", onMessage);
|
||||
}
|
||||
|
||||
await content.SpecialPowers.spawn(frame, [args.host], async function(
|
||||
host
|
||||
) {
|
||||
const { E10SUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/E10SUtils.jsm"
|
||||
);
|
||||
|
||||
E10SUtils.wrapHandlingUserInput(this.content, true, function() {
|
||||
let frameDoc = this.content.document;
|
||||
frameDoc.getElementById("geo").click();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (aExpect == PromptResult.PROMPT) {
|
||||
await waitForPrompt;
|
||||
// Notification is shown, check label and deny to clean
|
||||
let popuphidden = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popuphidden"
|
||||
);
|
||||
|
||||
let notification = PopupNotifications.panel.firstElementChild;
|
||||
// Check the label of the notificaiton should be the first party
|
||||
is(
|
||||
PopupNotifications.getNotification("geolocation").options.name,
|
||||
uri.host,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
|
||||
|
||||
await popuphidden;
|
||||
SitePermissions.removeFromPrincipal(null, "geo", browser);
|
||||
}
|
||||
|
||||
PermissionTestUtils.remove(uri, "geo");
|
||||
}
|
||||
|
||||
await checkPermission(Perms.PROMPT_ACTION, PromptResult.PROMPT);
|
||||
await checkPermission(Perms.DENY_ACTION, PromptResult.DENY);
|
||||
await checkPermission(Perms.ALLOW_ACTION, PromptResult.ALLOW);
|
||||
});
|
||||
});
|
@ -18,6 +18,15 @@ function requestPush() {
|
||||
});
|
||||
}
|
||||
|
||||
function requestGeo() {
|
||||
return navigator.geolocation.getCurrentPosition(() => {
|
||||
parent.postMessage("allow", "*");
|
||||
}, () => {
|
||||
parent.postMessage("deny", "*");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.onmessage = function(event) {
|
||||
switch (event.data) {
|
||||
case "push":
|
||||
@ -30,7 +39,7 @@ window.onmessage = function(event) {
|
||||
<body onkeydown="gKeyDowns++;" onkeypress="gKeyPresses++">
|
||||
<!-- This page could eventually request permissions from content
|
||||
and make sure that chrome responds appropriately -->
|
||||
<button id="geo" onclick="navigator.geolocation.getCurrentPosition(() => {})">Geolocation</button>
|
||||
<button id="geo" onclick="requestGeo()">Geolocation</button>
|
||||
<button id="desktop-notification" onclick="Notification.requestPermission()">Notifications</button>
|
||||
<button id="push" onclick="requestPush()">Push Notifications</button>
|
||||
<button id="camera" onclick="navigator.mediaDevices.getUserMedia({video: true, fake: true})">Camera</button>
|
||||
|
@ -162,6 +162,14 @@ var PermissionPromptPrototype = {
|
||||
return this.principal.URI.hostPort;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates the type of the permission request from content. This type might
|
||||
* be different from the permission key used in the permissions database.
|
||||
*/
|
||||
get type() {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If the nsIPermissionManager is being queried and written
|
||||
* to for this permission request, set this to the key to be
|
||||
@ -711,6 +719,10 @@ var PermissionPromptForRequestPrototype = {
|
||||
},
|
||||
|
||||
get principal() {
|
||||
if (Services.prefs.getBoolPref("permissions.delegate.enable", false)) {
|
||||
let request = this.request.QueryInterface(Ci.nsIContentPermissionRequest);
|
||||
return request.getDelegatePrincipal(this.type);
|
||||
}
|
||||
return this.request.principal;
|
||||
},
|
||||
|
||||
@ -739,6 +751,10 @@ function GeolocationPermissionPrompt(request) {
|
||||
GeolocationPermissionPrompt.prototype = {
|
||||
__proto__: PermissionPromptForRequestPrototype,
|
||||
|
||||
get type() {
|
||||
return "geo";
|
||||
},
|
||||
|
||||
get permissionKey() {
|
||||
return "geo";
|
||||
},
|
||||
@ -888,6 +904,10 @@ function DesktopNotificationPermissionPrompt(request) {
|
||||
DesktopNotificationPermissionPrompt.prototype = {
|
||||
__proto__: PermissionPromptForRequestPrototype,
|
||||
|
||||
get type() {
|
||||
return "desktop-notification";
|
||||
},
|
||||
|
||||
get permissionKey() {
|
||||
return "desktop-notification";
|
||||
},
|
||||
@ -991,6 +1011,10 @@ function PersistentStoragePermissionPrompt(request) {
|
||||
PersistentStoragePermissionPrompt.prototype = {
|
||||
__proto__: PermissionPromptForRequestPrototype,
|
||||
|
||||
get type() {
|
||||
return "persistent-storage";
|
||||
},
|
||||
|
||||
get permissionKey() {
|
||||
return "persistent-storage";
|
||||
},
|
||||
@ -1079,6 +1103,10 @@ function MIDIPermissionPrompt(request) {
|
||||
MIDIPermissionPrompt.prototype = {
|
||||
__proto__: PermissionPromptForRequestPrototype,
|
||||
|
||||
get type() {
|
||||
return "midi";
|
||||
},
|
||||
|
||||
get permissionKey() {
|
||||
return this.permName;
|
||||
},
|
||||
@ -1171,6 +1199,10 @@ StorageAccessPermissionPrompt.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
get type() {
|
||||
return "storage-access";
|
||||
},
|
||||
|
||||
get permissionKey() {
|
||||
// Make sure this name is unique per each third-party tracker
|
||||
return "storage-access-" + this.principal.origin;
|
||||
|
@ -85,13 +85,14 @@ function makeMockPermissionRequest(browser) {
|
||||
};
|
||||
let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
types.appendElement(type);
|
||||
let principal = browser.contentPrincipal;
|
||||
let result = {
|
||||
types,
|
||||
documentDOMContentLoadedTimestamp: 0,
|
||||
isHandlingUserInput: false,
|
||||
userHadInteractedWithDocument: false,
|
||||
principal: browser.contentPrincipal,
|
||||
topLevelPrincipal: browser.contentPrincipal,
|
||||
principal,
|
||||
topLevelPrincipal: principal,
|
||||
requester: null,
|
||||
_cancelled: false,
|
||||
cancel() {
|
||||
@ -101,6 +102,9 @@ function makeMockPermissionRequest(browser) {
|
||||
allow() {
|
||||
this._allowed = true;
|
||||
},
|
||||
getDelegatePrincipal(aType) {
|
||||
return principal;
|
||||
},
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
using mozilla::Unused; // <snicker>
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla;
|
||||
|
||||
using DelegateInfo = PermissionDelegateHandler::PermissionDelegateInfo;
|
||||
#define kVisibilityChange "visibilitychange"
|
||||
|
||||
class VisibilityChangeListener final : public nsIDOMEventListener {
|
||||
@ -581,9 +581,21 @@ ContentPermissionRequestBase::GetPrincipal(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionRequestBase::GetDelegatePrincipal(
|
||||
const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
|
||||
return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
|
||||
aRequestingPrincipal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionRequestBase::GetTopLevelPrincipal(
|
||||
nsIPrincipal** aRequestingPrincipal) {
|
||||
if (!mTopLevelPrincipal) {
|
||||
*aRequestingPrincipal = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -911,10 +923,27 @@ nsContentPermissionRequestProxy::GetTopLevelPrincipal(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mParent->mTopLevelPrincipal) {
|
||||
*aRequestingPrincipal = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetDelegatePrincipal(
|
||||
const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
|
||||
NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
|
||||
if (mParent == nullptr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
|
||||
aRequestingPrincipal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetElement(Element** aRequestingElement) {
|
||||
NS_ENSURE_ARG_POINTER(aRequestingElement);
|
||||
|
@ -120,6 +120,8 @@ class ContentPermissionRequestBase : public nsIContentPermissionRequest {
|
||||
|
||||
NS_IMETHOD GetTypes(nsIArray** aTypes) override;
|
||||
NS_IMETHOD GetPrincipal(nsIPrincipal** aPrincipal) override;
|
||||
NS_IMETHOD GetDelegatePrincipal(const nsACString& aType,
|
||||
nsIPrincipal** aPrincipal) override;
|
||||
NS_IMETHOD GetTopLevelPrincipal(nsIPrincipal** aTopLevelPrincipal) override;
|
||||
NS_IMETHOD GetWindow(mozIDOMWindow** aWindow) override;
|
||||
NS_IMETHOD GetElement(mozilla::dom::Element** aElement) override;
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/FeaturePolicyUtils.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/GeolocationPositionError.h"
|
||||
#include "mozilla/dom/GeolocationPositionErrorBinding.h"
|
||||
@ -984,21 +983,6 @@ bool Geolocation::ShouldBlockInsecureRequests() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Geolocation::FeaturePolicyBlocked() const {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mOwner);
|
||||
if (!win) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<Document> doc = win->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return FeaturePolicyUtils::IsFeatureAllowed(doc,
|
||||
NS_LITERAL_STRING("geolocation"));
|
||||
}
|
||||
|
||||
bool Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest) {
|
||||
if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
|
||||
this->NotifyAllowedRequest(aRequest);
|
||||
@ -1051,7 +1035,7 @@ nsresult Geolocation::GetCurrentPosition(GeoPositionCallback callback,
|
||||
static_cast<uint8_t>(mProtocolType), target);
|
||||
|
||||
if (!StaticPrefs::geo_enabled() || ShouldBlockInsecureRequests() ||
|
||||
!FeaturePolicyBlocked()) {
|
||||
!request->CheckPermissionDelegate()) {
|
||||
request->RequestDelayedTask(target,
|
||||
nsGeolocationRequest::DelayedTaskType::Deny);
|
||||
return NS_OK;
|
||||
@ -1124,7 +1108,7 @@ int32_t Geolocation::WatchPosition(GeoPositionCallback aCallback,
|
||||
watchId);
|
||||
|
||||
if (!StaticPrefs::geo_enabled() || ShouldBlockInsecureRequests() ||
|
||||
!FeaturePolicyBlocked()) {
|
||||
!request->CheckPermissionDelegate()) {
|
||||
request->RequestDelayedTask(target,
|
||||
nsGeolocationRequest::DelayedTaskType::Deny);
|
||||
return watchId;
|
||||
|
@ -205,10 +205,6 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache {
|
||||
// within a context that is not secure.
|
||||
bool ShouldBlockInsecureRequests() const;
|
||||
|
||||
// Return whather the Feature 'geolocation' is blocked by FeaturePolicy
|
||||
// directive.
|
||||
bool FeaturePolicyBlocked() const;
|
||||
|
||||
// Two callback arrays. The first |mPendingCallbacks| holds objects for only
|
||||
// one callback and then they are released/removed from the array. The second
|
||||
// |mWatchingCallbacks| holds objects until the object is explictly removed or
|
||||
|
@ -98,6 +98,15 @@ interface nsIContentPermissionRequest : nsISupports {
|
||||
*/
|
||||
readonly attribute nsIContentPermissionRequester requester;
|
||||
|
||||
/*
|
||||
* Get delegate principal of the permission request. This will return nullptr,
|
||||
* or request's principal or top level principal based on the delegate policy
|
||||
* will be applied for a given type.
|
||||
*
|
||||
* @param aType the permission type to get
|
||||
*/
|
||||
nsIPrincipal getDelegatePrincipal(in ACString aType);
|
||||
|
||||
/**
|
||||
* allow or cancel the request
|
||||
*/
|
||||
|
@ -4622,8 +4622,13 @@ ContentParent::AllocPContentPermissionRequestParent(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIPrincipal* topPrincipal = aTopLevelPrincipal;
|
||||
if (!topPrincipal) {
|
||||
nsCOMPtr<nsIPrincipal> principal = tp->GetContentPrincipal();
|
||||
topPrincipal = principal;
|
||||
}
|
||||
return nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
aRequests, tp->GetOwnerElement(), aPrincipal, aTopLevelPrincipal,
|
||||
aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
|
||||
aIsHandlingUserInput, aDocumentHasUserInput, aPageLoadTimestamp, aTabId);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,11 @@ static const DelegateInfo sPermissionsMap[] = {
|
||||
DelegatePolicy::ePersistDeniedCrossOrigin},
|
||||
{"persistent-storage", nullptr, DelegatePolicy::ePersistDeniedCrossOrigin},
|
||||
{"vibration", nullptr, DelegatePolicy::ePersistDeniedCrossOrigin},
|
||||
{"midi", nullptr, DelegatePolicy::eDelegateUseIframeOrigin},
|
||||
{"storage-access", nullptr, DelegatePolicy::eDelegateUseIframeOrigin},
|
||||
{"camera", u"camera", DelegatePolicy::eDelegateUseFeaturePolicy},
|
||||
{"microphone", u"microphone", DelegatePolicy::eDelegateUseFeaturePolicy},
|
||||
{"screen", u"display-capture", DelegatePolicy::eDelegateUseFeaturePolicy},
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(PermissionDelegateHandler)
|
||||
@ -47,8 +52,9 @@ PermissionDelegateHandler::PermissionDelegateHandler(dom::Document* aDocument)
|
||||
MOZ_ASSERT(aDocument);
|
||||
}
|
||||
|
||||
/* static */
|
||||
const DelegateInfo* PermissionDelegateHandler::GetPermissionDelegateInfo(
|
||||
const nsAString& aPermissionName) const {
|
||||
const nsAString& aPermissionName) {
|
||||
nsAutoString lowerContent(aPermissionName);
|
||||
ToLowerCase(lowerContent);
|
||||
|
||||
@ -61,6 +67,32 @@ const DelegateInfo* PermissionDelegateHandler::GetPermissionDelegateInfo(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult PermissionDelegateHandler::GetDelegatePrincipal(
|
||||
const nsACString& aType, nsIContentPermissionRequest* aRequest,
|
||||
nsIPrincipal** aResult) {
|
||||
MOZ_ASSERT(aRequest);
|
||||
|
||||
if (!StaticPrefs::permissions_delegation_enabled()) {
|
||||
return aRequest->GetPrincipal(aResult);
|
||||
}
|
||||
|
||||
const DelegateInfo* info =
|
||||
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
|
||||
if (!info) {
|
||||
*aResult = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (info->mPolicy == DelegatePolicy::eDelegateUseTopOrigin ||
|
||||
(info->mPolicy == DelegatePolicy::eDelegateUseFeaturePolicy &&
|
||||
StaticPrefs::dom_security_featurePolicy_enabled())) {
|
||||
return aRequest->GetTopLevelPrincipal(aResult);
|
||||
}
|
||||
|
||||
return aRequest->GetPrincipal(aResult);
|
||||
}
|
||||
|
||||
bool PermissionDelegateHandler::Initialize() {
|
||||
MOZ_ASSERT(mDocument);
|
||||
|
||||
@ -86,14 +118,21 @@ static bool IsTopWindowContent(Document* aDocument) {
|
||||
return browsingContext && browsingContext->IsTopContent();
|
||||
}
|
||||
|
||||
bool PermissionDelegateHandler::HasFeaturePolicyAllowed(
|
||||
const DelegateInfo* info) const {
|
||||
if (info->mPolicy != DelegatePolicy::eDelegateUseFeaturePolicy ||
|
||||
!info->mFeatureName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAutoString featureName(info->mFeatureName);
|
||||
return FeaturePolicyUtils::IsFeatureAllowed(mDocument, featureName);
|
||||
}
|
||||
|
||||
bool PermissionDelegateHandler::HasPermissionDelegated(
|
||||
const nsACString& aType) {
|
||||
MOZ_ASSERT(mDocument);
|
||||
|
||||
if (!StaticPrefs::permissions_delegation_enable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// System principal should have right to make permission request
|
||||
if (mPrincipal->IsSystemPrincipal()) {
|
||||
return true;
|
||||
@ -101,21 +140,12 @@ bool PermissionDelegateHandler::HasPermissionDelegated(
|
||||
|
||||
const DelegateInfo* info =
|
||||
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
|
||||
|
||||
// If the type is not in the supported list, auto denied
|
||||
if (!info) {
|
||||
if (!info || !HasFeaturePolicyAllowed(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info->mPolicy == DelegatePolicy::eDelegateUseFeaturePolicy &&
|
||||
info->mFeatureName) {
|
||||
nsAutoString featureName(info->mFeatureName);
|
||||
// Default allowlist for a feature used in permissions delegate should be
|
||||
// set to eSelf, to ensure that permission is denied by default and only
|
||||
// have the opportunity to request permission with allow attribute.
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(mDocument, featureName)) {
|
||||
return false;
|
||||
}
|
||||
if (!StaticPrefs::permissions_delegation_enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info->mPolicy == DelegatePolicy::ePersistDeniedCrossOrigin &&
|
||||
@ -137,37 +167,23 @@ nsresult PermissionDelegateHandler::GetPermission(const nsACString& aType,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const DelegateInfo* info =
|
||||
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
|
||||
if (!info || !HasFeaturePolicyAllowed(info)) {
|
||||
*aPermission = nsIPermissionManager::DENY_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult (NS_STDCALL nsIPermissionManager::*testPermission)(
|
||||
nsIPrincipal*, const nsACString&, uint32_t*) =
|
||||
aExactHostMatch ? &nsIPermissionManager::TestExactPermissionFromPrincipal
|
||||
: &nsIPermissionManager::TestPermissionFromPrincipal;
|
||||
|
||||
if (!StaticPrefs::permissions_delegation_enable()) {
|
||||
if (!StaticPrefs::permissions_delegation_enabled()) {
|
||||
return (mPermissionManager->*testPermission)(mPrincipal, aType,
|
||||
aPermission);
|
||||
}
|
||||
|
||||
const DelegateInfo* info =
|
||||
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
|
||||
|
||||
// If the type is not in the supported list, auto denied
|
||||
if (!info) {
|
||||
*aPermission = nsIPermissionManager::DENY_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (info->mPolicy == DelegatePolicy::eDelegateUseFeaturePolicy &&
|
||||
info->mFeatureName) {
|
||||
nsAutoString featureName(info->mFeatureName);
|
||||
// Default allowlist for a feature used in permissions delegate should be
|
||||
// set to eSelf, to ensure that permission is denied by default and only
|
||||
// have the opportunity to request permission with allow attribute.
|
||||
if (!FeaturePolicyUtils::IsFeatureAllowed(mDocument, featureName)) {
|
||||
*aPermission = nsIPermissionManager::DENY_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->mPolicy == DelegatePolicy::ePersistDeniedCrossOrigin &&
|
||||
!IsTopWindowContent(mDocument) &&
|
||||
!mPrincipal->Subsumes(mTopLevelPrincipal)) {
|
||||
|
@ -115,15 +115,40 @@ class PermissionDelegateHandler final : nsISupports {
|
||||
*/
|
||||
void DropDocumentReference() { mDocument = nullptr; }
|
||||
|
||||
private:
|
||||
virtual ~PermissionDelegateHandler() = default;
|
||||
|
||||
/*
|
||||
* Helper function to return the delegate info value for aPermissionName.
|
||||
* @param aPermissionName the permission name to get
|
||||
*/
|
||||
const PermissionDelegateInfo* GetPermissionDelegateInfo(
|
||||
const nsAString& aPermissionName) const;
|
||||
static const PermissionDelegateInfo* GetPermissionDelegateInfo(
|
||||
const nsAString& aPermissionName);
|
||||
|
||||
/*
|
||||
* Helper function to return the delegate principal. This will return nullptr,
|
||||
* or request's principal or top level principal based on the delegate policy
|
||||
* will be applied for a given type.
|
||||
* We use this function when prompting, no need to perform permission check
|
||||
* (deny/allow).
|
||||
*
|
||||
* @param aType the permission type to get
|
||||
* @param aRequest The request which the principal is get from.
|
||||
* @param aResult out argument which will be a principal that we
|
||||
* will return from this function.
|
||||
*/
|
||||
static nsresult GetDelegatePrincipal(const nsACString& aType,
|
||||
nsIContentPermissionRequest* aRequest,
|
||||
nsIPrincipal** aResult);
|
||||
|
||||
private:
|
||||
virtual ~PermissionDelegateHandler() = default;
|
||||
|
||||
/*
|
||||
* Check whether the permission is blocked by FeaturePolicy directive.
|
||||
* Default allowlist for a featureName of permission used in permissions
|
||||
* delegate should be set to eSelf, to ensure that permission is denied by
|
||||
* default and only have the opportunity to request permission with allow
|
||||
* attribute.
|
||||
*/
|
||||
bool HasFeaturePolicyAllowed(const PermissionDelegateInfo* info) const;
|
||||
|
||||
// A weak pointer to our document. Nulled out by DropDocumentReference.
|
||||
mozilla::dom::Document* mDocument;
|
||||
|
Loading…
Reference in New Issue
Block a user