зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1490811 - Part 1: Add a permission doorhanger for the storage access API r=baku,johannh
Differential Revision: https://phabricator.services.mozilla.com/D12467 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
508e50ac02
Коммит
cc714b7adc
|
@ -966,7 +966,15 @@ var gIdentityHandler = {
|
||||||
|
|
||||||
let hasBlockedPopupIndicator = false;
|
let hasBlockedPopupIndicator = false;
|
||||||
for (let permission of permissions) {
|
for (let permission of permissions) {
|
||||||
|
if (permission.id == "storage-access") {
|
||||||
|
// Ignore storage access permissions here, they are made visible inside
|
||||||
|
// the Content Blocking UI.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let item = this._createPermissionItem(permission);
|
let item = this._createPermissionItem(permission);
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
this._permissionList.appendChild(item);
|
this._permissionList.appendChild(item);
|
||||||
|
|
||||||
if (permission.id == "popup" &&
|
if (permission.id == "popup" &&
|
||||||
|
@ -1035,7 +1043,11 @@ var gIdentityHandler = {
|
||||||
let nameLabel = document.createXULElement("label");
|
let nameLabel = document.createXULElement("label");
|
||||||
nameLabel.setAttribute("flex", "1");
|
nameLabel.setAttribute("flex", "1");
|
||||||
nameLabel.setAttribute("class", "identity-popup-permission-label");
|
nameLabel.setAttribute("class", "identity-popup-permission-label");
|
||||||
nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
|
let label = SitePermissions.getPermissionLabel(aPermission.id);
|
||||||
|
if (label === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
nameLabel.textContent = label;
|
||||||
let nameLabelId = "identity-popup-permission-label-" + aPermission.id;
|
let nameLabelId = "identity-popup-permission-label-" + aPermission.id;
|
||||||
nameLabel.setAttribute("id", nameLabelId);
|
nameLabel.setAttribute("id", nameLabelId);
|
||||||
|
|
||||||
|
|
|
@ -977,6 +977,8 @@ xmlns="http://www.w3.org/1999/xhtml"
|
||||||
tooltiptext="&urlbar.midiNotificationAnchor.tooltip;"/>
|
tooltiptext="&urlbar.midiNotificationAnchor.tooltip;"/>
|
||||||
<image id="webauthn-notification-icon" class="notification-anchor-icon" role="button"
|
<image id="webauthn-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
tooltiptext="&urlbar.webAuthnAnchor.tooltip;"/>
|
tooltiptext="&urlbar.webAuthnAnchor.tooltip;"/>
|
||||||
|
<image id="storage-access-notification-icon" class="notification-anchor-icon storage-access-icon" role="button"
|
||||||
|
tooltiptext="&urlbar.storageAccessAnchor.tooltip;"/>
|
||||||
</box>
|
</box>
|
||||||
<image id="connection-icon"/>
|
<image id="connection-icon"/>
|
||||||
<image id="extension-icon"/>
|
<image id="extension-icon"/>
|
||||||
|
|
|
@ -12,11 +12,13 @@ var gPermPrincipal;
|
||||||
var gUsageRequest;
|
var gUsageRequest;
|
||||||
|
|
||||||
// Array of permissionIDs sorted alphabetically by label.
|
// Array of permissionIDs sorted alphabetically by label.
|
||||||
var gPermissions = SitePermissions.listPermissions().sort((a, b) => {
|
var gPermissions = SitePermissions.listPermissions()
|
||||||
|
.filter(p => SitePermissions.getPermissionLabel(p) != null)
|
||||||
|
.sort((a, b) => {
|
||||||
let firstLabel = SitePermissions.getPermissionLabel(a);
|
let firstLabel = SitePermissions.getPermissionLabel(a);
|
||||||
let secondLabel = SitePermissions.getPermissionLabel(b);
|
let secondLabel = SitePermissions.getPermissionLabel(b);
|
||||||
return firstLabel.localeCompare(secondLabel);
|
return firstLabel.localeCompare(secondLabel);
|
||||||
});
|
});
|
||||||
|
|
||||||
var permissionObserver = {
|
var permissionObserver = {
|
||||||
observe(aSubject, aTopic, aData) {
|
observe(aSubject, aTopic, aData) {
|
||||||
|
|
|
@ -103,3 +103,18 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
</popupnotificationfooter>
|
</popupnotificationfooter>
|
||||||
</popupnotification>
|
</popupnotification>
|
||||||
|
|
||||||
|
<popupnotification id="storage-access-notification" hidden="true">
|
||||||
|
<popupnotificationcontent class="storage-access-notification-content">
|
||||||
|
<xul:vbox flex="1">
|
||||||
|
<!-- These need to be on the same line to avoid creating
|
||||||
|
whitespace between them (whitespace is added in the
|
||||||
|
localization file, if necessary). -->
|
||||||
|
<xul:description class="storage-access-perm-text"><html:span
|
||||||
|
id="storage-access-perm-label"/><html:a id="storage-access-perm-learnmore"
|
||||||
|
onclick="openTrustedLinkIn(this.href, 'tab'); return false;"
|
||||||
|
class="text-link popup-notification-learnmore-link"/><html:span
|
||||||
|
id="storage-access-perm-endlabel"/></xul:description>
|
||||||
|
</xul:vbox>
|
||||||
|
</popupnotificationcontent>
|
||||||
|
</popupnotification>
|
||||||
|
|
|
@ -3135,6 +3135,9 @@ const ContentPermissionIntegration = {
|
||||||
case "autoplay-media": {
|
case "autoplay-media": {
|
||||||
return new PermissionUI.AutoplayPermissionPrompt(request);
|
return new PermissionUI.AutoplayPermissionPrompt(request);
|
||||||
}
|
}
|
||||||
|
case "storage-access": {
|
||||||
|
return new PermissionUI.StorageAccessPermissionPrompt(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
|
@ -282,6 +282,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||||
<!ENTITY urlbar.persistentStorageNotificationAnchor.tooltip "Store data in Persistent Storage">
|
<!ENTITY urlbar.persistentStorageNotificationAnchor.tooltip "Store data in Persistent Storage">
|
||||||
<!ENTITY urlbar.remoteControlNotificationAnchor.tooltip "Browser is under remote control">
|
<!ENTITY urlbar.remoteControlNotificationAnchor.tooltip "Browser is under remote control">
|
||||||
<!ENTITY urlbar.webAuthnAnchor.tooltip "Open Web Authentication panel">
|
<!ENTITY urlbar.webAuthnAnchor.tooltip "Open Web Authentication panel">
|
||||||
|
<!ENTITY urlbar.storageAccessAnchor.tooltip "Open browsing activity permission panel">
|
||||||
|
|
||||||
<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.tooltip "Manage sharing your camera and/or microphone with the site">
|
<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.tooltip "Manage sharing your camera and/or microphone with the site">
|
||||||
<!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.tooltip "Manage sharing your microphone with the site">
|
<!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.tooltip "Manage sharing your microphone with the site">
|
||||||
|
|
|
@ -965,6 +965,24 @@ autoplay.messageWithFile = Will you allow this file to autoplay media with sound
|
||||||
# popup panels, including the sliding subviews of the main menu.
|
# popup panels, including the sliding subviews of the main menu.
|
||||||
panel.back = Back
|
panel.back = Back
|
||||||
|
|
||||||
|
storageAccess.Allow.label = Allow Access
|
||||||
|
storageAccess.Allow.accesskey = A
|
||||||
|
storageAccess.AllowOnAnySite.label = Allow access on any site
|
||||||
|
storageAccess.AllowOnAnySite.accesskey = w
|
||||||
|
storageAccess.DontAllow.label = Block Access
|
||||||
|
storageAccess.DontAllow.accesskey = B
|
||||||
|
# LOCALIZATION NOTE (storageAccess.message):
|
||||||
|
# %1$S is the name of the site URL (www.site1.example) trying to track the user's activity.
|
||||||
|
# %2$S is the name of the site URL (www.site2.example) that the user is visiting. This is the same domain name displayed in the address bar.
|
||||||
|
storageAccess.message = Will you give %1$S access to track your browsing activity on %2$S?
|
||||||
|
# LOCALIZATION NOTE (storageAccess.description.label):
|
||||||
|
# %1$S is the name of the site URL (www.site1.example) trying to track the user's activity.
|
||||||
|
# %2$S will be replaced with the localized version of storageAccess.description.learnmore. This text will be converted into a hyper-link linking to the SUMO page explaining the concept of third-party trackers.
|
||||||
|
storageAccess.description.label = You may want to block %1$S on this site if you don’t recognize or trust it. Learn more about %2$S
|
||||||
|
# LOCALIZATION NOTE (storageAccess.description.learnmore):
|
||||||
|
# The value of this string is embedded inside storageAccess.description.label. See the localization note for storageAccess.description.label.
|
||||||
|
storageAccess.description.learnmore = third-party trackers
|
||||||
|
|
||||||
confirmationHint.sendToDevice.label = Sent!
|
confirmationHint.sendToDevice.label = Sent!
|
||||||
confirmationHint.sendToDeviceOffline.label = Queued (offline)
|
confirmationHint.sendToDeviceOffline.label = Queued (offline)
|
||||||
confirmationHint.copyURL.label = Copied to clipboard!
|
confirmationHint.copyURL.label = Copied to clipboard!
|
||||||
|
|
|
@ -257,8 +257,14 @@ var PermissionPromptPrototype = {
|
||||||
onBeforeShow() {},
|
onBeforeShow() {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the prompt was be shown to the user, this callback will
|
* If the prompt was shown to the user, this callback will be called just
|
||||||
* be called just after its been hidden.
|
* after it's been shown.
|
||||||
|
*/
|
||||||
|
onShown() {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the prompt was shown to the user, this callback will be called just
|
||||||
|
* after it's been hidden.
|
||||||
*/
|
*/
|
||||||
onAfterShow() {},
|
onAfterShow() {},
|
||||||
|
|
||||||
|
@ -423,6 +429,10 @@ var PermissionPromptPrototype = {
|
||||||
if (topic == "swapping") {
|
if (topic == "swapping") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// The prompt has been shown, notify the PermissionUI.
|
||||||
|
if (topic == "shown") {
|
||||||
|
this.onShown();
|
||||||
|
}
|
||||||
// The prompt has been removed, notify the PermissionUI.
|
// The prompt has been removed, notify the PermissionUI.
|
||||||
if (topic == "removed") {
|
if (topic == "removed") {
|
||||||
this.onAfterShow();
|
this.onAfterShow();
|
||||||
|
@ -472,8 +482,8 @@ var PermissionPromptForRequestPrototype = {
|
||||||
this.request.cancel();
|
this.request.cancel();
|
||||||
},
|
},
|
||||||
|
|
||||||
allow() {
|
allow(choices) {
|
||||||
this.request.allow();
|
this.request.allow(choices);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -905,3 +915,99 @@ AutoplayPermissionPrompt.prototype = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PermissionUI.AutoplayPermissionPrompt = AutoplayPermissionPrompt;
|
PermissionUI.AutoplayPermissionPrompt = AutoplayPermissionPrompt;
|
||||||
|
|
||||||
|
function StorageAccessPermissionPrompt(request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageAccessPermissionPrompt.prototype = {
|
||||||
|
__proto__: PermissionPromptForRequestPrototype,
|
||||||
|
|
||||||
|
get usePermissionManager() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get permissionKey() {
|
||||||
|
// Make sure this name is unique per each third-party tracker
|
||||||
|
return "storage-access-" + this.principal.origin;
|
||||||
|
},
|
||||||
|
|
||||||
|
get popupOptions() {
|
||||||
|
return {
|
||||||
|
displayURI: false,
|
||||||
|
name: this.principal.URI.hostPort,
|
||||||
|
secondName: this.topLevelPrincipal.URI.hostPort,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onShown() {
|
||||||
|
let document = this.browser.ownerDocument;
|
||||||
|
let label =
|
||||||
|
gBrowserBundle.formatStringFromName("storageAccess.description.label",
|
||||||
|
[this.request.principal.URI.hostPort, "<>"], 2);
|
||||||
|
let parts = label.split("<>");
|
||||||
|
if (parts.length == 1) {
|
||||||
|
parts.push("");
|
||||||
|
}
|
||||||
|
let map = {
|
||||||
|
"storage-access-perm-label": parts[0],
|
||||||
|
"storage-access-perm-learnmore":
|
||||||
|
gBrowserBundle.GetStringFromName("storageAccess.description.learnmore"),
|
||||||
|
"storage-access-perm-endlabel": parts[1],
|
||||||
|
};
|
||||||
|
for (let id in map) {
|
||||||
|
let str = map[id];
|
||||||
|
document.getElementById(id).textContent = str;
|
||||||
|
}
|
||||||
|
let learnMoreURL =
|
||||||
|
Services.urlFormatter.formatURLPref("app.support.baseURL") + "third-party-cookies";
|
||||||
|
document.getElementById("storage-access-perm-learnmore")
|
||||||
|
.href = learnMoreURL;
|
||||||
|
},
|
||||||
|
|
||||||
|
get notificationID() {
|
||||||
|
return "storage-access";
|
||||||
|
},
|
||||||
|
|
||||||
|
get anchorID() {
|
||||||
|
return "storage-access-notification-icon";
|
||||||
|
},
|
||||||
|
|
||||||
|
get message() {
|
||||||
|
return gBrowserBundle.formatStringFromName("storageAccess.message", ["<>", "<>"], 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
get promptActions() {
|
||||||
|
let self = this;
|
||||||
|
return [{
|
||||||
|
label: gBrowserBundle.GetStringFromName("storageAccess.DontAllow.label"),
|
||||||
|
accessKey: gBrowserBundle.GetStringFromName("storageAccess.DontAllow.accesskey"),
|
||||||
|
action: Ci.nsIPermissionManager.DENY_ACTION,
|
||||||
|
callback(state) {
|
||||||
|
self.cancel();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: gBrowserBundle.GetStringFromName("storageAccess.Allow.label"),
|
||||||
|
accessKey: gBrowserBundle.GetStringFromName("storageAccess.Allow.accesskey"),
|
||||||
|
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||||
|
callback(state) {
|
||||||
|
self.allow({"storage-access": "allow"});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: gBrowserBundle.GetStringFromName("storageAccess.AllowOnAnySite.label"),
|
||||||
|
accessKey: gBrowserBundle.GetStringFromName("storageAccess.AllowOnAnySite.accesskey"),
|
||||||
|
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||||
|
callback(state) {
|
||||||
|
self.allow({"storage-access": "allow-on-any-site"});
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
get topLevelPrincipal() {
|
||||||
|
return this.request.topLevelPrincipal;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
PermissionUI.StorageAccessPermissionPrompt = StorageAccessPermissionPrompt;
|
||||||
|
|
|
@ -355,7 +355,7 @@ var SitePermissions = {
|
||||||
* (e.g. SitePermissions.ALLOW)
|
* (e.g. SitePermissions.ALLOW)
|
||||||
* - scope: a constant representing how long the permission will
|
* - scope: a constant representing how long the permission will
|
||||||
* be kept.
|
* be kept.
|
||||||
* - label: the localized label
|
* - label: the localized label, or null if none is available.
|
||||||
*/
|
*/
|
||||||
getAllPermissionDetailsForBrowser(browser) {
|
getAllPermissionDetailsForBrowser(browser) {
|
||||||
return this.getAllForBrowser(browser).map(({id, scope, state}) =>
|
return this.getAllForBrowser(browser).map(({id, scope, state}) =>
|
||||||
|
@ -653,9 +653,18 @@ var SitePermissions = {
|
||||||
* @param {string} permissionID
|
* @param {string} permissionID
|
||||||
* The permission to get the label for.
|
* The permission to get the label for.
|
||||||
*
|
*
|
||||||
* @return {String} the localized label.
|
* @return {String} the localized label or null if none is available.
|
||||||
*/
|
*/
|
||||||
getPermissionLabel(permissionID) {
|
getPermissionLabel(permissionID) {
|
||||||
|
if (!(permissionID in gPermissionObject)) {
|
||||||
|
// Permission can't be found.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ("labelID" in gPermissionObject[permissionID] &&
|
||||||
|
gPermissionObject[permissionID].labelID === null) {
|
||||||
|
// Permission doesn't support having a label.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
let labelID = gPermissionObject[permissionID].labelID || permissionID;
|
let labelID = gPermissionObject[permissionID].labelID || permissionID;
|
||||||
return gStringBundle.GetStringFromName("permission." + labelID + ".label");
|
return gStringBundle.GetStringFromName("permission." + labelID + ".label");
|
||||||
},
|
},
|
||||||
|
@ -852,6 +861,13 @@ var gPermissionObject = {
|
||||||
"midi-sysex": {
|
"midi-sysex": {
|
||||||
exactHostMatch: true,
|
exactHostMatch: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"storage-access": {
|
||||||
|
labelID: null,
|
||||||
|
getDefault() {
|
||||||
|
return SitePermissions.UNKNOWN;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) {
|
if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
|
||||||
add_task(async function testPermissionsListing() {
|
add_task(async function testPermissionsListing() {
|
||||||
let expectedPermissions = ["autoplay-media", "camera", "cookie", "desktop-notification", "focus-tab-by-prompt",
|
let expectedPermissions = ["autoplay-media", "camera", "cookie", "desktop-notification", "focus-tab-by-prompt",
|
||||||
"geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts",
|
"geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts",
|
||||||
"persistent-storage"];
|
"persistent-storage", "storage-access"];
|
||||||
if (RESIST_FINGERPRINTING_ENABLED) {
|
if (RESIST_FINGERPRINTING_ENABLED) {
|
||||||
// Canvas permission should be hidden unless privacy.resistFingerprinting
|
// Canvas permission should be hidden unless privacy.resistFingerprinting
|
||||||
// is true.
|
// is true.
|
||||||
|
@ -119,7 +119,8 @@ add_task(async function testExactHostMatch() {
|
||||||
exactHostMatched.push("midi");
|
exactHostMatched.push("midi");
|
||||||
exactHostMatched.push("midi-sysex");
|
exactHostMatched.push("midi-sysex");
|
||||||
}
|
}
|
||||||
let nonExactHostMatched = ["image", "cookie", "plugin:flash", "popup", "install", "shortcuts"];
|
let nonExactHostMatched = ["image", "cookie", "plugin:flash", "popup", "install", "shortcuts",
|
||||||
|
"storage-access"];
|
||||||
|
|
||||||
let permissions = SitePermissions.listPermissions();
|
let permissions = SitePermissions.listPermissions();
|
||||||
for (let permission of permissions) {
|
for (let permission of permissions) {
|
||||||
|
|
|
@ -32,7 +32,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-notification-icon[popupid="persistent-storage"],
|
.popup-notification-icon[popupid="persistent-storage"],
|
||||||
.persistent-storage-icon {
|
.popup-notification-icon[popupid="storage-access"],
|
||||||
|
.persistent-storage-icon,
|
||||||
|
.storage-access-icon {
|
||||||
list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
|
list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +75,24 @@
|
||||||
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-blocked.svg);
|
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-blocked.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.storage-access-notification-content {
|
||||||
|
color: var(--panel-disabled-color);
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-access-notification-content .text-link {
|
||||||
|
color: -moz-nativehyperlinktext;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-access-notification-content .text-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#storage-access-notification .popup-notification-body-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
|
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
|
||||||
.indexedDB-icon {
|
.indexedDB-icon {
|
||||||
list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
|
list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include "StorageAccessPermissionRequest.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(StorageAccessPermissionRequest,
|
||||||
|
ContentPermissionRequestBase)
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(StorageAccessPermissionRequest,
|
||||||
|
ContentPermissionRequestBase)
|
||||||
|
|
||||||
|
StorageAccessPermissionRequest::StorageAccessPermissionRequest(
|
||||||
|
nsPIDOMWindowInner* aWindow,
|
||||||
|
nsIPrincipal* aNodePrincipal,
|
||||||
|
AllowCallback&& aAllowCallback,
|
||||||
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||||
|
CancelCallback&& aCancelCallback)
|
||||||
|
: ContentPermissionRequestBase(aNodePrincipal, false, aWindow,
|
||||||
|
NS_LITERAL_CSTRING("dom.storage_access"),
|
||||||
|
NS_LITERAL_CSTRING("storage-access")),
|
||||||
|
mAllowCallback(std::move(aAllowCallback)),
|
||||||
|
mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
|
||||||
|
mCancelCallback(std::move(aCancelCallback)),
|
||||||
|
mCallbackCalled(false)
|
||||||
|
{
|
||||||
|
mPermissionRequests.AppendElement(PermissionRequest(mType, nsTArray<nsString>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageAccessPermissionRequest::~StorageAccessPermissionRequest()
|
||||||
|
{
|
||||||
|
Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
StorageAccessPermissionRequest::Cancel()
|
||||||
|
{
|
||||||
|
if (!mCallbackCalled) {
|
||||||
|
mCallbackCalled = true;
|
||||||
|
mCancelCallback();
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices)
|
||||||
|
{
|
||||||
|
nsTArray<PermissionChoice> choices;
|
||||||
|
nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mCallbackCalled) {
|
||||||
|
mCallbackCalled = true;
|
||||||
|
if (choices.Length() == 1 &&
|
||||||
|
choices[0].choice().EqualsLiteral("allow-on-any-site")) {
|
||||||
|
mAllowAnySiteCallback();
|
||||||
|
} else {
|
||||||
|
mAllowCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<StorageAccessPermissionRequest>
|
||||||
|
StorageAccessPermissionRequest::Create(nsPIDOMWindowInner* aWindow,
|
||||||
|
AllowCallback&& aAllowCallback,
|
||||||
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||||
|
CancelCallback&& aCancelCallback)
|
||||||
|
{
|
||||||
|
if (!aWindow) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
|
||||||
|
if (!win->GetPrincipal()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
RefPtr<StorageAccessPermissionRequest> request =
|
||||||
|
new StorageAccessPermissionRequest(aWindow,
|
||||||
|
win->GetPrincipal(),
|
||||||
|
std::move(aAllowCallback),
|
||||||
|
std::move(aAllowAnySiteCallback),
|
||||||
|
std::move(aCancelCallback));
|
||||||
|
return request.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef StorageAccessPermissionRequest_h_
|
||||||
|
#define StorageAccessPermissionRequest_h_
|
||||||
|
|
||||||
|
#include "nsContentPermissionHelper.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class nsPIDOMWindowInner;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class StorageAccessPermissionRequest final : public ContentPermissionRequestBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StorageAccessPermissionRequest,
|
||||||
|
ContentPermissionRequestBase)
|
||||||
|
|
||||||
|
// nsIContentPermissionRequest
|
||||||
|
NS_IMETHOD Cancel(void) override;
|
||||||
|
NS_IMETHOD Allow(JS::HandleValue choices) override;
|
||||||
|
|
||||||
|
typedef std::function<void()> AllowCallback;
|
||||||
|
typedef std::function<void()> AllowAnySiteCallback;
|
||||||
|
typedef std::function<void()> CancelCallback;
|
||||||
|
|
||||||
|
static already_AddRefed<StorageAccessPermissionRequest> Create(
|
||||||
|
nsPIDOMWindowInner* aWindow,
|
||||||
|
AllowCallback&& aAllowCallback,
|
||||||
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||||
|
CancelCallback&& aCancelCallback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StorageAccessPermissionRequest(nsPIDOMWindowInner* aWindow,
|
||||||
|
nsIPrincipal* aNodePrincipal,
|
||||||
|
AllowCallback&& aAllowCallback,
|
||||||
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||||
|
CancelCallback&& aCancelCallback);
|
||||||
|
~StorageAccessPermissionRequest();
|
||||||
|
|
||||||
|
AllowCallback mAllowCallback;
|
||||||
|
AllowAnySiteCallback mAllowAnySiteCallback;
|
||||||
|
CancelCallback mCancelCallback;
|
||||||
|
nsTArray<PermissionRequest> mPermissionRequests;
|
||||||
|
bool mCallbackCalled;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // StorageAccessPermissionRequest_h_
|
|
@ -373,6 +373,7 @@ UNIFIED_SOURCES += [
|
||||||
'Selection.cpp',
|
'Selection.cpp',
|
||||||
'SelectionChangeEventDispatcher.cpp',
|
'SelectionChangeEventDispatcher.cpp',
|
||||||
'ShadowRoot.cpp',
|
'ShadowRoot.cpp',
|
||||||
|
'StorageAccessPermissionRequest.cpp',
|
||||||
'StructuredCloneBlob.cpp',
|
'StructuredCloneBlob.cpp',
|
||||||
'StructuredCloneHolder.cpp',
|
'StructuredCloneHolder.cpp',
|
||||||
'StructuredCloneTester.cpp',
|
'StructuredCloneTester.cpp',
|
||||||
|
|
|
@ -284,6 +284,7 @@
|
||||||
#include "NodeUbiReporting.h"
|
#include "NodeUbiReporting.h"
|
||||||
#include "nsICookieService.h"
|
#include "nsICookieService.h"
|
||||||
#include "mozilla/net/RequestContextService.h"
|
#include "mozilla/net/RequestContextService.h"
|
||||||
|
#include "StorageAccessPermissionRequest.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
@ -13906,7 +13907,7 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1. If the document already has been granted access, resolve.
|
// Step 1. If the document already has been granted access, resolve.
|
||||||
nsPIDOMWindowInner* inner = GetInnerWindow();
|
nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
|
||||||
RefPtr<nsGlobalWindowOuter> outer;
|
RefPtr<nsGlobalWindowOuter> outer;
|
||||||
if (inner) {
|
if (inner) {
|
||||||
outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
|
outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
|
||||||
|
@ -13976,10 +13977,9 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool granted = true;
|
|
||||||
bool isTrackingWindow = false;
|
|
||||||
if (StaticPrefs::network_cookie_cookieBehavior() ==
|
if (StaticPrefs::network_cookie_cookieBehavior() ==
|
||||||
nsICookieService::BEHAVIOR_REJECT_TRACKER) {
|
nsICookieService::BEHAVIOR_REJECT_TRACKER &&
|
||||||
|
inner) {
|
||||||
// Only do something special for third-party tracking content.
|
// Only do something special for third-party tracking content.
|
||||||
if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
|
if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
|
||||||
// Note: If this has returned true, the top-level document is guaranteed
|
// Note: If this has returned true, the top-level document is guaranteed
|
||||||
|
@ -13996,33 +13996,68 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
|
||||||
isOnAllowList)),
|
isOnAllowList)),
|
||||||
!isOnAllowList);
|
!isOnAllowList);
|
||||||
|
|
||||||
isTrackingWindow = true;
|
auto performFinalChecks = [inner] () -> RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise> {
|
||||||
// TODO: prompt for permission
|
RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise::Private> p =
|
||||||
|
new AntiTrackingCommon::StorageAccessFinalCheckPromise::Private(__func__);
|
||||||
|
RefPtr<StorageAccessPermissionRequest> sapr =
|
||||||
|
StorageAccessPermissionRequest::Create(inner,
|
||||||
|
// Allow
|
||||||
|
[p] { p->Resolve(false, __func__); },
|
||||||
|
// Allow on any site
|
||||||
|
[p] { p->Resolve(true, __func__); },
|
||||||
|
// Block
|
||||||
|
[p] { p->Reject(false, __func__); });
|
||||||
|
|
||||||
|
typedef ContentPermissionRequestBase::PromptResult PromptResult;
|
||||||
|
PromptResult pr = sapr->CheckPromptPrefs();
|
||||||
|
bool onAnySite = false;
|
||||||
|
if (pr == PromptResult::Pending) {
|
||||||
|
// Also check our custom pref for the "Allow on any site" case
|
||||||
|
if (Preferences::GetBool("dom.storage_access.prompt.testing", false) &&
|
||||||
|
Preferences::GetBool("dom.storage_access.prompt.testing.allowonanysite", false)) {
|
||||||
|
pr = PromptResult::Granted;
|
||||||
|
onAnySite = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pr != PromptResult::Pending) {
|
||||||
|
MOZ_ASSERT_IF(pr != PromptResult::Granted,
|
||||||
|
pr == PromptResult::Denied);
|
||||||
|
if (pr == PromptResult::Granted) {
|
||||||
|
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||||
|
CreateAndResolve(onAnySite, __func__);
|
||||||
|
}
|
||||||
|
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||||
|
CreateAndReject(false, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
sapr->RequestDelayedTask(inner->EventTargetFor(TaskCategory::Other),
|
||||||
|
ContentPermissionRequestBase::DelayedTaskType::Request);
|
||||||
|
return p.forget();
|
||||||
|
};
|
||||||
|
AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
|
||||||
|
NodePrincipal(),
|
||||||
|
inner,
|
||||||
|
AntiTrackingCommon::eStorageAccessAPI,
|
||||||
|
performFinalChecks)->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
|
[outer, promise] {
|
||||||
// Step 10. Grant the document access to cookies and store that fact for
|
// Step 10. Grant the document access to cookies and store that fact for
|
||||||
// the purposes of future calls to hasStorageAccess() and
|
// the purposes of future calls to hasStorageAccess() and
|
||||||
// requestStorageAccess().
|
// requestStorageAccess().
|
||||||
if (granted && inner) {
|
|
||||||
if (isTrackingWindow) {
|
|
||||||
AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(NodePrincipal(),
|
|
||||||
inner,
|
|
||||||
AntiTrackingCommon::eStorageAccessAPI)
|
|
||||||
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
|
||||||
[outer, promise] (bool) {
|
|
||||||
outer->SetHasStorageAccess(true);
|
outer->SetHasStorageAccess(true);
|
||||||
promise->MaybeResolveWithUndefined();
|
promise->MaybeResolveWithUndefined();
|
||||||
},
|
},
|
||||||
[outer, promise] (bool) {
|
[outer, promise] {
|
||||||
outer->SetHasStorageAccess(false);
|
outer->SetHasStorageAccess(false);
|
||||||
promise->MaybeRejectWithUndefined();
|
promise->MaybeRejectWithUndefined();
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outer->SetHasStorageAccess(true);
|
outer->SetHasStorageAccess(true);
|
||||||
promise->MaybeResolveWithUndefined();
|
promise->MaybeResolveWithUndefined();
|
||||||
}
|
|
||||||
}
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6010,14 +6010,23 @@ ContentParent::RecvBHRThreadHang(const HangDetails& aDetails)
|
||||||
|
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
|
ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
|
||||||
|
const Principal& aTrackingPrincipal,
|
||||||
const nsCString& aTrackingOrigin,
|
const nsCString& aTrackingOrigin,
|
||||||
const nsCString& aGrantedOrigin,
|
const nsCString& aGrantedOrigin,
|
||||||
|
const bool& aAnySite,
|
||||||
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver)
|
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver)
|
||||||
{
|
{
|
||||||
AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(aParentPrincipal,
|
AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(aParentPrincipal,
|
||||||
|
aTrackingPrincipal,
|
||||||
aTrackingOrigin,
|
aTrackingOrigin,
|
||||||
aGrantedOrigin,
|
aGrantedOrigin,
|
||||||
std::move(aResolver));
|
aAnySite)
|
||||||
|
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
|
[aResolver = std::move(aResolver)]
|
||||||
|
(AntiTrackingCommon::FirstPartyStorageAccessGrantPromise::ResolveOrRejectValue&& aValue) {
|
||||||
|
bool success = aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue());
|
||||||
|
aResolver(success);
|
||||||
|
});
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1248,8 +1248,10 @@ public:
|
||||||
|
|
||||||
virtual mozilla::ipc::IPCResult
|
virtual mozilla::ipc::IPCResult
|
||||||
RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
|
RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
|
||||||
|
const Principal& aTrackingPrincipal,
|
||||||
const nsCString& aTrackingOrigin,
|
const nsCString& aTrackingOrigin,
|
||||||
const nsCString& aGrantedOrigin,
|
const nsCString& aGrantedOrigin,
|
||||||
|
const bool& aAnySite,
|
||||||
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver) override;
|
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver) override;
|
||||||
|
|
||||||
virtual mozilla::ipc::IPCResult
|
virtual mozilla::ipc::IPCResult
|
||||||
|
|
|
@ -1157,8 +1157,10 @@ parent:
|
||||||
* granted to have access to aGrantedOrigin when loaded by aParentPrincipal.
|
* granted to have access to aGrantedOrigin when loaded by aParentPrincipal.
|
||||||
*/
|
*/
|
||||||
async FirstPartyStorageAccessGrantedForOrigin(Principal aParentPrincipal,
|
async FirstPartyStorageAccessGrantedForOrigin(Principal aParentPrincipal,
|
||||||
|
Principal aTrackingPrincipal,
|
||||||
nsCString aTrackingOrigin,
|
nsCString aTrackingOrigin,
|
||||||
nsCString aGrantedOrigin)
|
nsCString aGrantedOrigin,
|
||||||
|
bool aAnySite)
|
||||||
returns (bool unused);
|
returns (bool unused);
|
||||||
|
|
||||||
async StoreUserInteractionAsPermission(Principal aPrincipal);
|
async StoreUserInteractionAsPermission(Principal aPrincipal);
|
||||||
|
|
|
@ -417,19 +417,20 @@ CompareBaseDomains(nsIURI* aTrackingURI,
|
||||||
/* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
|
/* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
|
||||||
AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
|
AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
|
||||||
nsPIDOMWindowInner* aParentWindow,
|
nsPIDOMWindowInner* aParentWindow,
|
||||||
StorageAccessGrantedReason aReason)
|
StorageAccessGrantedReason aReason,
|
||||||
|
const AntiTrackingCommon::PerformFinalChecks& aPerformFinalChecks)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aParentWindow);
|
MOZ_ASSERT(aParentWindow);
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
nsCOMPtr<nsIURI> uri;
|
||||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
aPrincipal->GetURI(getter_AddRefs(uri));
|
||||||
if (NS_WARN_IF(!uri)) {
|
if (NS_WARN_IF(!uri)) {
|
||||||
LOG(("Can't get the URI from the principal"));
|
LOG(("Can't get the URI from the principal"));
|
||||||
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString origin;
|
nsAutoString origin;
|
||||||
rv = nsContentUtils::GetUTFOrigin(uri, origin);
|
nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
LOG(("Can't get the origin from the URI"));
|
LOG(("Can't get the origin from the URI"));
|
||||||
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
|
@ -454,7 +455,7 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
||||||
nsAutoCString trackingOrigin;
|
nsAutoCString trackingOrigin;
|
||||||
nsCOMPtr<nsIPrincipal> trackingPrincipal;
|
nsCOMPtr<nsIPrincipal> trackingPrincipal;
|
||||||
|
|
||||||
nsGlobalWindowInner* parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
|
RefPtr<nsGlobalWindowInner> parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
|
||||||
nsGlobalWindowOuter* outerParentWindow =
|
nsGlobalWindowOuter* outerParentWindow =
|
||||||
nsGlobalWindowOuter::Cast(parentWindow->GetOuterWindow());
|
nsGlobalWindowOuter::Cast(parentWindow->GetOuterWindow());
|
||||||
if (NS_WARN_IF(!outerParentWindow)) {
|
if (NS_WARN_IF(!outerParentWindow)) {
|
||||||
|
@ -510,7 +511,13 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
||||||
// user-interaction state, because it could be that the current process has
|
// user-interaction state, because it could be that the current process has
|
||||||
// just sent the request to store the user-interaction permission into the
|
// just sent the request to store the user-interaction permission into the
|
||||||
// parent, without having received the permission itself yet.
|
// parent, without having received the permission itself yet.
|
||||||
const uint32_t blockReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
|
//
|
||||||
|
// We define this as an enum, since without that MSVC fails to capturing this
|
||||||
|
// name inside the lambda without the explicit capture and clang warns if
|
||||||
|
// there is an explicit capture with -Wunused-lambda-capture.
|
||||||
|
enum : uint32_t {
|
||||||
|
blockReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
|
||||||
|
};
|
||||||
if ((aReason != eOpenerAfterUserInteraction ||
|
if ((aReason != eOpenerAfterUserInteraction ||
|
||||||
nsContentUtils::IsURIInPrefList(trackingURI,
|
nsContentUtils::IsURIInPrefList(trackingURI,
|
||||||
"privacy.restrict3rdpartystorage.userInteractionRequiredForHosts")) &&
|
"privacy.restrict3rdpartystorage.userInteractionRequiredForHosts")) &&
|
||||||
|
@ -528,6 +535,10 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
||||||
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto storePermission = [pwin, parentWindow, origin, trackingOrigin,
|
||||||
|
trackingPrincipal, trackingURI, topInnerWindow,
|
||||||
|
topLevelStoragePrincipal, aReason]
|
||||||
|
(bool aAnySite) -> RefPtr<StorageAccessGrantPromise> {
|
||||||
NS_ConvertUTF16toUTF8 grantedOrigin(origin);
|
NS_ConvertUTF16toUTF8 grantedOrigin(origin);
|
||||||
|
|
||||||
nsAutoCString permissionKey;
|
nsAutoCString permissionKey;
|
||||||
|
@ -551,14 +562,18 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
||||||
LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s",
|
LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s",
|
||||||
trackingOrigin.get(), grantedOrigin.get()));
|
trackingOrigin.get(), grantedOrigin.get()));
|
||||||
|
|
||||||
RefPtr<StorageAccessGrantPromise::Private> p = new StorageAccessGrantPromise::Private(__func__);
|
return SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(topLevelStoragePrincipal,
|
||||||
SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(topLevelStoragePrincipal,
|
trackingPrincipal,
|
||||||
trackingOrigin,
|
trackingOrigin,
|
||||||
grantedOrigin,
|
grantedOrigin,
|
||||||
[p] (bool success) {
|
aAnySite)
|
||||||
p->Resolve(success, __func__);
|
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
|
[] (FirstPartyStorageAccessGrantPromise::ResolveOrRejectValue&& aValue) {
|
||||||
|
if (aValue.IsResolve()) {
|
||||||
|
return StorageAccessGrantPromise::CreateAndResolve(NS_SUCCEEDED(aValue.ResolveValue()), __func__);
|
||||||
|
}
|
||||||
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
});
|
});
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentChild* cc = ContentChild::GetSingleton();
|
ContentChild* cc = ContentChild::GetSingleton();
|
||||||
|
@ -569,24 +584,39 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
||||||
|
|
||||||
// This is not really secure, because here we have the content process sending
|
// This is not really secure, because here we have the content process sending
|
||||||
// the request of storing a permission.
|
// the request of storing a permission.
|
||||||
RefPtr<StorageAccessGrantPromise::Private> p = new StorageAccessGrantPromise::Private(__func__);
|
return cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(topLevelStoragePrincipal),
|
||||||
cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(topLevelStoragePrincipal),
|
IPC::Principal(trackingPrincipal),
|
||||||
trackingOrigin,
|
trackingOrigin,
|
||||||
grantedOrigin)
|
grantedOrigin,
|
||||||
|
aAnySite)
|
||||||
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
[p] (bool success) {
|
[] (const ContentChild::FirstPartyStorageAccessGrantedForOriginPromise::ResolveOrRejectValue& aValue) {
|
||||||
p->Resolve(success, __func__);
|
if (aValue.IsResolve()) {
|
||||||
}, [p] (ipc::ResponseRejectReason aReason) {
|
return StorageAccessGrantPromise::CreateAndResolve(aValue.ResolveValue(), __func__);
|
||||||
p->Reject(false, __func__);
|
}
|
||||||
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
});
|
});
|
||||||
return p;
|
};
|
||||||
|
|
||||||
|
if (aPerformFinalChecks) {
|
||||||
|
return aPerformFinalChecks()
|
||||||
|
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||||
|
[storePermission] (StorageAccessGrantPromise::ResolveOrRejectValue&& aValue) {
|
||||||
|
if (aValue.IsResolve()) {
|
||||||
|
return storePermission(aValue.ResolveValue());
|
||||||
|
}
|
||||||
|
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return storePermission(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ RefPtr<mozilla::AntiTrackingCommon::FirstPartyStorageAccessGrantPromise>
|
||||||
AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aParentPrincipal,
|
AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aParentPrincipal,
|
||||||
|
nsIPrincipal* aTrackingPrincipal,
|
||||||
const nsCString& aTrackingOrigin,
|
const nsCString& aTrackingOrigin,
|
||||||
const nsCString& aGrantedOrigin,
|
const nsCString& aGrantedOrigin,
|
||||||
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver)
|
bool aAnySite)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess());
|
||||||
|
|
||||||
|
@ -598,15 +628,13 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
||||||
if (NS_WARN_IF(!aParentPrincipal)) {
|
if (NS_WARN_IF(!aParentPrincipal)) {
|
||||||
// The child process is sending something wrong. Let's ignore it.
|
// The child process is sending something wrong. Let's ignore it.
|
||||||
LOG(("aParentPrincipal is null, bailing out early"));
|
LOG(("aParentPrincipal is null, bailing out early"));
|
||||||
aResolver(false);
|
return FirstPartyStorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
|
nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
|
||||||
if (NS_WARN_IF(!pm)) {
|
if (NS_WARN_IF(!pm)) {
|
||||||
LOG(("Permission manager is null, bailing out early"));
|
LOG(("Permission manager is null, bailing out early"));
|
||||||
aResolver(false);
|
return FirstPartyStorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember that this pref is stored in seconds!
|
// Remember that this pref is stored in seconds!
|
||||||
|
@ -615,8 +643,26 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
||||||
StaticPrefs::privacy_restrict3rdpartystorage_expiration() * 1000;
|
StaticPrefs::privacy_restrict3rdpartystorage_expiration() * 1000;
|
||||||
int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + expirationTime;
|
int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + expirationTime;
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
if (aAnySite) {
|
||||||
uint32_t privateBrowsingId = 0;
|
uint32_t privateBrowsingId = 0;
|
||||||
nsresult rv = aParentPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
|
rv = aTrackingPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
|
||||||
|
if (!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) {
|
||||||
|
// If we are coming from a private window, make sure to store a session-only
|
||||||
|
// permission which won't get persisted to disk.
|
||||||
|
expirationType = nsIPermissionManager::EXPIRE_SESSION;
|
||||||
|
when = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(("Setting 'any site' permission expiry: %u, proceeding to save in the permission manager",
|
||||||
|
expirationTime));
|
||||||
|
|
||||||
|
rv = pm->AddFromPrincipal(aTrackingPrincipal, "cookie",
|
||||||
|
nsICookiePermission::ACCESS_ALLOW,
|
||||||
|
expirationType, when);
|
||||||
|
} else {
|
||||||
|
uint32_t privateBrowsingId = 0;
|
||||||
|
rv = aParentPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
|
||||||
if (!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) {
|
if (!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) {
|
||||||
// If we are coming from a private window, make sure to store a session-only
|
// If we are coming from a private window, make sure to store a session-only
|
||||||
// permission which won't get persisted to disk.
|
// permission which won't get persisted to disk.
|
||||||
|
@ -633,10 +679,11 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
||||||
rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
|
rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
|
||||||
nsIPermissionManager::ALLOW_ACTION,
|
nsIPermissionManager::ALLOW_ACTION,
|
||||||
expirationType, when);
|
expirationType, when);
|
||||||
|
}
|
||||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||||
aResolver(NS_SUCCEEDED(rv));
|
|
||||||
|
|
||||||
LOG(("Result: %s", NS_SUCCEEDED(rv) ? "success" : "failure"));
|
LOG(("Result: %s", NS_SUCCEEDED(rv) ? "success" : "failure"));
|
||||||
|
return FirstPartyStorageAccessGrantPromise::CreateAndResolve(rv, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -93,11 +93,14 @@ public:
|
||||||
// Ex: example.net import tracker.com/script.js which does opens a popup and
|
// Ex: example.net import tracker.com/script.js which does opens a popup and
|
||||||
// the user interacts with it. tracker.com is allowed when loaded by
|
// the user interacts with it. tracker.com is allowed when loaded by
|
||||||
// example.net.
|
// example.net.
|
||||||
typedef MozPromise<bool, bool, false> StorageAccessGrantPromise;
|
typedef MozPromise<bool, bool, true> StorageAccessFinalCheckPromise;
|
||||||
|
typedef std::function<RefPtr<StorageAccessFinalCheckPromise>()> PerformFinalChecks;
|
||||||
|
typedef MozPromise<bool, bool, true> StorageAccessGrantPromise;
|
||||||
static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise>
|
static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise>
|
||||||
AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
|
AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
|
||||||
nsPIDOMWindowInner* aParentWindow,
|
nsPIDOMWindowInner* aParentWindow,
|
||||||
StorageAccessGrantedReason aReason);
|
StorageAccessGrantedReason aReason,
|
||||||
|
const PerformFinalChecks& aPerformFinalChecks = nullptr);
|
||||||
|
|
||||||
// Returns true if the permission passed in is a storage access permission
|
// Returns true if the permission passed in is a storage access permission
|
||||||
// for the passed in principal argument.
|
// for the passed in principal argument.
|
||||||
|
@ -111,11 +114,13 @@ public:
|
||||||
HasUserInteraction(nsIPrincipal* aPrincipal);
|
HasUserInteraction(nsIPrincipal* aPrincipal);
|
||||||
|
|
||||||
// For IPC only.
|
// For IPC only.
|
||||||
static void
|
typedef MozPromise<nsresult, bool, true> FirstPartyStorageAccessGrantPromise;
|
||||||
|
static RefPtr<FirstPartyStorageAccessGrantPromise>
|
||||||
SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
|
SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
|
||||||
|
nsIPrincipal* aTrackingPrinciapl,
|
||||||
const nsCString& aParentOrigin,
|
const nsCString& aParentOrigin,
|
||||||
const nsCString& aGrantedOrigin,
|
const nsCString& aGrantedOrigin,
|
||||||
FirstPartyStorageAccessGrantedForOriginResolver&& aResolver);
|
bool aAnySite);
|
||||||
|
|
||||||
enum ContentBlockingAllowListPurpose {
|
enum ContentBlockingAllowListPurpose {
|
||||||
eStorageChecks,
|
eStorageChecks,
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
prefs =
|
||||||
|
# Disable the Storage Access API prompts for all of the tests in this directory
|
||||||
|
dom.storage_access.prompt.testing=true
|
||||||
|
dom.storage_access.prompt.testing.allow=true
|
||||||
|
|
||||||
support-files =
|
support-files =
|
||||||
embedder.html
|
embedder.html
|
||||||
head.js
|
head.js
|
||||||
|
|
|
@ -35,7 +35,8 @@
|
||||||
localization file, if necessary). -->
|
localization file, if necessary). -->
|
||||||
<xul:description class="popup-notification-description" xbl:inherits="popupid"><html:span
|
<xul:description class="popup-notification-description" xbl:inherits="popupid"><html:span
|
||||||
xbl:inherits="xbl:text=label,popupid"/><html:b xbl:inherits="xbl:text=name,popupid"/><html:span
|
xbl:inherits="xbl:text=label,popupid"/><html:b xbl:inherits="xbl:text=name,popupid"/><html:span
|
||||||
xbl:inherits="xbl:text=endlabel,popupid"/></xul:description>
|
xbl:inherits="xbl:text=endlabel,popupid"/><html:b xbl:inherits="xbl:text=secondname,popupid"/><html:span
|
||||||
|
xbl:inherits="xbl:text=secondendlabel,popupid"/></xul:description>
|
||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
<xul:toolbarbutton anonid="closebutton"
|
<xul:toolbarbutton anonid="closebutton"
|
||||||
class="messageCloseButton close-icon popup-notification-closebutton tabbable"
|
class="messageCloseButton close-icon popup-notification-closebutton tabbable"
|
||||||
|
|
|
@ -329,10 +329,12 @@ PopupNotifications.prototype = {
|
||||||
* at a time. If a notification already exists with the given ID, it
|
* at a time. If a notification already exists with the given ID, it
|
||||||
* will be replaced.
|
* will be replaced.
|
||||||
* @param message
|
* @param message
|
||||||
* A string containing the text to be displayed as the notification header.
|
* A string containing the text to be displayed as the notification
|
||||||
* The string may optionally contain "<>" as a placeholder which is later
|
* header. The string may optionally contain one or two "<>" as a
|
||||||
* replaced by a host name or an addon name that is formatted to look bold,
|
* placeholder which is later replaced by a host name or an addon name
|
||||||
* in which case the options.name property needs to be specified.
|
* that is formatted to look bold, in which case the options.name
|
||||||
|
* property (as well as options.secondName if passing two "<>"
|
||||||
|
* placeholders) needs to be specified.
|
||||||
* @param anchorID
|
* @param anchorID
|
||||||
* The ID of the element that should be used as this notification
|
* The ID of the element that should be used as this notification
|
||||||
* popup's anchor. May be null, in which case the notification will be
|
* popup's anchor. May be null, in which case the notification will be
|
||||||
|
@ -460,6 +462,11 @@ PopupNotifications.prototype = {
|
||||||
* An optional string formatted to look bold and used in the
|
* An optional string formatted to look bold and used in the
|
||||||
* notifiation description header text. Usually a host name or
|
* notifiation description header text. Usually a host name or
|
||||||
* addon name.
|
* addon name.
|
||||||
|
* secondName:
|
||||||
|
* An optional string formatted to look bold and used in the
|
||||||
|
* notification description header text. Usually a host name or
|
||||||
|
* addon name. This is similar to name, and only used in case
|
||||||
|
* where message contains two "<>" placeholders.
|
||||||
* @returns the Notification object corresponding to the added notification.
|
* @returns the Notification object corresponding to the added notification.
|
||||||
*/
|
*/
|
||||||
show: function PopupNotifications_show(browser, id, message, anchorID,
|
show: function PopupNotifications_show(browser, id, message, anchorID,
|
||||||
|
@ -781,6 +788,13 @@ PopupNotifications.prototype = {
|
||||||
text.start = array[0] || "";
|
text.start = array[0] || "";
|
||||||
text.name = n.options.name || "";
|
text.name = n.options.name || "";
|
||||||
text.end = array[1] || "";
|
text.end = array[1] || "";
|
||||||
|
if (array.length == 3) {
|
||||||
|
text.secondName = n.options.secondName || "";
|
||||||
|
text.secondEnd = array[2] || "";
|
||||||
|
} else if (array.length > 3) {
|
||||||
|
Cu.reportError("Unexpected array length encountered in " +
|
||||||
|
"_formatDescriptionMessage: " + array.length);
|
||||||
|
}
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -807,6 +821,11 @@ PopupNotifications.prototype = {
|
||||||
popupnotification.setAttribute("label", desc.start);
|
popupnotification.setAttribute("label", desc.start);
|
||||||
popupnotification.setAttribute("name", desc.name);
|
popupnotification.setAttribute("name", desc.name);
|
||||||
popupnotification.setAttribute("endlabel", desc.end);
|
popupnotification.setAttribute("endlabel", desc.end);
|
||||||
|
if (("secondName" in desc) &&
|
||||||
|
("secondEnd" in desc)) {
|
||||||
|
popupnotification.setAttribute("secondname", desc.secondName);
|
||||||
|
popupnotification.setAttribute("secondendlabel", desc.secondEnd);
|
||||||
|
}
|
||||||
|
|
||||||
popupnotification.setAttribute("id", popupnotificationID);
|
popupnotification.setAttribute("id", popupnotificationID);
|
||||||
popupnotification.setAttribute("popupid", n.id);
|
popupnotification.setAttribute("popupid", n.id);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче