mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1911163 - Add full domains list in addon-webext-permissions-notification permissions list. r=desktop-theme-reviewers,fluent-reviewers,willdurand,bolsson,sfoster
Differential Revision: https://phabricator.services.mozilla.com/D218593
This commit is contained in:
parent
9626bd1576
commit
92862ecb60
@ -134,6 +134,26 @@ customElements.define(
|
||||
);
|
||||
}
|
||||
|
||||
get domainsSet() {
|
||||
if (!this.notification?.options?.customElementOptions) {
|
||||
return undefined;
|
||||
}
|
||||
const { strings } = this.notification.options.customElementOptions;
|
||||
return strings.fullDomainsList?.domainsSet;
|
||||
}
|
||||
|
||||
get hasFullDomainsList() {
|
||||
return this.domainsSet?.size;
|
||||
}
|
||||
|
||||
#isFullDomainsListEntryIndex(idx) {
|
||||
if (!this.hasFullDomainsList) {
|
||||
return false;
|
||||
}
|
||||
const { strings } = this.notification.options.customElementOptions;
|
||||
return strings.fullDomainsList.msgIdIndex === idx;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { strings, showIncognitoCheckbox } =
|
||||
this.notification.options.customElementOptions;
|
||||
@ -171,10 +191,17 @@ customElements.define(
|
||||
// (and one for the private browsing checkbox, if it should
|
||||
// be shown) and return earlier.
|
||||
if (this.hasMultiplePermissionsEntries) {
|
||||
for (let msg of strings.msgs) {
|
||||
for (let [idx, msg] of strings.msgs.entries()) {
|
||||
let item = doc.createElementNS(HTML_NS, "li");
|
||||
item.classList.add("webext-perm-granted");
|
||||
item.textContent = msg;
|
||||
if (
|
||||
this.hasFullDomainsList &&
|
||||
this.#isFullDomainsListEntryIndex(idx)
|
||||
) {
|
||||
item.append(this.#createFullDomainsListFragment(msg));
|
||||
} else {
|
||||
item.textContent = msg;
|
||||
}
|
||||
permsListEl.appendChild(item);
|
||||
}
|
||||
if (showIncognitoCheckbox) {
|
||||
@ -203,10 +230,41 @@ customElements.define(
|
||||
return;
|
||||
}
|
||||
|
||||
permsSingleEl.textContent = strings.msgs[0];
|
||||
const msg = strings.msgs[0];
|
||||
if (this.hasFullDomainsList && this.#isFullDomainsListEntryIndex(0)) {
|
||||
permsSingleEl.append(this.#createFullDomainsListFragment(msg));
|
||||
} else {
|
||||
permsSingleEl.textContent = msg;
|
||||
}
|
||||
permsSingleEl.hidden = false;
|
||||
}
|
||||
|
||||
#createFullDomainsListFragment(msg) {
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const doc = this.ownerDocument;
|
||||
const label = doc.createXULElement("label");
|
||||
label.value = msg;
|
||||
const domainsList = doc.createElementNS(HTML_NS, "ul");
|
||||
domainsList.classList.add("webext-perm-domains-list");
|
||||
|
||||
// Enforce max-height and ensure the domains list is
|
||||
// scrollable when there are more than 5 domains.
|
||||
if (this.domainsSet.size > 5) {
|
||||
domainsList.classList.add("scrollable-domains-list");
|
||||
}
|
||||
|
||||
for (const domain of this.domainsSet) {
|
||||
let domainItem = doc.createElementNS(HTML_NS, "li");
|
||||
domainItem.textContent = domain;
|
||||
domainsList.appendChild(domainItem);
|
||||
}
|
||||
const { DocumentFragment } = this.ownerGlobal;
|
||||
const fragment = new DocumentFragment();
|
||||
fragment.append(label);
|
||||
fragment.append(domainsList);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
#clearChildElements() {
|
||||
const { textEl, introEl, permsSingleEl, permsListEl } = this;
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
PERMISSION_L10N: "resource://gre/modules/ExtensionPermissionMessages.sys.mjs",
|
||||
});
|
||||
|
||||
// This test case verifies that `permissions.request()` resolves in the
|
||||
// expected order.
|
||||
add_task(async function test_permissions_prompt() {
|
||||
@ -119,3 +123,202 @@ add_task(async function test_permissions_prompt() {
|
||||
// The extension tabs are automatically closed upon unload.
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// NOTE: more tests covering the full domains list are part of the separate
|
||||
// test case covering the full domains list when this dialog is being used
|
||||
// as the addon install prompt (the test case part of the AOM mochitests
|
||||
// and named testInstallDialogShowsFullDomainsList).
|
||||
add_task(async function testOptionalPermissionsDialogShowsFullDomainsList() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// These are both expected to be the default, but we are setting
|
||||
// them explicitly to make sure this test task is always running
|
||||
// with the prefs set with these values even if we would be
|
||||
// rolling back the pref value temporarily.
|
||||
["extensions.ui.installDialogFullDomains", true],
|
||||
],
|
||||
});
|
||||
// Sanity check.
|
||||
ok(
|
||||
ExtensionsUI.SHOW_FULL_DOMAINS_LIST,
|
||||
"Expect SHOW_FULL_DOMAINS_LIST to be enabled"
|
||||
);
|
||||
|
||||
const createTestExtension = ({
|
||||
id,
|
||||
domainsListLength = 0,
|
||||
optional_permissions = [],
|
||||
}) =>
|
||||
ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
// Set the generated id as a name to make it easier to recognize the test case
|
||||
// from dialog screenshots (e.g. in the screenshot captured when the test hits
|
||||
// a failure).
|
||||
name: id,
|
||||
version: "1.0",
|
||||
browser_specific_settings: {
|
||||
gecko: { id },
|
||||
},
|
||||
optional_permissions: optional_permissions.concat(
|
||||
new Array(domainsListLength).fill("examplehost").map((v, i) => {
|
||||
return `*://${v}${i}.com/*`;
|
||||
})
|
||||
),
|
||||
},
|
||||
files: {
|
||||
"extpage.html": `<!DOCTYPE html><script src="extpage.js"></script>`,
|
||||
"extpage.js"() {
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
if (msg !== "optional-origins:request") {
|
||||
browser.test.fail(`Got unexpected test message ${msg}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const { optional_permissions } = browser.runtime.getManifest();
|
||||
const permissions = optional_permissions.filter(
|
||||
p => !p.startsWith("*://")
|
||||
);
|
||||
const origins = optional_permissions.filter(p =>
|
||||
p.startsWith("*://")
|
||||
);
|
||||
browser.test.withHandlingUserInput(() => {
|
||||
browser.permissions.request({
|
||||
permissions,
|
||||
origins,
|
||||
});
|
||||
browser.test.sendMessage("optional-origins:requested");
|
||||
});
|
||||
});
|
||||
browser.test.sendMessage("extpage:loaded");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const assertNoDomainsList = popupContentEl => {
|
||||
const domainsListEl = popupContentEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(!domainsListEl, "Expect no domains list element to be found");
|
||||
};
|
||||
|
||||
const assertOneDomainPermission = hostPermStringEl => {
|
||||
Assert.equal(
|
||||
hostPermStringEl.textContent,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-one-domain",
|
||||
{
|
||||
domain: "examplehost0.com",
|
||||
}
|
||||
),
|
||||
"Got the expected host permission string on extension with only one granted domain"
|
||||
);
|
||||
};
|
||||
|
||||
const assertMultipleDomainsPermission = (
|
||||
domainsListEl,
|
||||
domainsListLength
|
||||
) => {
|
||||
// The permission string associated to XUL label element can be reached as labelEl.value.
|
||||
Assert.equal(
|
||||
domainsListEl.previousElementSibling.value,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-multiple-domains",
|
||||
{
|
||||
domainCount: domainsListLength,
|
||||
}
|
||||
),
|
||||
`Got the expected host permission string on extension with ${this.domainsListLength} granted domain`
|
||||
);
|
||||
Assert.deepEqual(
|
||||
Array.from(domainsListEl.querySelectorAll("li")).map(
|
||||
el => el.textContent
|
||||
),
|
||||
new Array(domainsListLength)
|
||||
.fill("examplehost")
|
||||
.map((v, i) => `${v}${i}.com`),
|
||||
"Got the expected domains listed in the domains list element"
|
||||
);
|
||||
};
|
||||
|
||||
const TEST_CASES = [
|
||||
{
|
||||
msg: "Test request API permission and no origins",
|
||||
id: "api-and-no-domains@test-ext",
|
||||
optional_permissions: ["history"],
|
||||
domainsListLength: 0,
|
||||
verifyDialog(popupContentEl) {
|
||||
assertNoDomainsList(popupContentEl);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test request access to a single domain",
|
||||
id: "single-domain@test-ext",
|
||||
optional_permissions: [],
|
||||
domainsListLength: 1,
|
||||
verifyDialog(popupContentEl) {
|
||||
assertNoDomainsList(popupContentEl);
|
||||
assertOneDomainPermission(popupContentEl.permsSingleEl);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test request API permission and access to a single domain",
|
||||
id: "api-and-single-domain@test-ext",
|
||||
optional_permissions: ["history"],
|
||||
domainsListLength: 1,
|
||||
verifyDialog(popupContentEl) {
|
||||
assertNoDomainsList(popupContentEl);
|
||||
assertOneDomainPermission(
|
||||
popupContentEl.permsListEl.querySelector("li:first-child")
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test request access to multiple domains",
|
||||
id: "multiple-domains@test-ext",
|
||||
optional_permissions: [],
|
||||
domainsListLength: 10,
|
||||
verifyDialog(popupContentEl) {
|
||||
const domainsListEl = popupContentEl.permsSingleEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(domainsListEl, "Expect domains list element to be found");
|
||||
assertMultipleDomainsPermission(domainsListEl, this.domainsListLength);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test request API permision and access to multiple domains",
|
||||
id: "api-and-multiple-domains@test-ext",
|
||||
optional_permissions: ["history"],
|
||||
domainsListLength: 10,
|
||||
verifyDialog(popupContentEl) {
|
||||
const domainsListEl = popupContentEl.permsListEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(domainsListEl, "Expect domains list element to be found");
|
||||
assertMultipleDomainsPermission(domainsListEl, this.domainsListLength);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of TEST_CASES) {
|
||||
info(testCase.msg);
|
||||
const extension = createTestExtension(testCase);
|
||||
|
||||
await extension.startup();
|
||||
|
||||
let extPageURL = `moz-extension://${extension.uuid}/extpage.html`;
|
||||
|
||||
await BrowserTestUtils.withNewTab(extPageURL, async () => {
|
||||
let promiseRequestDisalog = promisePopupNotificationShown(
|
||||
"addon-webext-permissions"
|
||||
);
|
||||
await extension.awaitMessage("extpage:loaded");
|
||||
extension.sendMessage("optional-origins:request");
|
||||
await extension.awaitMessage("optional-origins:requested");
|
||||
const popupContentEl = await promiseRequestDisalog;
|
||||
testCase.verifyDialog(popupContentEl);
|
||||
});
|
||||
|
||||
await extension.unload();
|
||||
}
|
||||
});
|
||||
|
@ -40,6 +40,13 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
false
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"SHOW_FULL_DOMAINS_LIST",
|
||||
"extensions.ui.installDialogFullDomains",
|
||||
true
|
||||
);
|
||||
|
||||
const DEFAULT_EXTENSION_ICON =
|
||||
"chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
|
||||
@ -65,6 +72,10 @@ export var ExtensionsUI = {
|
||||
|
||||
pendingNotifications: new WeakMap(),
|
||||
|
||||
get SHOW_FULL_DOMAINS_LIST() {
|
||||
return lazy.SHOW_FULL_DOMAINS_LIST;
|
||||
},
|
||||
|
||||
get POSTINSTALL_PRIVATEBROWSING_CHECKBOX() {
|
||||
return lazy.POSTINSTALL_PRIVATEBROWSING_CHECKBOX;
|
||||
},
|
||||
@ -369,9 +380,12 @@ export var ExtensionsUI = {
|
||||
|
||||
// Create a set of formatted strings for a permission prompt
|
||||
_buildStrings(info) {
|
||||
const strings = lazy.ExtensionData.formatPermissionStrings(info, {
|
||||
collapseOrigins: true,
|
||||
});
|
||||
const strings = lazy.ExtensionData.formatPermissionStrings(
|
||||
info,
|
||||
this.SHOW_FULL_DOMAINS_LIST
|
||||
? { fullDomainsList: true }
|
||||
: { collapseOrigins: true }
|
||||
);
|
||||
strings.addonName = info.addon.name;
|
||||
return strings;
|
||||
},
|
||||
|
@ -21,6 +21,12 @@ html|*.addon-webext-perm-list {
|
||||
> html|li {
|
||||
list-style: none;
|
||||
|
||||
&.webext-perm-granted {
|
||||
/* NOTE: Insert line breaks on long permission strings (or domain name included
|
||||
* in the localized string that ends up be overflowing */
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* style the permissions list items that are not editable to use the check.svg image */
|
||||
&.webext-perm-granted::before {
|
||||
content: "";
|
||||
@ -40,8 +46,39 @@ html|*.addon-webext-perm-list {
|
||||
}
|
||||
}
|
||||
|
||||
html|ul.webext-perm-domains-list {
|
||||
--domains-list-border-color: var(--border-color-deemphasized);
|
||||
--domains-list-text-color: var(--text-color-deemphasized);
|
||||
|
||||
border: var(--border-width) solid var(--domains-list-border-color);
|
||||
border-radius: var(--border-radius-small);
|
||||
margin-block: var(--space-small);
|
||||
margin-inline: calc(var(--size-item-small) + var(--space-small)) 0;
|
||||
padding-block: var(--space-xsmall);
|
||||
padding-inline-start: var(--size-item-medium);
|
||||
|
||||
max-width: 80vw;
|
||||
overflow: auto;
|
||||
|
||||
/* max-height is set when the domains list is longer than 5 domains
|
||||
* to force the domains list to become scrollable. */
|
||||
&.scrollable-domains-list {
|
||||
max-height: 5.5lh;
|
||||
}
|
||||
|
||||
> html|li {
|
||||
list-style: disc;
|
||||
color: var(--domains-list-text-color);
|
||||
/* NOTE: Insert line breaks anywhere in long domain names that would be overflowing */
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-webext-perm-single-entry {
|
||||
margin-top: 11px;
|
||||
/* NOTE: Insert line breaks on long permission strings (or domain name included
|
||||
* in the localized string that ends up be overflowing */
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.addon-webext-perm-text,
|
||||
|
@ -2448,6 +2448,9 @@ export class ExtensionData {
|
||||
* @param {boolean} [options.buildOptionalOrigins]
|
||||
* Wether to build optional origins Maps for permission
|
||||
* controls. Defaults to false.
|
||||
* @param {boolean} [options.fullDomainsList]
|
||||
* Wether to include the full domains set in the returned
|
||||
* results. Defaults to false.
|
||||
*
|
||||
* @returns {object} An object with properties containing localized strings
|
||||
* for various elements of a permission dialog. The "header"
|
||||
@ -2463,6 +2466,14 @@ export class ExtensionData {
|
||||
* "object.optionalOrigins" is a map of a host permission to localized strings
|
||||
* describing the host permission, where appropriate. Currently only
|
||||
* all url style permissions are included.
|
||||
*
|
||||
* "object.fullDomainsList" is an object with a Set of the
|
||||
* full domains list (with the property name "domainsSet")
|
||||
* and the index of the corresponding message string (with
|
||||
* the property name "msgIdIndex"). This property is
|
||||
* expected to be set only if "options.fullDomainsList" is
|
||||
* passed as true and the extension doesn't include
|
||||
* allUrls origin permissions.
|
||||
*/
|
||||
static formatPermissionStrings(
|
||||
{
|
||||
@ -2474,7 +2485,11 @@ export class ExtensionData {
|
||||
type,
|
||||
unsigned,
|
||||
},
|
||||
{ collapseOrigins = false, buildOptionalOrigins = false } = {}
|
||||
{
|
||||
collapseOrigins = false,
|
||||
buildOptionalOrigins = false,
|
||||
fullDomainsList = false,
|
||||
} = {}
|
||||
) {
|
||||
const l10n = lazy.PERMISSION_L10N;
|
||||
|
||||
@ -2596,7 +2611,7 @@ export class ExtensionData {
|
||||
// first, then individual host permissions.
|
||||
if (allUrls) {
|
||||
msgIds.push("webext-perms-host-description-all-urls");
|
||||
} else {
|
||||
} else if (!fullDomainsList) {
|
||||
// Formats a list of host permissions. If we have 4 or fewer, display
|
||||
// them all, otherwise display the first 3 followed by an item that
|
||||
// says "...plus N others"
|
||||
@ -2628,6 +2643,29 @@ export class ExtensionData {
|
||||
);
|
||||
}
|
||||
|
||||
if (!allUrls && fullDomainsList) {
|
||||
const allHostPermissions = wildcards.union(sites);
|
||||
if (allHostPermissions.size > 1) {
|
||||
msgIds.push({
|
||||
id: "webext-perms-host-description-multiple-domains",
|
||||
args: {
|
||||
domainCount: allHostPermissions.size,
|
||||
},
|
||||
});
|
||||
result.fullDomainsList = {
|
||||
domainsSet: allHostPermissions,
|
||||
msgIdIndex: msgIds.length - 1,
|
||||
};
|
||||
} else if (allHostPermissions.size) {
|
||||
msgIds.push({
|
||||
id: "webext-perms-host-description-one-domain",
|
||||
args: {
|
||||
domain: Array.from(allHostPermissions)[0],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, show remaining permissions, in the same order as AMO.
|
||||
// The permissions are sorted alphabetically by the permission
|
||||
// string to match AMO.
|
||||
|
@ -76,6 +76,22 @@ webext-perms-host-description-too-many-sites =
|
||||
*[other] Access your data on { $domainCount } other sites
|
||||
}
|
||||
|
||||
# Variables:
|
||||
# $domain (String): will be replaced by the DNS host name for which a webextension is requesting access (e.g., mozilla.org),
|
||||
# $domain should be treated as plural (because it may also include all subdomains, e.g www.mozilla.org, ftp.mozilla.org).
|
||||
webext-perms-host-description-one-domain = Access your data for sites in { $domain } domains
|
||||
|
||||
# Permission string used for webextensions requesting access to 2 or more domains (and so $domainCount is expected to always
|
||||
# be >= 2, for webextensions requesting access to only one domain the `webext-perms-host-description-one-domain` string is
|
||||
# used instead).
|
||||
# Variables:
|
||||
# $domainCount (Number): Integer indicating the number of websites domains for which this webextension is requesting permission
|
||||
# (the list of domains will follow this string).
|
||||
webext-perms-host-description-multiple-domains =
|
||||
{ $domainCount ->
|
||||
*[other] Access your data for sites in { $domainCount } domains
|
||||
}
|
||||
|
||||
## Headers used in the webextension permissions dialog for synthetic add-ons.
|
||||
## The part of the string describing what privileges the extension gives should be consistent
|
||||
## with the value of webext-site-perms-description-gated-perms-{sitePermission}.
|
||||
|
@ -12,6 +12,10 @@ const ADDON_ID = "addon1@test.mozilla.org";
|
||||
const CUSTOM_THEME_ID = "theme1@test.mozilla.org";
|
||||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
PERMISSION_L10N: "resource://gre/modules/ExtensionPermissionMessages.sys.mjs",
|
||||
});
|
||||
|
||||
AddonTestUtils.initMochitest(this);
|
||||
|
||||
function assertDisabledSideloadedExtensionElement(managerWindow, addonElement) {
|
||||
@ -176,3 +180,450 @@ add_task(async function test_sideloaded_extension_permissions_prompt() {
|
||||
await close_manager(manager);
|
||||
await addon.uninstall();
|
||||
});
|
||||
|
||||
add_task(async function testInstallDialogShowsFullDomainsList() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// These are both expected to be the default, but we are setting
|
||||
// them explicitly to make sure this test task is always running
|
||||
// with the prefs set with these values even if we would be
|
||||
// rolling back the pref value temporarily.
|
||||
["extensions.ui.installDialogFullDomains", true],
|
||||
["extensions.ui.postInstallPrivateBrowsingCheckbox", false],
|
||||
],
|
||||
});
|
||||
// Sanity check.
|
||||
ok(
|
||||
ExtensionsUI.SHOW_FULL_DOMAINS_LIST,
|
||||
"Expect SHOW_FULL_DOMAINS_LIST to be enabled"
|
||||
);
|
||||
ok(
|
||||
!ExtensionsUI.POSTINSTALL_PRIVATEBROWSING_CHECKBOX,
|
||||
"Expect POSTINSTALL_PRIVATEBROWSING_CHECKBOX to be disabled"
|
||||
);
|
||||
|
||||
const createTestExtensionXPI = ({
|
||||
id,
|
||||
domainsListLength = 0,
|
||||
permissions = [],
|
||||
incognito = "spanning",
|
||||
}) =>
|
||||
AddonTestUtils.createTempWebExtensionFile({
|
||||
manifest: {
|
||||
// Set the generated id as a name to make it easier to recognize the test case
|
||||
// from dialog screenshots (e.g. in the screenshot captured when the test hits
|
||||
// a failure).
|
||||
name: id,
|
||||
version: "1.0",
|
||||
browser_specific_settings: {
|
||||
gecko: { id },
|
||||
},
|
||||
incognito,
|
||||
permissions: permissions.concat(
|
||||
new Array(domainsListLength).fill("examplehost").map((v, i) => {
|
||||
return `*://${v}${i}.com/*`;
|
||||
})
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const LONG_DOMAIN_NAME = `averylongdomainname.${new Array(40)
|
||||
.fill("x")
|
||||
.join("")}.com`;
|
||||
|
||||
const assertPermsElVisibility = (popupContentEl, noIncognitoCheckbox) => {
|
||||
// We expect the host permissions entry to be the only entry to be shown
|
||||
// if the incognito checkbox isn't expected to be visible for the test
|
||||
// extension (because the test extension doesn't request any other
|
||||
// permission and each test case is executed with and without opting-out
|
||||
// of the private browsing access).
|
||||
Assert.equal(
|
||||
BrowserTestUtils.isHidden(popupContentEl.permsListEl),
|
||||
noIncognitoCheckbox,
|
||||
`Expect the permissions list element to be ${
|
||||
noIncognitoCheckbox ? "hidden" : "visible"
|
||||
}`
|
||||
);
|
||||
Assert.equal(
|
||||
BrowserTestUtils.isVisible(popupContentEl.permsSingleEl),
|
||||
noIncognitoCheckbox,
|
||||
`Expect the single permission element to be ${
|
||||
noIncognitoCheckbox ? "visible" : "hidden"
|
||||
}`
|
||||
);
|
||||
};
|
||||
|
||||
const assertNoDomainsList = popupContentEl => {
|
||||
const domainsListEl = popupContentEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(!domainsListEl, "Expect no domain list element to be found");
|
||||
};
|
||||
|
||||
const TEST_CASES = [
|
||||
{
|
||||
msg: "Test install extension with no host permissions",
|
||||
id: "no-domains",
|
||||
domainsListLength: 0,
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertNoDomainsList(popupContentEl);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isHidden(popupContentEl.permsListEl),
|
||||
`Expect the permissions list element to be hidden`
|
||||
);
|
||||
Assert.equal(
|
||||
BrowserTestUtils.isHidden(popupContentEl.permsSingleEl),
|
||||
noIncognitoCheckbox,
|
||||
`Expect the permissions list element to be ${
|
||||
noIncognitoCheckbox ? "hidden" : "visible"
|
||||
}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test install extension with one domain listed in host permissions",
|
||||
id: "one-domain",
|
||||
domainsListLength: 1,
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
assertNoDomainsList(popupContentEl);
|
||||
const hostPermStringEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl
|
||||
: popupContentEl.permsListEl.querySelector("li.webext-perm-granted");
|
||||
Assert.ok(
|
||||
hostPermStringEl,
|
||||
"Expect one granted permission string element"
|
||||
);
|
||||
Assert.equal(
|
||||
hostPermStringEl.textContent,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-one-domain",
|
||||
{
|
||||
domain: "examplehost0.com",
|
||||
}
|
||||
),
|
||||
"Got the expected host permission string on extension with only one granted domain"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isVisible(hostPermStringEl),
|
||||
"Expect the host permission string to be visible"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test install extension with less than 6 domains listed in host permissions",
|
||||
id: "few-domains",
|
||||
domainsListLength: 5,
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const domainsListEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
)
|
||||
: popupContentEl.permsListEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
domainsListEl,
|
||||
"Expect domains list element to be found inside the permission list element"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isVisible(domainsListEl),
|
||||
"Expect the domains list element to be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
domainsListEl.scrollTopMax,
|
||||
0,
|
||||
"Expect domains list to not be scrollable (chromeOnly scrollTopMax set to 0)"
|
||||
);
|
||||
// The permission string associated to XUL label element can be reached as labelEl.value.
|
||||
Assert.equal(
|
||||
domainsListEl.previousElementSibling.value,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-multiple-domains",
|
||||
{
|
||||
domainCount: this.domainsListLength,
|
||||
}
|
||||
),
|
||||
`Got the expected host permission string on extension with ${this.domainsListLength} granted domain`
|
||||
);
|
||||
Assert.deepEqual(
|
||||
Array.from(domainsListEl.querySelectorAll("li")).map(
|
||||
el => el.textContent
|
||||
),
|
||||
new Array(this.domainsListLength)
|
||||
.fill("examplehost")
|
||||
.map((v, i) => `${v}${i}.com`),
|
||||
"Got the expected domains listed in the domains list element"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test install extension with many domains listed in host permissions",
|
||||
id: "many-domains",
|
||||
domainsListLength: 20,
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const domainsListEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
)
|
||||
: popupContentEl.permsListEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
domainsListEl,
|
||||
"Expect domains list element to be found inside the permission list element"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isVisible(domainsListEl),
|
||||
"Expect the domains list element to be visible"
|
||||
);
|
||||
Assert.greater(
|
||||
domainsListEl.scrollTopMax,
|
||||
domainsListEl.clientHeight,
|
||||
"Expect domains list to be scrollable (chromeOnly scrollTopMax greater than clientHeight)"
|
||||
);
|
||||
// The permission string associated to XUL label element can be reached as labelEl.value.
|
||||
Assert.equal(
|
||||
domainsListEl.previousElementSibling.value,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-multiple-domains",
|
||||
{
|
||||
domainCount: this.domainsListLength,
|
||||
}
|
||||
),
|
||||
`Got the expected host permission string on extension with ${this.domainsListLength} granted domain`
|
||||
);
|
||||
Assert.deepEqual(
|
||||
Array.from(domainsListEl.querySelectorAll("li")).map(
|
||||
el => el.textContent
|
||||
),
|
||||
new Array(this.domainsListLength)
|
||||
.fill("examplehost")
|
||||
.map((v, i) => `${v}${i}.com`),
|
||||
"Got the expected domains listed in the domains list element"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test text wrapping on a single long domain name",
|
||||
id: "one-long-domain",
|
||||
domainsListLength: 0,
|
||||
permissions: [`*://${LONG_DOMAIN_NAME}/*`],
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const hostPermStringEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl
|
||||
: popupContentEl.permsListEl.querySelector("li.webext-perm-granted");
|
||||
Assert.equal(
|
||||
hostPermStringEl.childNodes[0].nodeType,
|
||||
hostPermStringEl.TEXT_NODE,
|
||||
"Expect to have host permission element child to be a text node"
|
||||
);
|
||||
Assert.equal(
|
||||
hostPermStringEl.childNodes[0].nodeValue,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-one-domain",
|
||||
{
|
||||
domain: LONG_DOMAIN_NAME,
|
||||
}
|
||||
),
|
||||
"Got the expected host permission string set as the nextNode value"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
// Calling getBoxQuads on the text node is expected to be returning
|
||||
// one DOMQuad instance for each line the text node has been broken
|
||||
// into (e.g. 3 DOMQuad instances when the long domain name forces
|
||||
// the text node to be broken over 3 lines).
|
||||
//
|
||||
// This check is asserting that none of the lines the text node has
|
||||
// been broken into has a width larger than the width of the parent
|
||||
// element.
|
||||
//
|
||||
// NOTE: this assertion is expected to hit a failure if .webext-perm-granted
|
||||
// or .addon-webext-perm-single-entry elements are missing the overflow-wrap
|
||||
// CSS rule.
|
||||
hostPermStringEl.childNodes[0]
|
||||
.getBoxQuads()
|
||||
.map(quad => quad.getBounds().width)
|
||||
.filter(width => {
|
||||
return width > hostPermStringEl.getBoundingClientRect().width;
|
||||
}),
|
||||
[],
|
||||
"The host permission text node should NOT overflow the parent element"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test text wrapping on long domain name in domains list",
|
||||
id: "one-long-domain-in-domains-list",
|
||||
domainsListLength: 10,
|
||||
permissions: [`*://${LONG_DOMAIN_NAME}/*`],
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const domainsListEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
)
|
||||
: popupContentEl.permsListEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(
|
||||
domainsListEl,
|
||||
"Expect domains list element to be found inside the permission list element"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isVisible(domainsListEl),
|
||||
"Expect the domains list element to be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
domainsListEl.firstElementChild.childNodes[0].nodeType,
|
||||
domainsListEl.TEXT_NODE,
|
||||
"Found text node for the long domain name item part of the domain list item"
|
||||
);
|
||||
Assert.equal(
|
||||
domainsListEl.firstElementChild.childNodes[0].nodeValue,
|
||||
LONG_DOMAIN_NAME,
|
||||
"Got the expected domain name set on the first domain list item"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
// This check is asserting that none of the lines the text node has
|
||||
// been broken into has a width larger than the width of the parent
|
||||
// element.
|
||||
//
|
||||
// NOTE: this assertion is expected to hit a failure if .webext-perm-domains-list
|
||||
// list items elements are overflowing (e.g. if it is not inheriting the
|
||||
// overflow-wrap CSS rule from its ascending notes and doesn't have one set on
|
||||
// its own).
|
||||
domainsListEl.firstElementChild.childNodes[0]
|
||||
.getBoxQuads()
|
||||
.map(quad => quad.getBounds().width)
|
||||
.filter(width => {
|
||||
return (
|
||||
width >
|
||||
domainsListEl.firstElementChild.getBoundingClientRect().width
|
||||
);
|
||||
}),
|
||||
[],
|
||||
"The domain name text node should NOT overflow the parent element"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test wildcard subdomains shown as single host permission",
|
||||
id: "with-wildcard-subdomains",
|
||||
domainsListLength: 0,
|
||||
permissions: ["*://*.example.com/*", "*://example.com/*"],
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const hostPermStringEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl
|
||||
: popupContentEl.permsListEl.querySelector("li.webext-perm-granted");
|
||||
Assert.equal(
|
||||
hostPermStringEl.textContent,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-one-domain",
|
||||
{
|
||||
domain: "example.com",
|
||||
}
|
||||
),
|
||||
"Expected *.example.com and example.com host permissions to be reported as a single domain permission string"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
msg: "Test wildcard subdomains in domains list",
|
||||
id: "with-wildcard-subdomains",
|
||||
domainsListLength: 0,
|
||||
permissions: [
|
||||
"*://*.example.com/*",
|
||||
"*://example.com/*`",
|
||||
"*://*.example.org/*",
|
||||
"*://example.org/*",
|
||||
],
|
||||
verifyDialog(popupContentEl, noIncognitoCheckbox) {
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
assertPermsElVisibility(popupContentEl, noIncognitoCheckbox);
|
||||
const domainsListEl = noIncognitoCheckbox
|
||||
? popupContentEl.permsSingleEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
)
|
||||
: popupContentEl.permsListEl.querySelector(
|
||||
".webext-perm-domains-list"
|
||||
);
|
||||
Assert.ok(
|
||||
domainsListEl,
|
||||
"Expect domains list element to be found inside the permission list element"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.isVisible(domainsListEl),
|
||||
"Expect the domains list element to be visible"
|
||||
);
|
||||
// Expect the domains list to only include 2 domains and the host permissions string
|
||||
// to reflect that as well.
|
||||
Assert.deepEqual(
|
||||
Array.from(domainsListEl.querySelectorAll("li")).map(
|
||||
el => el.textContent
|
||||
),
|
||||
["example.com", "example.org"],
|
||||
"Got the expected domains listed in the domains list element"
|
||||
);
|
||||
Assert.equal(
|
||||
domainsListEl.previousElementSibling.value,
|
||||
PERMISSION_L10N.formatValueSync(
|
||||
"webext-perms-host-description-multiple-domains",
|
||||
{
|
||||
domainCount: 2,
|
||||
}
|
||||
),
|
||||
"Got the expected host permission string on extension with 2 granted domain"
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of TEST_CASES) {
|
||||
// Repeat each test without and without the private browsing checkbox
|
||||
// (to test the dialog when the host permissions is expected to be part of a list
|
||||
// or to be the only permissions listed in the dialog).
|
||||
for (const incognito of ["spanning", "not_allowed"]) {
|
||||
const noIncognitoCheckbox = incognito === "not_allowed";
|
||||
info(
|
||||
`${testCase.msg} ${
|
||||
noIncognitoCheckbox ? "and no other permissions" : ""
|
||||
}`
|
||||
);
|
||||
const xpi = createTestExtensionXPI({
|
||||
...testCase,
|
||||
id: `${testCase.id}-with-incognito-${incognito}@test-ext`,
|
||||
incognito,
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async () => {
|
||||
const dialogPromise = promisePopupNotificationShown(
|
||||
"addon-webext-permissions"
|
||||
);
|
||||
|
||||
gURLBar.value = xpi.path;
|
||||
gURLBar.focus();
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
const popupContentEl = await dialogPromise;
|
||||
|
||||
testCase.verifyDialog(popupContentEl, noIncognitoCheckbox);
|
||||
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(
|
||||
window.PopupNotifications.panel,
|
||||
"popuphidden"
|
||||
);
|
||||
// hide the panel (this simulates the user dismissing it)
|
||||
popupContentEl.closest("panel").hidePopup();
|
||||
await popupHiddenPromise;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user