зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1483631
- Prompt with both first party and third party origin if we are delegating permission with allows all feature policy. r=baku
Differential Revision: https://phabricator.services.mozilla.com/D51839 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
a0b817ac65
Коммит
1e82cdc161
|
@ -2,7 +2,8 @@
|
|||
support-files=
|
||||
head.js
|
||||
permissions.html
|
||||
|
||||
temporary_permissions_subframe.html
|
||||
temporary_permissions_frame.html
|
||||
[browser_canvas_fingerprinting_resistance.js]
|
||||
skip-if = debug || os == "linux" && asan # Bug 1522069
|
||||
[browser_permissions.js]
|
||||
|
@ -21,7 +22,6 @@ support-files=
|
|||
[browser_reservedkey.js]
|
||||
[browser_temporary_permissions.js]
|
||||
support-files =
|
||||
temporary_permissions_subframe.html
|
||||
../webrtc/get_user_media.html
|
||||
[browser_autoplay_blocked.js]
|
||||
support-files =
|
||||
|
|
|
@ -8,6 +8,10 @@ const CROSS_SUBFRAME_PAGE =
|
|||
getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) +
|
||||
"temporary_permissions_subframe.html";
|
||||
|
||||
const CROSS_FRAME_PAGE =
|
||||
getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) +
|
||||
"temporary_permissions_frame.html";
|
||||
|
||||
const PromptResult = {
|
||||
ALLOW: "allow",
|
||||
DENY: "deny",
|
||||
|
@ -18,6 +22,93 @@ var Perms = Services.perms;
|
|||
var uri = NetUtil.newURI(ORIGIN);
|
||||
var principal = Services.scriptSecurityManager.createContentPrincipal(uri, {});
|
||||
|
||||
async function checkNotificationBothOrigins(
|
||||
firstPartyOrigin,
|
||||
thirdPartyOrigin
|
||||
) {
|
||||
// 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,
|
||||
firstPartyOrigin,
|
||||
"Use first party's origin"
|
||||
);
|
||||
|
||||
// Check the second name of the notificaiton should be the third party
|
||||
is(
|
||||
PopupNotifications.getNotification("geolocation").options.secondName,
|
||||
thirdPartyOrigin,
|
||||
"Use third party's origin"
|
||||
);
|
||||
|
||||
// Check remember checkbox is hidden
|
||||
let checkbox = notification.checkbox;
|
||||
ok(!!checkbox, "checkbox is present");
|
||||
ok(checkbox.hidden, "checkbox is not visible");
|
||||
ok(!checkbox.checked, "checkbox not checked");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
|
||||
await popuphidden;
|
||||
}
|
||||
|
||||
async function checkGeolocation(browser, frameId, expect) {
|
||||
let waitForPrompt = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown"
|
||||
);
|
||||
|
||||
let isPrompt = expect == PromptResult.PROMPT;
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[{ frameId, expect, isPrompt }],
|
||||
async args => {
|
||||
let frame = content.document.getElementById(args.frameId);
|
||||
|
||||
let waitForNoPrompt = new Promise(resolve => {
|
||||
function onMessage(event) {
|
||||
// Check the result right here because there's no notification
|
||||
Assert.equal(
|
||||
event.data,
|
||||
args.expect,
|
||||
"Correct expectation for third party"
|
||||
);
|
||||
content.window.removeEventListener("message", onMessage);
|
||||
resolve();
|
||||
}
|
||||
|
||||
if (!args.isPrompt) {
|
||||
content.window.addEventListener("message", onMessage);
|
||||
}
|
||||
});
|
||||
|
||||
await content.SpecialPowers.spawn(frame, [], async () => {
|
||||
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 (!args.isPrompt) {
|
||||
await waitForNoPrompt;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (isPrompt) {
|
||||
await waitForPrompt;
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await new Promise(r => {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
|
@ -48,28 +139,7 @@ add_task(async function testUseTempPermissionsFirstParty() {
|
|||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
await checkGeolocation(browser, "frame", PromptResult.DENY);
|
||||
|
||||
SitePermissions.removeFromPrincipal(principal, "geo", browser);
|
||||
});
|
||||
|
@ -83,48 +153,9 @@ add_task(async function testUsePersistentPermissionsFirstParty() {
|
|||
) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
await checkGeolocation(browser, "frame", aExpect);
|
||||
|
||||
if (aExpect == PromptResult.PROMPT) {
|
||||
await waitForPrompt;
|
||||
// Notification is shown, check label and deny to clean
|
||||
let popuphidden = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
|
@ -153,3 +184,74 @@ add_task(async function testUsePersistentPermissionsFirstParty() {
|
|||
await checkPermission(Perms.ALLOW_ACTION, PromptResult.ALLOW);
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we should prompt if we are in unsafe permission delegation. The
|
||||
// prompt popup should include both first and third party origin.
|
||||
add_task(async function testPromptInMaybeUnsafePermissionDelegation() {
|
||||
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
|
||||
browser
|
||||
) {
|
||||
// Persistent allow top level origin
|
||||
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
|
||||
|
||||
await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT);
|
||||
await checkNotificationBothOrigins(uri.host, "example.org");
|
||||
|
||||
SitePermissions.removeFromPrincipal(null, "geo", browser);
|
||||
PermissionTestUtils.remove(uri, "geo");
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we should prompt if we are in unsafe permission delegation and
|
||||
// change location to origin which is not explicitly trusted. The prompt popup
|
||||
// should include both first and third party origin.
|
||||
add_task(async function testPromptChangeLocatioUnsafePermissionDelegation() {
|
||||
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
|
||||
browser
|
||||
) {
|
||||
// Persistent allow top level origin
|
||||
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
|
||||
|
||||
// Request change location.
|
||||
await ContentTask.spawn(browser, { host: uri.host }, async function(args) {
|
||||
let frame = content.document.getElementById("frameAllowsAll");
|
||||
await new Promise(resolve => {
|
||||
function listener() {
|
||||
frame.removeEventListener("load", listener, true);
|
||||
resolve();
|
||||
}
|
||||
frame.addEventListener("load", listener, true);
|
||||
|
||||
frame.contentWindow.location =
|
||||
"https://test1.example.com/browser/browser/base/content/test/permissions/permissions.html";
|
||||
});
|
||||
});
|
||||
|
||||
await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT);
|
||||
await checkNotificationBothOrigins(uri.host, "test1.example.com");
|
||||
|
||||
SitePermissions.removeFromPrincipal(null, "geo", browser);
|
||||
PermissionTestUtils.remove(uri, "geo");
|
||||
});
|
||||
});
|
||||
|
||||
// If we are in unsafe permission delegation and the origin is explicitly
|
||||
// trusted in ancestor chain. Do not need prompt
|
||||
add_task(async function testExplicitlyAllowedInChain() {
|
||||
await BrowserTestUtils.withNewTab(CROSS_FRAME_PAGE, async function(browser) {
|
||||
// Persistent allow top level origin
|
||||
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
|
||||
|
||||
const iframeAncestor = await SpecialPowers.spawn(browser, [], () => {
|
||||
return content.document.getElementById("frameAncestor").browsingContext;
|
||||
});
|
||||
|
||||
await checkGeolocation(
|
||||
iframeAncestor,
|
||||
"frameAllowsAll",
|
||||
PromptResult.ALLOW
|
||||
);
|
||||
|
||||
PermissionTestUtils.remove(uri, "geo");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,8 +21,9 @@ function requestPush() {
|
|||
function requestGeo() {
|
||||
return navigator.geolocation.getCurrentPosition(() => {
|
||||
parent.postMessage("allow", "*");
|
||||
}, () => {
|
||||
parent.postMessage("deny", "*");
|
||||
}, error => {
|
||||
// PERMISSION_DENIED = 1
|
||||
parent.postMessage(error.code == 1 ? "deny" : "allow", "*");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Permissions Subframe Test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frameAncestor"
|
||||
src="https://test1.example.com/browser/browser/base/content/test/permissions/temporary_permissions_subframe.html"
|
||||
allow="geolocation 'src' https://example.org"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -5,6 +5,7 @@
|
|||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frame" src="https://example.org/browser/browser/base/content/test/permissions/permissions.html" allow="geolocation"/>
|
||||
<iframe id="frame" src="https://example.org/browser/browser/base/content/test/permissions/permissions.html" allow="geolocation"></iframe>
|
||||
<iframe id="frameAllowsAll" src="https://example.org/browser/browser/base/content/test/permissions/permissions.html" allow="geolocation *"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -623,6 +623,9 @@ geolocation.dontAllowLocation=Don’t Allow
|
|||
geolocation.dontAllowLocation.accesskey=n
|
||||
geolocation.shareWithSite3=Will you allow %S to access your location?
|
||||
geolocation.shareWithFile3=Will you allow this local file to access your location?
|
||||
# LOCALIZATION NOTE(geolocation.shareWithSiteUnsafeDelegation):
|
||||
# %1$S is the first party origin, %2$S is the third party origin.
|
||||
geolocation.shareWithSiteUnsafeDelegation=Will you allow %1$S to give %2$S permission to access your location?
|
||||
geolocation.remember=Remember this decision
|
||||
|
||||
# Persistent storage UI
|
||||
|
|
|
@ -148,20 +148,6 @@ var PermissionPromptPrototype = {
|
|||
throw new Error("Not implemented.");
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides the preferred name to use in the permission popups,
|
||||
* based on the principal URI (the URI.hostPort for any URI scheme
|
||||
* besides the moz-extension one which should default to the
|
||||
* extension name).
|
||||
*/
|
||||
get principalName() {
|
||||
if (this.principal.addonPolicy) {
|
||||
return this.principal.addonPolicy.name;
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -283,6 +269,20 @@ var PermissionPromptPrototype = {
|
|||
throw new Error("Not implemented.");
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides the preferred name to use in the permission popups,
|
||||
* based on the principal URI (the URI.hostPort for any URI scheme
|
||||
* besides the moz-extension one which should default to the
|
||||
* extension name).
|
||||
*/
|
||||
getPrincipalName(principal = this.principal) {
|
||||
if (principal.addonPolicy) {
|
||||
return principal.addonPolicy.name;
|
||||
}
|
||||
|
||||
return principal.URI.hostPort;
|
||||
},
|
||||
|
||||
/**
|
||||
* This will be called if the request is to be cancelled.
|
||||
*
|
||||
|
@ -421,7 +421,10 @@ var PermissionPromptPrototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (state == SitePermissions.ALLOW) {
|
||||
if (
|
||||
state == SitePermissions.ALLOW &&
|
||||
!this.request.maybeUnsafePermissionDelegate
|
||||
) {
|
||||
this.allow();
|
||||
return;
|
||||
}
|
||||
|
@ -719,11 +722,8 @@ 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;
|
||||
let request = this.request.QueryInterface(Ci.nsIContentPermissionRequest);
|
||||
return request.getDelegatePrincipal(this.type);
|
||||
},
|
||||
|
||||
cancel() {
|
||||
|
@ -768,7 +768,7 @@ GeolocationPermissionPrompt.prototype = {
|
|||
let options = {
|
||||
learnMoreURL: Services.urlFormatter.formatURLPref(pref),
|
||||
displayURI: false,
|
||||
name: this.principalName,
|
||||
name: this.getPrincipalName(),
|
||||
};
|
||||
|
||||
if (this.principal.schemeIs("file")) {
|
||||
|
@ -780,6 +780,12 @@ GeolocationPermissionPrompt.prototype = {
|
|||
};
|
||||
}
|
||||
|
||||
if (this.request.maybeUnsafePermissionDelegate) {
|
||||
// Second name should be the third party origin
|
||||
options.secondName = this.getPrincipalName(this.request.principal);
|
||||
options.checkbox = { show: false };
|
||||
}
|
||||
|
||||
if (options.checkbox.show) {
|
||||
options.checkbox.label = gBrowserBundle.GetStringFromName(
|
||||
"geolocation.remember"
|
||||
|
@ -802,6 +808,13 @@ GeolocationPermissionPrompt.prototype = {
|
|||
return gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
|
||||
}
|
||||
|
||||
if (this.request.maybeUnsafePermissionDelegate) {
|
||||
return gBrowserBundle.formatStringFromName(
|
||||
"geolocation.shareWithSiteUnsafeDelegation",
|
||||
["<>", "{}"]
|
||||
);
|
||||
}
|
||||
|
||||
return gBrowserBundle.formatStringFromName("geolocation.shareWithSite3", [
|
||||
"<>",
|
||||
]);
|
||||
|
@ -923,7 +936,7 @@ DesktopNotificationPermissionPrompt.prototype = {
|
|||
return {
|
||||
learnMoreURL,
|
||||
displayURI: false,
|
||||
name: this.principalName,
|
||||
name: this.getPrincipalName(),
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1026,7 +1039,7 @@ PersistentStoragePermissionPrompt.prototype = {
|
|||
return {
|
||||
learnMoreURL,
|
||||
displayURI: false,
|
||||
name: this.principalName,
|
||||
name: this.getPrincipalName(),
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1115,7 +1128,7 @@ MIDIPermissionPrompt.prototype = {
|
|||
// TODO (bug 1433235) We need a security/permissions explanation URL for this
|
||||
let options = {
|
||||
displayURI: false,
|
||||
name: this.principalName,
|
||||
name: this.getPrincipalName(),
|
||||
};
|
||||
|
||||
if (this.principal.schemeIs("file")) {
|
||||
|
|
|
@ -8337,6 +8337,10 @@ void Document::SetPrototypeDocument(nsXULPrototypeDocument* aPrototype) {
|
|||
mSynchronousDOMContentLoaded = true;
|
||||
}
|
||||
|
||||
nsIPermissionDelegateHandler* Document::PermDelegateHandler() {
|
||||
return GetPermissionDelegateHandler();
|
||||
}
|
||||
|
||||
Document* Document::RequestExternalResource(
|
||||
nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
|
||||
ExternalResourceLoad** aPendingLoad) {
|
||||
|
|
|
@ -134,6 +134,7 @@ class nsIAppWindow;
|
|||
class nsXULPrototypeDocument;
|
||||
class nsXULPrototypeElement;
|
||||
class PermissionDelegateHandler;
|
||||
class nsIPermissionDelegateHandler;
|
||||
struct nsFont;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -4187,6 +4188,8 @@ class Document : public nsINode,
|
|||
|
||||
void SetPrototypeDocument(nsXULPrototypeDocument* aPrototype);
|
||||
|
||||
nsIPermissionDelegateHandler* PermDelegateHandler();
|
||||
|
||||
// Returns true if we use overlay scrollbars on the system wide or on the
|
||||
// given document.
|
||||
static bool UseOverlayScrollbars(const Document* aDocument);
|
||||
|
|
|
@ -124,6 +124,7 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aIsHandlingUserInput,
|
||||
const bool aMaybeUnsafePermissionDelegate,
|
||||
const bool aUserHadInteractedWithDocument,
|
||||
const DOMTimeStamp aDocumentDOMContentLoadedTimestamp);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
|
@ -134,6 +135,7 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
|
||||
nsCOMPtr<Element> mElement;
|
||||
bool mIsHandlingUserInput;
|
||||
bool mMaybeUnsafePermissionDelegate;
|
||||
bool mUserHadInteractedWithDocument;
|
||||
DOMTimeStamp mDocumentDOMContentLoadedTimestamp;
|
||||
RefPtr<nsContentPermissionRequestProxy> mProxy;
|
||||
|
@ -152,7 +154,8 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
ContentPermissionRequestParent::ContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aIsHandlingUserInput, const bool aUserHadInteractedWithDocument,
|
||||
const bool aIsHandlingUserInput, const bool aMaybeUnsafePermissionDelegate,
|
||||
const bool aUserHadInteractedWithDocument,
|
||||
const DOMTimeStamp aDocumentDOMContentLoadedTimestamp) {
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
||||
|
@ -161,6 +164,7 @@ ContentPermissionRequestParent::ContentPermissionRequestParent(
|
|||
mElement = aElement;
|
||||
mRequests = aRequests;
|
||||
mIsHandlingUserInput = aIsHandlingUserInput;
|
||||
mMaybeUnsafePermissionDelegate = aMaybeUnsafePermissionDelegate;
|
||||
mUserHadInteractedWithDocument = aUserHadInteractedWithDocument;
|
||||
mDocumentDOMContentLoadedTimestamp = aDocumentDOMContentLoadedTimestamp;
|
||||
}
|
||||
|
@ -329,12 +333,14 @@ PContentPermissionRequestParent*
|
|||
nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aIsHandlingUserInput, const bool aUserHadInteractedWithDocument,
|
||||
const bool aIsHandlingUserInput, const bool aMaybeUnsafePermissionDelegate,
|
||||
const bool aUserHadInteractedWithDocument,
|
||||
const DOMTimeStamp aDocumentDOMContentLoadedTimestamp,
|
||||
const TabId& aTabId) {
|
||||
PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
|
||||
aRequests, aElement, aPrincipal, aTopLevelPrincipal, aIsHandlingUserInput,
|
||||
aUserHadInteractedWithDocument, aDocumentDOMContentLoadedTimestamp);
|
||||
aMaybeUnsafePermissionDelegate, aUserHadInteractedWithDocument,
|
||||
aDocumentDOMContentLoadedTimestamp);
|
||||
ContentPermissionRequestParentMap()[parent] = aTabId;
|
||||
|
||||
return parent;
|
||||
|
@ -374,6 +380,11 @@ nsresult nsContentPermissionUtils::AskPermission(
|
|||
rv = aRequest->GetIsHandlingUserInput(&isHandlingUserInput);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool maybeUnsafePermissionDelegate;
|
||||
rv = aRequest->GetMaybeUnsafePermissionDelegate(
|
||||
&maybeUnsafePermissionDelegate);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool userHadInteractedWithDocument;
|
||||
rv = aRequest->GetUserHadInteractedWithDocument(
|
||||
&userHadInteractedWithDocument);
|
||||
|
@ -391,8 +402,8 @@ nsresult nsContentPermissionUtils::AskPermission(
|
|||
ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
|
||||
req, permArray, IPC::Principal(principal),
|
||||
IPC::Principal(topLevelPrincipal), isHandlingUserInput,
|
||||
userHadInteractedWithDocument, documentDOMContentLoadedTimestamp,
|
||||
child->GetTabId());
|
||||
maybeUnsafePermissionDelegate, userHadInteractedWithDocument,
|
||||
documentDOMContentLoadedTimestamp, child->GetTabId());
|
||||
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
|
||||
|
||||
req->Sendprompt();
|
||||
|
@ -553,6 +564,7 @@ ContentPermissionRequestBase::ContentPermissionRequestBase(
|
|||
mType(aType),
|
||||
mIsHandlingUserInput(UserActivation::IsHandlingUserInput()),
|
||||
mUserHadInteractedWithDocument(false),
|
||||
mMaybeUnsafePermissionDelegate(false),
|
||||
mDocumentDOMContentLoadedTimestamp(0) {
|
||||
if (!aWindow) {
|
||||
return;
|
||||
|
@ -564,6 +576,12 @@ ContentPermissionRequestBase::ContentPermissionRequestBase(
|
|||
}
|
||||
|
||||
mPermissionHandler = doc->GetPermissionDelegateHandler();
|
||||
if (mPermissionHandler) {
|
||||
nsTArray<nsCString> types;
|
||||
types.AppendElement(mType);
|
||||
mPermissionHandler->MaybeUnsafePermissionDelegate(
|
||||
types, &mMaybeUnsafePermissionDelegate);
|
||||
}
|
||||
|
||||
mUserHadInteractedWithDocument = doc->UserHasInteracted();
|
||||
|
||||
|
@ -588,6 +606,13 @@ ContentPermissionRequestBase::GetDelegatePrincipal(
|
|||
aRequestingPrincipal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionRequestBase::GetMaybeUnsafePermissionDelegate(
|
||||
bool* aMaybeUnsafePermissionDelegate) {
|
||||
*aMaybeUnsafePermissionDelegate = mMaybeUnsafePermissionDelegate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionRequestBase::GetTopLevelPrincipal(
|
||||
nsIPrincipal** aRequestingPrincipal) {
|
||||
|
@ -967,6 +992,17 @@ nsContentPermissionRequestProxy::GetIsHandlingUserInput(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetMaybeUnsafePermissionDelegate(
|
||||
bool* aMaybeUnsafePermissionDelegate) {
|
||||
NS_ENSURE_ARG_POINTER(aMaybeUnsafePermissionDelegate);
|
||||
if (mParent == nullptr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
*aMaybeUnsafePermissionDelegate = mParent->mMaybeUnsafePermissionDelegate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetUserHadInteractedWithDocument(
|
||||
bool* aUserHadInteractedWithDocument) {
|
||||
|
|
|
@ -73,6 +73,7 @@ class nsContentPermissionUtils {
|
|||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aIsHandlingUserInput,
|
||||
const bool aMaybeUnsafePermissionDelegate,
|
||||
const bool aUserHadInteractedWithDocument,
|
||||
const DOMTimeStamp aDocumentDOMContentLoadedTimestamp,
|
||||
const TabId& aTabId);
|
||||
|
@ -126,6 +127,8 @@ class ContentPermissionRequestBase : public nsIContentPermissionRequest {
|
|||
NS_IMETHOD GetWindow(mozIDOMWindow** aWindow) override;
|
||||
NS_IMETHOD GetElement(mozilla::dom::Element** aElement) override;
|
||||
NS_IMETHOD GetIsHandlingUserInput(bool* aIsHandlingUserInput) override;
|
||||
NS_IMETHOD GetMaybeUnsafePermissionDelegate(
|
||||
bool* aMaybeUnsafePermissionDelegate) override;
|
||||
NS_IMETHOD GetUserHadInteractedWithDocument(
|
||||
bool* aUserHadInteractedWithDocument) override;
|
||||
NS_IMETHOD GetDocumentDOMContentLoadedTimestamp(
|
||||
|
@ -169,6 +172,7 @@ class ContentPermissionRequestBase : public nsIContentPermissionRequest {
|
|||
nsCString mType;
|
||||
bool mIsHandlingUserInput;
|
||||
bool mUserHadInteractedWithDocument;
|
||||
bool mMaybeUnsafePermissionDelegate;
|
||||
DOMTimeStamp mDocumentDOMContentLoadedTimestamp;
|
||||
};
|
||||
|
||||
|
|
|
@ -1858,3 +1858,6 @@ addExternalIface('XULTemplateRuleFilter', nativeType='nsIXULTemplateRuleFilter',
|
|||
notflattened=True)
|
||||
addExternalIface('nsISHistory', nativeType='nsISHistory', notflattened=True)
|
||||
addExternalIface('ReferrerInfo', nativeType='nsIReferrerInfo')
|
||||
addExternalIface('nsIPermissionDelegateHandler',
|
||||
nativeType='nsIPermissionDelegateHandler',
|
||||
notflattened=True)
|
||||
|
|
|
@ -21,6 +21,7 @@ XPIDL_SOURCES += [
|
|||
'nsIDOMWindow.idl',
|
||||
'nsIDOMWindowUtils.idl',
|
||||
'nsIFocusManager.idl',
|
||||
'nsIPermissionDelegateHandler.idl',
|
||||
'nsIQueryContentEventResult.idl',
|
||||
'nsIRemoteTab.idl',
|
||||
'nsIServiceWorkerManager.idl',
|
||||
|
|
|
@ -92,6 +92,7 @@ interface nsIContentPermissionRequest : nsISupports {
|
|||
readonly attribute boolean userHadInteractedWithDocument;
|
||||
readonly attribute DOMTimeStamp documentDOMContentLoadedTimestamp;
|
||||
|
||||
readonly attribute boolean maybeUnsafePermissionDelegate;
|
||||
/**
|
||||
* The requester to get the required information of
|
||||
* the window.
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This file contains an interface to the Permission Delegate Handler,
|
||||
*/
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPrincipal;
|
||||
|
||||
[scriptable, builtinclass, uuid(07611dc6-bf4d-4d8a-a64b-f3a5904dddc7)]
|
||||
interface nsIPermissionDelegateHandler : nsISupports
|
||||
{
|
||||
/*
|
||||
* Return true if we are delegating permission to a third party which is not
|
||||
* explicitly trusted. An orgin is not explicitly trusted means it is not
|
||||
* presented in the Feature Policy ancestor chain, via src, explicitly listed
|
||||
* in allow, and it is not the top-level origin.
|
||||
*
|
||||
* @param aTypes the permission types to check
|
||||
*/
|
||||
boolean maybeUnsafePermissionDelegate(in Array<ACString> aTypes);
|
||||
};
|
||||
|
|
@ -3151,8 +3151,10 @@ PContentPermissionRequestChild*
|
|||
ContentChild::AllocPContentPermissionRequestChild(
|
||||
const nsTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
|
||||
const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
|
||||
const DOMTimeStamp aPageLoadTimestamp, const TabId& aTabId) {
|
||||
const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate,
|
||||
const bool& aDocumentHasUserInput, const DOMTimeStamp aPageLoadTimestamp,
|
||||
const TabId& aTabId) {
|
||||
MOZ_CRASH("unused");
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -528,8 +528,10 @@ class ContentChild final
|
|||
const nsTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const IPC::Principal& aTopLevelPrincipal,
|
||||
const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
|
||||
const DOMTimeStamp aPageLoadTimestamp, const TabId& aTabId);
|
||||
const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate,
|
||||
const bool& aDocumentHasUserInput, const DOMTimeStamp aPageLoadTimestamp,
|
||||
const TabId& aTabId);
|
||||
bool DeallocPContentPermissionRequestChild(
|
||||
PContentPermissionRequestChild* actor);
|
||||
|
||||
|
|
|
@ -4613,8 +4613,10 @@ PContentPermissionRequestParent*
|
|||
ContentParent::AllocPContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
|
||||
const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
|
||||
const DOMTimeStamp& aPageLoadTimestamp, const TabId& aTabId) {
|
||||
const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate,
|
||||
const bool& aDocumentHasUserInput, const DOMTimeStamp& aPageLoadTimestamp,
|
||||
const TabId& aTabId) {
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
RefPtr<BrowserParent> tp =
|
||||
cpm->GetTopLevelBrowserParentByProcessAndTabId(this->ChildID(), aTabId);
|
||||
|
@ -4629,7 +4631,8 @@ ContentParent::AllocPContentPermissionRequestParent(
|
|||
}
|
||||
return nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
|
||||
aIsHandlingUserInput, aDocumentHasUserInput, aPageLoadTimestamp, aTabId);
|
||||
aIsHandlingUserInput, aMaybeUnsafePermissionDelegate,
|
||||
aDocumentHasUserInput, aPageLoadTimestamp, aTabId);
|
||||
}
|
||||
|
||||
bool ContentParent::DeallocPContentPermissionRequestParent(
|
||||
|
|
|
@ -509,8 +509,10 @@ class ContentParent final
|
|||
const nsTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const IPC::Principal& aTopLevelPrincipal,
|
||||
const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
|
||||
const DOMTimeStamp& aPageLoadTimestamp, const TabId& aTabId);
|
||||
const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate,
|
||||
const bool& aDocumentHasUserInput, const DOMTimeStamp& aPageLoadTimestamp,
|
||||
const TabId& aTabId);
|
||||
|
||||
bool DeallocPContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* actor);
|
||||
|
|
|
@ -1168,9 +1168,13 @@ parent:
|
|||
* principals that can live in the content process should
|
||||
* provided.
|
||||
*/
|
||||
async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
|
||||
Principal aTopLevelPrincipal, bool aIsHandlingUserInput,
|
||||
bool aDocumentHasUserInput, uint64_t aPageLoadTimestamp, TabId tabId);
|
||||
async PContentPermissionRequest(PermissionRequest[] aRequests,
|
||||
Principal aPrincipal,
|
||||
Principal aTopLevelPrincipal,
|
||||
bool aIsHandlingUserInput,
|
||||
bool aMaybeUnsafePermissionDelegate,
|
||||
bool aDocumentHasUserInput,
|
||||
uint64_t aPageLoadTimestamp, TabId tabId);
|
||||
|
||||
async ShutdownProfile(nsCString aProfile);
|
||||
|
||||
|
|
|
@ -32,9 +32,20 @@ void FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy) {
|
|||
|
||||
RefPtr<FeaturePolicy> dest = this;
|
||||
RefPtr<FeaturePolicy> src = aParentPolicy;
|
||||
|
||||
// Inherit origins which explicitly declared policy in chain
|
||||
for (const Feature& featureInChain :
|
||||
aParentPolicy->mDeclaredFeaturesInAncestorChain) {
|
||||
dest->AppendToDeclaredAllowInAncestorChain(featureInChain);
|
||||
}
|
||||
|
||||
FeaturePolicyUtils::ForEachFeature([dest, src](const char* aFeatureName) {
|
||||
nsString featureName;
|
||||
featureName.AppendASCII(aFeatureName);
|
||||
// Store unsafe allows all (allow=*)
|
||||
if (src->HasFeatureUnsafeAllowsAll(featureName)) {
|
||||
dest->mParentAllowedAllFeatures.AppendElement(featureName);
|
||||
}
|
||||
|
||||
// If the destination has a declared feature (via the HTTP header or 'allow'
|
||||
// attribute) we allow the feature if the destination allows it and the
|
||||
|
@ -76,6 +87,38 @@ bool FeaturePolicy::HasDeclaredFeature(const nsAString& aFeatureName) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool FeaturePolicy::HasFeatureUnsafeAllowsAll(
|
||||
const nsAString& aFeatureName) const {
|
||||
for (const Feature& feature : mFeatures) {
|
||||
if (feature.AllowsAll() && feature.Name().Equals(aFeatureName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We should look into parent too (for example, document of iframe which
|
||||
// allows all, would be unsafe)
|
||||
return mParentAllowedAllFeatures.Contains(aFeatureName);
|
||||
}
|
||||
|
||||
void FeaturePolicy::AppendToDeclaredAllowInAncestorChain(
|
||||
const Feature& aFeature) {
|
||||
for (Feature& featureInChain : mDeclaredFeaturesInAncestorChain) {
|
||||
if (featureInChain.Name().Equals(aFeature.Name())) {
|
||||
MOZ_ASSERT(featureInChain.HasAllowList());
|
||||
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>> list;
|
||||
aFeature.GetAllowList(list);
|
||||
|
||||
for (nsIPrincipal* principal : list) {
|
||||
featureInChain.AppendToAllowList(principal);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
mDeclaredFeaturesInAncestorChain.AppendElement(aFeature);
|
||||
}
|
||||
|
||||
void FeaturePolicy::SetDeclaredPolicy(Document* aDocument,
|
||||
const nsAString& aPolicyString,
|
||||
nsIPrincipal* aSelfOrigin,
|
||||
|
@ -88,6 +131,13 @@ void FeaturePolicy::SetDeclaredPolicy(Document* aDocument,
|
|||
|
||||
Unused << NS_WARN_IF(!FeaturePolicyParser::ParseString(
|
||||
aPolicyString, aDocument, aSelfOrigin, aSrcOrigin, mFeatures));
|
||||
|
||||
// Only store explicitly declared allowlist
|
||||
for (const Feature& feature : mFeatures) {
|
||||
if (feature.HasAllowList()) {
|
||||
AppendToDeclaredAllowInAncestorChain(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeaturePolicy::ResetDeclaredPolicy() {
|
||||
|
@ -95,6 +145,7 @@ void FeaturePolicy::ResetDeclaredPolicy() {
|
|||
mDeclaredString.Truncate();
|
||||
mSelfOrigin = nullptr;
|
||||
mSrcOrigin = nullptr;
|
||||
mDeclaredFeaturesInAncestorChain.Clear();
|
||||
}
|
||||
|
||||
JSObject* FeaturePolicy::WrapObject(JSContext* aCx,
|
||||
|
@ -124,6 +175,19 @@ bool FeaturePolicy::AllowsFeature(const nsAString& aFeatureName,
|
|||
return AllowsFeatureInternal(aFeatureName, origin);
|
||||
}
|
||||
|
||||
bool FeaturePolicy::AllowsFeatureExplicitlyInAncestorChain(
|
||||
const nsAString& aFeatureName, nsIPrincipal* aOrigin) const {
|
||||
MOZ_ASSERT(aOrigin);
|
||||
|
||||
for (const Feature& feature : mDeclaredFeaturesInAncestorChain) {
|
||||
if (feature.Name().Equals(aFeatureName)) {
|
||||
return feature.AllowListContains(aOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FeaturePolicy::AllowsFeatureInternal(const nsAString& aFeatureName,
|
||||
nsIPrincipal* aOrigin) const {
|
||||
MOZ_ASSERT(aOrigin);
|
||||
|
|
|
@ -101,6 +101,20 @@ class FeaturePolicy final : public nsISupports, public nsWrapperCache {
|
|||
// policy.
|
||||
void ResetDeclaredPolicy();
|
||||
|
||||
// This method appends a feature to in-chain declared allowlist. If the name's
|
||||
// feature existed in the list, we only need to append the allowlist of new
|
||||
// feature to the existed one.
|
||||
void AppendToDeclaredAllowInAncestorChain(const Feature& aFeature);
|
||||
|
||||
// This method returns true if aFeatureName is declared as "*" (allow all)
|
||||
// in parent.
|
||||
bool HasFeatureUnsafeAllowsAll(const nsAString& aFeatureName) const;
|
||||
|
||||
// This method returns true if the aFeatureName is allowed for aOrigin
|
||||
// explicitly in ancestor chain,
|
||||
bool AllowsFeatureExplicitlyInAncestorChain(const nsAString& aFeatureName,
|
||||
nsIPrincipal* aOrigin) const;
|
||||
|
||||
// WebIDL internal methods.
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
|
@ -159,6 +173,14 @@ class FeaturePolicy final : public nsISupports, public nsWrapperCache {
|
|||
// current context.
|
||||
nsTArray<nsString> mInheritedDeniedFeatureNames;
|
||||
|
||||
// This is set of feature names when the parent allows all for that feature.
|
||||
nsTArray<nsString> mParentAllowedAllFeatures;
|
||||
|
||||
// The explicitly declared policy contains allowlist as a set of origins
|
||||
// except 'none' and '*'. This set contains all explicitly declared policies
|
||||
// in ancestor chain
|
||||
nsTArray<Feature> mDeclaredFeaturesInAncestorChain;
|
||||
|
||||
// Feature policy for the current context.
|
||||
nsTArray<Feature> mFeatures;
|
||||
|
||||
|
|
|
@ -123,6 +123,51 @@ FeaturePolicyUtils::DefaultAllowListFeature(const nsAString& aFeatureName) {
|
|||
return FeaturePolicyValue::eNone;
|
||||
}
|
||||
|
||||
static bool IsSameOriginAsTop(Document* aDocument) {
|
||||
MOZ_ASSERT(aDocument);
|
||||
|
||||
BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
|
||||
if (!browsingContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* topWindow = browsingContext->Top()->GetDOMWindow();
|
||||
if (!topWindow) {
|
||||
// If we don't have a DOMWindow, We are not in same origin.
|
||||
return false;
|
||||
}
|
||||
|
||||
Document* topLevelDocument = topWindow->GetExtantDoc();
|
||||
if (!topLevelDocument) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(
|
||||
nsContentUtils::CheckSameOrigin(topLevelDocument, aDocument));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool FeaturePolicyUtils::IsFeatureUnsafeAllowedAll(
|
||||
Document* aDocument, const nsAString& aFeatureName) {
|
||||
MOZ_ASSERT(aDocument);
|
||||
|
||||
if (!StaticPrefs::dom_security_featurePolicy_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aDocument->IsHTMLDocument()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FeaturePolicy* policy = aDocument->FeaturePolicy();
|
||||
MOZ_ASSERT(policy);
|
||||
|
||||
return policy->HasFeatureUnsafeAllowsAll(aFeatureName) &&
|
||||
!policy->AllowsFeatureExplicitlyInAncestorChain(
|
||||
aFeatureName, policy->DefaultOrigin()) &&
|
||||
!IsSameOriginAsTop(aDocument);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool FeaturePolicyUtils::IsFeatureAllowed(Document* aDocument,
|
||||
const nsAString& aFeatureName) {
|
||||
|
|
|
@ -49,6 +49,13 @@ class FeaturePolicyUtils final {
|
|||
static FeaturePolicyValue DefaultAllowListFeature(
|
||||
const nsAString& aFeatureName);
|
||||
|
||||
// This method returns true if aFeatureName is in unsafe allowed "*" case.
|
||||
// We are in "unsafe" case when there is 'allow "*"' presents for an origin
|
||||
// that's not presented in the ancestor feature policy chain, via src, via
|
||||
// explicitly listed in allow, and not being the top-level origin.
|
||||
static bool IsFeatureUnsafeAllowedAll(Document* aDocument,
|
||||
const nsAString& aFeatureName);
|
||||
|
||||
private:
|
||||
static void ReportViolation(Document* aDocument,
|
||||
const nsAString& aFeatureName);
|
||||
|
|
|
@ -23,6 +23,7 @@ interface URI;
|
|||
interface nsIDocShell;
|
||||
interface nsILoadGroup;
|
||||
interface nsIReferrerInfo;
|
||||
interface nsIPermissionDelegateHandler;
|
||||
interface XULCommandDispatcher;
|
||||
|
||||
enum VisibilityState { "hidden", "visible" };
|
||||
|
@ -676,3 +677,9 @@ partial interface Document {
|
|||
[ChromeOnly, BinaryName="setUserHasInteracted"]
|
||||
void userInteractionForTesting();
|
||||
};
|
||||
|
||||
// Extension for permission delegation.
|
||||
partial interface Document {
|
||||
[Pref="permissions.delegation.enabled", ChromeOnly, Pure]
|
||||
readonly attribute nsIPermissionDelegateHandler permDelegateHandler;
|
||||
};
|
||||
|
|
|
@ -44,6 +44,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(PermissionDelegateHandler)
|
|||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PermissionDelegateHandler)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PermissionDelegateHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPermissionDelegateHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
@ -67,6 +68,31 @@ const DelegateInfo* PermissionDelegateHandler::GetPermissionDelegateInfo(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PermissionDelegateHandler::MaybeUnsafePermissionDelegate(
|
||||
const nsTArray<nsCString>& aTypes, bool* aMaybeUnsafe) {
|
||||
*aMaybeUnsafe = false;
|
||||
if (!StaticPrefs::permissions_delegation_enabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
for (auto& type : aTypes) {
|
||||
const DelegateInfo* info =
|
||||
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(type));
|
||||
if (!info) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString featureName(info->mFeatureName);
|
||||
if (FeaturePolicyUtils::IsFeatureUnsafeAllowedAll(mDocument, featureName)) {
|
||||
*aMaybeUnsafe = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult PermissionDelegateHandler::GetDelegatePrincipal(
|
||||
const nsACString& aType, nsIContentPermissionRequest* aRequest,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#define PermissionDelegateHandler_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsIPermissionDelegateHandler.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsIContentPermissionRequest;
|
||||
|
@ -37,13 +38,16 @@ class Document;
|
|||
}
|
||||
} // namespace mozilla
|
||||
|
||||
class PermissionDelegateHandler final : nsISupports {
|
||||
class PermissionDelegateHandler final : public nsIPermissionDelegateHandler {
|
||||
public:
|
||||
explicit PermissionDelegateHandler(mozilla::dom::Document* aDocument);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(PermissionDelegateHandler)
|
||||
|
||||
NS_DECL_NSIPERMISSIONDELEGATEHANDLER
|
||||
|
||||
explicit PermissionDelegateHandler() = default;
|
||||
explicit PermissionDelegateHandler(mozilla::dom::Document* aDocument);
|
||||
|
||||
bool Initialize();
|
||||
|
||||
/*
|
||||
|
@ -139,7 +143,7 @@ class PermissionDelegateHandler final : nsISupports {
|
|||
nsIPrincipal** aResult);
|
||||
|
||||
private:
|
||||
virtual ~PermissionDelegateHandler() = default;
|
||||
~PermissionDelegateHandler() = default;
|
||||
|
||||
/*
|
||||
* Check whether the permission is blocked by FeaturePolicy directive.
|
||||
|
|
|
@ -21,4 +21,10 @@ Classes = [
|
|||
'constructor': 'nsPermissionManager::GetXPCOMSingleton',
|
||||
'headers': ['/extensions/permissions/nsPermissionManager.h'],
|
||||
},
|
||||
{
|
||||
'cid': '{07611dc6-bf4d-4d8a-a64b-f3a5904dddc7}',
|
||||
'contract_ids': ['@mozilla.org/permissiondelegatehandler;1'],
|
||||
'type': 'PermissionDelegateHandler',
|
||||
'headers': ['/extensions/permissions/PermissionDelegateHandler.h'],
|
||||
},
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче