Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* Any copyright is dedicated to the Public Domain.
"use strict";
const { AddonTestUtils } = ChromeUtils.importESModule(
);
const { PERMISSION_L10N } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPermissionMessages.sys.mjs"
);
AddonTestUtils.initMochitest(this);
add_setup(async () => {
await SpecialPowers.pushPrefEnv({
set: [["extensions.userScripts.mv3.enabled", true]],
});
});
function loadExtensionWithPermissions(addonId) {
function extensionScript() {
browser.test.onMessage.addListener(async (msg, perm) => {
browser.test.assertEq("permissions_request", msg, "Expected msg");
const permissions = [perm];
let granted = await new Promise(resolve => {
browser.test.withHandlingUserInput(() => {
resolve(browser.permissions.request({ permissions }));
});
});
if (perm === "userScripts") {
browser.test.assertEq(
granted,
!!browser.userScripts,
"userScripts API availability matches permission"
);
}
browser.test.sendMessage("permissions_request:done", granted);
});
}
return ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
manifest_version: 3,
optional_permissions: ["userScripts", "webNavigation"],
browser_specific_settings: { gecko: { id: addonId } },
},
files: {
// permissions.request() requires a visible window to attach the prompt
// to, so it must be in a tab instead of a background script.
"tab.html": `<!DOCTYPE html><script src="tab.js"></script>`,
"tab.js": extensionScript,
},
});
}
async function triggerPermRequest(extension, perm) {
let granted;
await BrowserTestUtils.withNewTab({ gBrowser, url }, async () => {
extension.sendMessage("permissions_request", perm);
granted = await extension.awaitMessage("permissions_request:done");
});
return granted;
}
function getUserScriptsCheckbox(panel) {
// popupnotifications has one checkbox (.popup-notification-checkbox) by
// default, but there should not be anything else, other than potentially
// the userscript checkbox.
let checkbox = panel.querySelector(
"checkbox:not(.popup-notification-checkbox)"
);
if (checkbox) {
is(
checkbox.textContent,
PERMISSION_L10N.formatValueSync("webext-perms-description-userScripts"),
"userScripts permission warning is the label of a checkbox"
);
}
return checkbox;
}
function getUserScriptsWarningBar(panel) {
let warningBar = panel.querySelector("moz-message-bar[type='warning']");
if (warningBar) {
is(
warningBar.message,
PERMISSION_L10N.formatValueSync(
"webext-perms-extra-warning-userScripts-short"
),
"Warning should be the extra description for userScripts"
);
}
return warningBar;
}
function assertIsUserScriptsPermissionPrompt(panel) {
let checkbox = getUserScriptsCheckbox(panel);
ok(checkbox, "Found checkbox for userScripts permission");
let warningBar = getUserScriptsWarningBar(panel);
ok(warningBar, "Panel contains warning message");
is(warningBar, checkbox.nextElementSibling, "Warning is below checkbox");
}
function toggleUserScriptCheckbox(panel, { checked, oldChecked = !checked }) {
let checkbox = getUserScriptsCheckbox(panel);
is(checkbox.checked, oldChecked, "Initial checkbox state");
// Simulate user clicking the checkbox to toggle it:
checkbox.click();
is(checkbox.checked, checked, "Checked state should change");
}
add_task(async function test_short_prompt_message() {
// two similar permission messages. The message in the prompt should be
// shorter than the message in about:addons, because the prompt has less
// visual room than about:addons.
// assertIsUserScriptsPermissionPrompt above confirms that this string is
// used by the prompt:
let promptWarning = PERMISSION_L10N.formatValueSync(
"webext-perms-extra-warning-userScripts-short"
);
// browser_html_detail_permissions_userScripts.js confirms that this string
// is used in about:addons:
let aboutaddonsWarning = PERMISSION_L10N.formatValueSync(
"webext-perms-extra-warning-userScripts-long"
);
ok(promptWarning, "Has short warning");
ok(aboutaddonsWarning, "Has long warning");
Assert.greater(
aboutaddonsWarning.length,
promptWarning.length,
"Short warningg should be shorter than the long warning"
);
});
add_task(async function test_userScripts_not_allowed_by_default() {
const addonId = "test@test_userScripts_not_allowed_by_default";
let extension = loadExtensionWithPermissions(addonId);
await extension.startup();
async function verifyUserScriptPromptDeniedByDefault(doToggleBeforeDeny) {
let panelPromise = promisePopupNotificationShown(
"addon-webext-permissions"
);
let resultPromise = triggerPermRequest(extension, "userScripts");
let panel = await panelPromise;
assertIsUserScriptsPermissionPrompt(panel);
ok(panel.button.disabled, "Allow button initially disabled");
panel.button.click();
ok(!panel.hidden, "Should have ignored clicks on a disabled Allow button");
if (doToggleBeforeDeny) {
// Sanity check: Toggling checkbox before deny does not somehow grant
// the permission when the prompt is denied.
toggleUserScriptCheckbox(panel, { checked: true, oldChecked: false });
}
panel.secondaryButton.click();
ok(panel.hidden, "Should be able to Deny the prompt");
is(await resultPromise, false, "Permission should be denied upon Deny");
}
info("Verifying that userScripts permission is denied by default");
await verifyUserScriptPromptDeniedByDefault();
info("Verifying that disabled button does not bleed to unrelated permission");
{
let panelPromise = promisePopupNotificationShown(
"addon-webext-permissions"
);
let resultPromise = triggerPermRequest(extension, "webNavigation");
let panel = await panelPromise;
is(getUserScriptsCheckbox(panel), null, "Panel does not have a checkbox");
is(getUserScriptsWarningBar(panel), null, "Panel does not have a warning");
is(
panel.permsSingleEl.textContent,
PERMISSION_L10N.formatValueSync("webext-perms-description-webNavigation"),
"Regular permission string can be displayed"
);
ok(!panel.button.disabled, "Allow button is not disabled");
panel.button.click();
ok(panel.hidden, "Permission prompt closed upon Allow");
is(await resultPromise, true, "webNavigation permission granted on Allow");
}
info("Verifying that another userScripts prompt still behaves as before");
await verifyUserScriptPromptDeniedByDefault();
// Sanity check: the Allow button should be the only path towards permission
// approval. Checking the checkbox and clicking Deny should still result in
// permission denial.
info("Verifying that Deny button overrides the checked userScripts checkbox");
await verifyUserScriptPromptDeniedByDefault(/* doToggleBeforeDeny */ true);
await extension.unload();
});
add_task(async function test_userScripts_allowed_with_double_confirmation() {
const addonId = "test@test_userScripts_allowed_with_double_confirmation";
let extension = loadExtensionWithPermissions(addonId);
await extension.startup();
let panelPromise = promisePopupNotificationShown("addon-webext-permissions");
let resultPromise = triggerPermRequest(extension, "userScripts");
let panel = await panelPromise;
assertIsUserScriptsPermissionPrompt(panel);
ok(panel.button.disabled, "Allow button initially disabled");
toggleUserScriptCheckbox(panel, { checked: true, oldChecked: false });
ok(!panel.button.disabled, "Allow button enabled after checking checkbox");
toggleUserScriptCheckbox(panel, { checked: false });
ok(panel.button.disabled, "Allow button disabled again after unchecking");
panel.button.click();
ok(!panel.hidden, "Should have ignored clicks on a disabled Allow button");
toggleUserScriptCheckbox(panel, { checked: true });
ok(!panel.button.disabled, "Allow button enabled again after toggling check");
panel.button.click();
ok(panel.hidden, "Should have closed permission prompt on Allow");
is(await resultPromise, true, "userScripts allowed after double prompt");
await extension.unload();
});