Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

"use strict";
ChromeUtils.defineESModuleGetters(this, {
ExtensionDNRLimits: "resource://gre/modules/ExtensionDNRLimits.sys.mjs",
});
AddonTestUtils.init(this);
const PREF_DNR_FEEDBACK_DEFAULT_VALUE = Services.prefs.getBoolPref(
"extensions.dnr.feedback",
false
);
// To distinguish from testMatchOutcome, undefined vs resolving vs rejecting.
const kTestMatchOutcomeNotAllowed = "kTestMatchOutcomeNotAllowed";
async function testAvailability({
allowDNRFeedback = false,
testExpectations,
...extensionData
}) {
async function background(testExpectations) {
let {
// declarativeNetRequest should be available if "declarativeNetRequest" or
// "declarativeNetRequestWithHostAccess" permission is requested.
// (and always unavailable when "extensions.dnr.enabled" pref is false)
declarativeNetRequest_available = false,
// testMatchOutcome is available when the "declarativeNetRequestFeedback"
// permission is granted AND the "extensions.dnr.feedback" pref is true.
// (and always unavailable when "extensions.dnr.enabled" pref is false)
// testMatchOutcome_available: true - permission granted + pref true.
// testMatchOutcome_available: false - no permission, pref doesn't matter.
// testMatchOutcome_available: kTestMatchOutcomeNotAllowed - permission
// granted, but pref is false.
testMatchOutcome_available = false,
} = testExpectations;
browser.test.assertEq(
declarativeNetRequest_available,
!!browser.declarativeNetRequest,
"declarativeNetRequest API namespace availability"
);
// Dummy param for testMatchOutcome:
const dummyRequest = { url: "https://example.com/", type: "other" };
if (!testMatchOutcome_available) {
browser.test.assertEq(
undefined,
browser.declarativeNetRequest?.testMatchOutcome,
"declarativeNetRequest.testMatchOutcome availability"
);
} else if (testMatchOutcome_available === "kTestMatchOutcomeNotAllowed") {
await browser.test.assertRejects(
browser.declarativeNetRequest.testMatchOutcome(dummyRequest),
`declarativeNetRequest.testMatchOutcome is only available when the "extensions.dnr.feedback" preference is set to true.`,
"declarativeNetRequest.testMatchOutcome is unavailable"
);
} else {
browser.test.assertDeepEq(
{ matchedRules: [] },
await browser.declarativeNetRequest.testMatchOutcome(dummyRequest),
"declarativeNetRequest.testMatchOutcome is available"
);
}
browser.test.sendMessage("done");
}
let extension = ExtensionTestUtils.loadExtension({
...extensionData,
manifest: {
manifest_version: 3,
...extensionData.manifest,
},
background: `(${background})(${JSON.stringify(testExpectations)});`,
});
Services.prefs.setBoolPref("extensions.dnr.feedback", allowDNRFeedback);
try {
await extension.startup();
await extension.awaitMessage("done");
await extension.unload();
} finally {
Services.prefs.clearUserPref("extensions.dnr.feedback");
}
}
add_setup(async () => {
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
// test_optional_declarativeNetRequestFeedback calls permission.request().
// We don't care about the UI, only about the effect of being granted.
Services.prefs.setBoolPref(
"extensions.webextOptionalPermissionPrompts",
false
);
});
add_task(
{
pref_set: [["extensions.dnr.enabled", false]],
},
async function extensions_dnr_enabled_pref_disabled_dnr_feature() {
let { messages } = await promiseConsoleOutput(async () => {
await testAvailability({
allowDNRFeedback: PREF_DNR_FEEDBACK_DEFAULT_VALUE,
testExpectations: {
declarativeNetRequest_available: false,
},
manifest: {
permissions: [
"declarativeNetRequest",
"declarativeNetRequestFeedback",
"declarativeNetRequestWithHostAccess",
],
},
});
});
AddonTestUtils.checkMessages(messages, {
expected: [
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequest$/,
},
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequestFeedback/,
},
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequestWithHostAccess/,
},
],
});
}
);
add_task(async function dnr_feedback_apis_disabled_by_default() {
let { messages } = await promiseConsoleOutput(async () => {
await testAvailability({
allowDNRFeedback: PREF_DNR_FEEDBACK_DEFAULT_VALUE,
testExpectations: {
declarativeNetRequest_available: true,
testMatchOutcome_available: kTestMatchOutcomeNotAllowed,
},
manifest: {
permissions: [
"declarativeNetRequest",
"declarativeNetRequestFeedback",
"declarativeNetRequestWithHostAccess",
],
},
});
});
AddonTestUtils.checkMessages(messages, {
forbidden: [
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequest$/,
},
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequestFeedback/,
},
{
message:
/Reading manifest: Invalid extension permission: declarativeNetRequestWithHostAccess/,
},
],
});
});
add_task(async function dnr_available_in_mv2() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
declarativeNetRequest_available: true,
testMatchOutcome_available: true,
},
manifest: {
manifest_version: 2,
permissions: [
"declarativeNetRequest",
"declarativeNetRequestFeedback",
"declarativeNetRequestWithHostAccess",
],
},
});
});
add_task(async function with_declarativeNetRequest_permission() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
declarativeNetRequest_available: true,
// feature allowed, but missing declarativeNetRequestFeedback:
testMatchOutcome_available: false,
},
manifest: {
permissions: ["declarativeNetRequest"],
},
});
});
add_task(async function with_declarativeNetRequestWithHostAccess_permission() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
declarativeNetRequest_available: true,
// feature allowed, but missing declarativeNetRequestFeedback:
testMatchOutcome_available: false,
},
manifest: {
permissions: ["declarativeNetRequestWithHostAccess"],
},
});
});
add_task(async function with_all_declarativeNetRequest_permissions() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
declarativeNetRequest_available: true,
// feature allowed, but missing declarativeNetRequestFeedback:
testMatchOutcome_available: false,
},
manifest: {
permissions: [
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess",
],
},
});
});
add_task(async function no_declarativeNetRequest_permission() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
// Just declarativeNetRequestFeedback should not unlock the API.
declarativeNetRequest_available: false,
},
manifest: {
permissions: ["declarativeNetRequestFeedback"],
},
});
});
add_task(async function with_declarativeNetRequestFeedback_permission() {
await testAvailability({
allowDNRFeedback: true,
testExpectations: {
declarativeNetRequest_available: true,
// feature allowed, and all permissions specified:
testMatchOutcome_available: true,
},
manifest: {
permissions: ["declarativeNetRequest", "declarativeNetRequestFeedback"],
},
});
});
add_task(async function declarativeNetRequestFeedback_without_feature() {
await testAvailability({
allowDNRFeedback: false,
testExpectations: {
declarativeNetRequest_available: true,
// all permissions set, but DNR feedback feature not allowed.
testMatchOutcome_available: kTestMatchOutcomeNotAllowed,
},
manifest: {
permissions: ["declarativeNetRequest", "declarativeNetRequestFeedback"],
},
});
});
add_task(
{ pref_set: [["extensions.dnr.feedback", true]] },
async function declarativeNetRequestFeedback_is_optional() {
async function background() {
async function assertTestMatchOutcomeEnabled(expected, description) {
let enabled;
try {
// testAvailability already checks the errors etc, so here we only
// care about the method working vs not working.
await browser.declarativeNetRequest.testMatchOutcome({
type: "other",
});
enabled = true;
} catch (e) {
enabled = false;
}
browser.test.assertEq(expected, enabled, description);
}
await assertTestMatchOutcomeEnabled(false, "disabled when not granted");
await new Promise(resolve => {
// browser.test.withHandlingUserInput would have been simpler, but due
// to bug 1598804 it cannot be used.
browser.test.onMessage.addListener(async msg => {
browser.test.assertEq("withHandlingUserInput_ok", msg, "Resuming");
await browser.permissions.request({
permissions: ["declarativeNetRequestFeedback"],
});
browser.test.sendMessage("withHandlingUserInput_done");
resolve();
});
browser.test.sendMessage("withHandlingUserInput_wanted");
});
await assertTestMatchOutcomeEnabled(true, "enabled by permission");
await browser.permissions.remove({
permissions: ["declarativeNetRequestFeedback"],
});
await assertTestMatchOutcomeEnabled(false, "disabled after perm removal");
browser.test.sendMessage("done");
}
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
manifest_version: 3,
permissions: ["declarativeNetRequestWithHostAccess"],
optional_permissions: ["declarativeNetRequestFeedback"],
},
});
await extension.startup();
await extension.awaitMessage("withHandlingUserInput_wanted");
await withHandlingUserInput(extension, async () => {
extension.sendMessage("withHandlingUserInput_ok");
await extension.awaitMessage("withHandlingUserInput_done");
});
await extension.awaitMessage("done");
await extension.unload();
}
);
add_task(async function test_dnr_limits_namespace_properties() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
permissions: ["declarativeNetRequest"],
},
background() {
browser.test.assertEq(
"_dynamic",
browser.declarativeNetRequest.DYNAMIC_RULESET_ID,
"Value of DYNAMIC_RULESET_ID constant"
);
browser.test.assertEq(
"_session",
browser.declarativeNetRequest.SESSION_RULESET_ID,
"Value of SESSION_RULESET_ID constant"
);
const {
GUARANTEED_MINIMUM_STATIC_RULES,
MAX_NUMBER_OF_STATIC_RULESETS,
MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
MAX_NUMBER_OF_DISABLED_STATIC_RULES,
MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES,
MAX_NUMBER_OF_DYNAMIC_RULES,
MAX_NUMBER_OF_SESSION_RULES,
MAX_NUMBER_OF_REGEX_RULES,
} = browser.declarativeNetRequest;
browser.test.sendMessage("dnr-namespace-properties", {
GUARANTEED_MINIMUM_STATIC_RULES,
MAX_NUMBER_OF_STATIC_RULESETS,
MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
MAX_NUMBER_OF_DISABLED_STATIC_RULES,
MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES,
MAX_NUMBER_OF_DYNAMIC_RULES,
MAX_NUMBER_OF_SESSION_RULES,
MAX_NUMBER_OF_REGEX_RULES,
});
},
});
await extension.startup();
Assert.deepEqual(
await extension.awaitMessage("dnr-namespace-properties"),
{
...ExtensionDNRLimits,
// The following is a deprecated limit and so it is still available to the
// extensions as an API namespace property but it is not part of the limits
// exported by ExtensionDNRLimits and actually enforced internally.
MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES: Math.min(
ExtensionDNRLimits.MAX_NUMBER_OF_DYNAMIC_RULES,
ExtensionDNRLimits.MAX_NUMBER_OF_SESSION_RULES
),
},
"Got the expected limits values set on the dnr namespace"
);
await extension.unload();
});