Bug 1643191 - Show storage access permissions in the identity panel. r=pbz,fluent-reviewers,johannh,flod

This patch adds storage access permission items in the identity panel. This is a new
class of permission - multiple permission items might exist for the category i.e.
one for each third-party that has been granted access. To achieve this, we add support
for an "anchorfor" attribute in the permission list markup, which when present will
signal to the code that the element is to be the container for permission items of type
matching the value of the attribute. This hence adds support generally for categorical
permission types.

Differential Revision: https://phabricator.services.mozilla.com/D94703
This commit is contained in:
Nihanth Subramanya 2020-11-09 00:21:13 +00:00
parent e84023a1a4
commit 4e142d3196
5 changed files with 131 additions and 38 deletions

View File

@ -301,6 +301,12 @@ var gIdentityHandler = {
"identity-popup-permission-list"
));
},
get _defaultPermissionAnchor() {
delete this._defaultPermissionAnchor;
return (this._defaultPermissionAnchor = document.getElementById(
"identity-popup-permission-list-default-anchor"
));
},
get _permissionEmptyHint() {
delete this._permissionEmptyHint;
return (this._permissionEmptyHint = document.getElementById(
@ -1498,7 +1504,7 @@ var gIdentityHandler = {
if (this._popupInitialized && this._identityPopup.state != "closed") {
this._permissionReloadHint.setAttribute("hidden", "true");
if (!this._permissionList.hasChildNodes()) {
if (this._isPermissionListEmpty()) {
this._permissionEmptyHint.removeAttribute("hidden");
}
}
@ -1512,10 +1518,19 @@ var gIdentityHandler = {
}
},
_isPermissionListEmpty() {
return !this._permissionList.querySelectorAll(
".identity-popup-permission-item"
).length;
},
updateSitePermissions() {
while (this._permissionList.hasChildNodes()) {
this._permissionList.removeChild(this._permissionList.lastChild);
}
let permissionItemSelector = [
".identity-popup-permission-item, .identity-popup-permission-item-container",
];
this._permissionList
.querySelectorAll(permissionItemSelector)
.forEach(e => e.remove());
let permissions = SitePermissions.getAllPermissionDetailsForBrowser(
gBrowser.selectedBrowser
@ -1591,6 +1606,9 @@ var gIdentityHandler = {
}
let item;
let anchor =
this._permissionList.querySelector(`[anchorfor="${id}"]`) ||
this._defaultPermissionAnchor;
if (id == "open-protocol-handler") {
let permContainer = this._createProtocolHandlerPermissionItem(
@ -1598,18 +1616,19 @@ var gIdentityHandler = {
key
);
if (permContainer) {
this._permissionList.appendChild(permContainer);
anchor.appendChild(permContainer);
}
} else {
item = this._createPermissionItem(
item = this._createPermissionItem({
permission,
id == "geo" || id == "xr"
);
isContainer: id == "geo" || id == "xr",
nowrapLabel: id == "3rdPartyStorage",
});
if (!item) {
continue;
}
this._permissionList.appendChild(item);
anchor.appendChild(item);
}
if (id == "popup" && totalBlockedPopups) {
@ -1626,14 +1645,14 @@ var gIdentityHandler = {
state: SitePermissions.getDefault("popup"),
scope: SitePermissions.SCOPE_PERSISTENT,
};
let item = this._createPermissionItem(permission);
this._permissionList.appendChild(item);
let item = this._createPermissionItem({ permission });
this._defaultPermissionAnchor.appendChild(item);
this._createBlockedPopupIndicator(totalBlockedPopups);
}
// Show a placeholder text if there's no permission and no reload hint.
if (
!this._permissionList.hasChildNodes() &&
this._isPermissionListEmpty() &&
this._permissionReloadHint.hasAttribute("hidden")
) {
this._permissionEmptyHint.removeAttribute("hidden");
@ -1642,13 +1661,33 @@ var gIdentityHandler = {
}
},
_createPermissionItem(
aPermission,
/**
* Creates a permission item based on the supplied options and returns it.
* It is up to the caller to actually insert the element somewhere.
*
* @param permission - An object containing information representing the
* permission, typically obtained via SitePermissions.jsm
* @param isContainer - If true, the permission item will be added to a vbox
* and the vbox will be returned.
* @param permClearButton - Whether to show an "x" button to clear the permission
* @param showStateLabel - Whether to show a label indicating the current status
* of the permission e.g. "Temporary Allowed"
* @param idNoSuffix - Some permission types have additional information suffixed
* to the ID - callers can pass the unsuffixed ID via this
* parameter to indicate the permission type manually.
* @param nowrapLabel - Whether to prevent the permission item's label from
* wrapping its text content. This allows styling text-overflow
* and is useful for e.g. 3rdPartyStorage permissions whose
* labels are origins - which could be of any length.
*/
_createPermissionItem({
permission,
isContainer = false,
permClearButton = true,
showStateLabel = true,
idNoSuffix = aPermission.id
) {
idNoSuffix = permission.id,
nowrapLabel = false,
}) {
let container = document.createXULElement("hbox");
container.setAttribute("class", "identity-popup-permission-item");
container.setAttribute("align", "center");
@ -1657,18 +1696,18 @@ var gIdentityHandler = {
let img = document.createXULElement("image");
img.classList.add("identity-popup-permission-icon", idNoSuffix + "-icon");
if (
aPermission.state == SitePermissions.BLOCK ||
aPermission.state == SitePermissions.AUTOPLAY_BLOCKED_ALL
permission.state == SitePermissions.BLOCK ||
permission.state == SitePermissions.AUTOPLAY_BLOCKED_ALL
) {
img.classList.add("blocked-permission-icon");
}
if (
aPermission.sharingState ==
permission.sharingState ==
Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED ||
(idNoSuffix == "screen" &&
aPermission.sharingState &&
!aPermission.sharingState.includes("Paused"))
permission.sharingState &&
!permission.sharingState.includes("Paused"))
) {
img.classList.add("in-use");
@ -1693,14 +1732,20 @@ var gIdentityHandler = {
if (label === null) {
return null;
}
nameLabel.textContent = label;
if (nowrapLabel) {
nameLabel.setAttribute("value", label);
nameLabel.setAttribute("tooltiptext", label);
nameLabel.setAttribute("crop", "end");
} else {
nameLabel.textContent = label;
}
let nameLabelId = "identity-popup-permission-label-" + idNoSuffix;
nameLabel.setAttribute("id", nameLabelId);
let isPolicyPermission = [
SitePermissions.SCOPE_POLICY,
SitePermissions.SCOPE_GLOBAL,
].includes(aPermission.scope);
].includes(permission.scope);
if (
(idNoSuffix == "popup" && !isPolicyPermission) ||
@ -1710,6 +1755,7 @@ var gIdentityHandler = {
let menupopup = document.createXULElement("menupopup");
let block = document.createXULElement("vbox");
block.setAttribute("id", "identity-popup-popup-container");
block.setAttribute("class", "identity-popup-permission-item-container");
menulist.setAttribute("sizetopopup", "none");
menulist.setAttribute("id", "identity-popup-popup-menulist");
@ -1732,10 +1778,10 @@ var gIdentityHandler = {
menulist.appendChild(menupopup);
if (aPermission.state == SitePermissions.getDefault(idNoSuffix)) {
if (permission.state == SitePermissions.getDefault(idNoSuffix)) {
menulist.value = "0";
} else {
menulist.value = aPermission.state;
menulist.value = permission.state;
}
// Avoiding listening to the "select" event on purpose. See Bug 1404262.
@ -1761,7 +1807,7 @@ var gIdentityHandler = {
let labelledBy = nameLabelId;
if (showStateLabel) {
let stateLabel = this._createStateLabel(aPermission, idNoSuffix);
let stateLabel = this._createStateLabel(permission, idNoSuffix);
container.appendChild(stateLabel);
labelledBy += " " + stateLabel.id;
}
@ -1778,9 +1824,10 @@ var gIdentityHandler = {
if (isContainer) {
let block = document.createXULElement("vbox");
block.setAttribute("id", "identity-popup-" + idNoSuffix + "-container");
block.setAttribute("class", "identity-popup-permission-item-container");
if (permClearButton) {
let button = this._createPermissionClearButton(aPermission, block);
let button = this._createPermissionClearButton(permission, block);
container.appendChild(button);
}
@ -1789,7 +1836,7 @@ var gIdentityHandler = {
}
if (permClearButton) {
let button = this._createPermissionClearButton(aPermission, container);
let button = this._createPermissionClearButton(permission, container);
container.appendChild(button);
}
@ -1977,13 +2024,13 @@ var gIdentityHandler = {
if (!container) {
// First open-protocol-handler permission, create container.
container = this._createPermissionItem(
container = this._createPermissionItem({
permission,
true,
false,
false,
"open-protocol-handler"
);
isContainer: true,
permClearButton: false,
showStateLabel: false,
idNoSuffix: "open-protocol-handler",
});
initialCall = true;
}

View File

@ -98,7 +98,18 @@
role="heading" aria-level="2"
data-l10n-id="identity-permissions"/>
</hbox>
<vbox id="identity-popup-permission-list"/>
<vbox id="identity-popup-permission-list">
<vbox id="identity-popup-permission-list-default-anchor" class="identity-popup-permission-list-anchor"/>
<vbox class="identity-popup-permission-list-anchor" anchorfor="3rdPartyStorage">
<vbox id="identity-popup-storage-access-permission-list-header">
<hbox align="center" role="group">
<image class="identity-popup-permission-icon storage-access-icon"/>
<label data-l10n-id="identity-permissions-storage-access-header" class="identity-popup-permission-header-label"/>
</hbox>
<description id="identity-popup-storage-access-permission-list-hint" data-l10n-id="identity-permissions-storage-access-hint"></description>
</vbox>
</vbox>
</vbox>
<description id="identity-popup-permission-reload-hint" data-l10n-id="identity-permissions-reload-hint"></description>
<description id="identity-popup-permission-empty-hint" data-l10n-id="identity-permissions-empty"></description>
</vbox>

View File

@ -309,6 +309,9 @@ identity-https-only-info-no-upgrade = Unable to upgrade connection from HTTP.
identity-permissions =
.value = Permissions
identity-permissions-storage-access-header = Cross-site cookies
identity-permissions-storage-access-hint = These parties can use cross-site cookies and site data while you are on this site.
identity-permissions-reload-hint = You may need to reload the page for changes to apply.
identity-permissions-empty = You have not granted this site any special permissions.
identity-clear-site-data =

View File

@ -784,6 +784,10 @@ var SitePermissions = {
// Permission doesn't support having a label.
return null;
}
if (id == "3rdPartyStorage") {
// The key is the 3rd party origin, which we use for the label.
return key;
}
let labelID = gPermissions.get(id).labelID || id;
return gStringBundle.formatStringFromName(`permission.${labelID}.label`, [
key,
@ -1094,6 +1098,12 @@ let gPermissions = {
return SitePermissions.UNKNOWN;
},
},
"3rdPartyStorage": {
get disabled() {
return !SitePermissions.statePartitioningPermissionsEnabled;
},
},
},
};
@ -1121,3 +1131,10 @@ XPCOMUtils.defineLazyPreferenceGetter(
true,
SitePermissions.invalidatePermissionList.bind(SitePermissions)
);
XPCOMUtils.defineLazyPreferenceGetter(
SitePermissions,
"statePartitioningPermissionsEnabled",
"browser.contentblocking.state-partitioning.mvp.ui.enabled",
false,
SitePermissions.invalidatePermissionList.bind(SitePermissions)
);

View File

@ -758,17 +758,31 @@ description#identity-popup-content-verifier,
display: none;
}
.identity-popup-permission-item {
.identity-popup-permission-item,
#identity-popup-storage-access-permission-list-header {
padding-inline-end: 8px;
margin-top: 0.25em;
}
#identity-popup-permission-reload-hint,
#identity-popup-permission-empty-hint,
#identity-popup-permission-list:not(:empty) {
#identity-popup-permission-list:not(:empty),
#identity-popup-permission-list:empty + #identity-popup-storage-access-permission-list:not(:empty) {
margin-top: 8px;
}
.identity-popup-permission-list-anchor[anchorfor="3rdPartyStorage"] > vbox:only-child {
display: none;
}
#identity-popup-storage-access-permission-list-hint {
margin-top: 0.25em;
font-size: 0.85em;
/* Matches offset for items - 3px margin + 16px icon + 10px margin */
margin-inline-start: calc(3px + 16px + 10px);
color: var(--panel-description-color);
}
.identity-popup-permission-icon {
margin-inline-start: 3px;
}
@ -794,7 +808,8 @@ description#identity-popup-content-verifier,
margin-inline-start: 1em;
}
.identity-popup-permission-label {
.identity-popup-permission-label,
.identity-popup-permission-header-label {
margin-inline-start: 10px;
}