Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: toolkit/components/ipprotection/tests/xpcshell/xpcshell.toml
/* 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
"use strict";
const { IPPPermissionRuleProvider } = ChromeUtils.importESModule(
"moz-src:///toolkit/components/ipprotection/IPPSiteRuleProviders.sys.mjs"
);
const PERM_NAME = "ipp-vpn";
const makePrincipal = origin =>
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
registerCleanupFunction(() => {
Services.perms.removeByType(PERM_NAME);
});
/**
* Runs body with an inited provider, and leaves no ipp-vpn permissions behind
* so the next task starts from a clean store.
*
* @param {Function} body
*/
async function withProvider(body) {
Services.perms.removeByType(PERM_NAME);
const provider = new IPPPermissionRuleProvider();
provider.init();
try {
await body(provider);
} finally {
provider.uninit();
Services.perms.removeByType(PERM_NAME);
}
}
/**
* Rules round-trip through the ipp-vpn permission type: an exclusion is a DENY
* permission, an inclusion is an ALLOW one, and DEFAULT removes the entry
* rather than storing a third state.
*/
add_task(async function test_rules_map_to_permission_capabilities() {
await withProvider(provider => {
provider.setRule(principal, IPPPrincipalRules.EXCLUDED);
Assert.equal(
provider.getPermissionObject(principal)?.capability,
Ci.nsIPermissionManager.DENY_ACTION,
"an exclusion is stored as DENY"
);
Assert.equal(
provider.getRule(principal),
IPPPrincipalRules.EXCLUDED,
"DENY reads back as EXCLUDED"
);
provider.setRule(principal, IPPPrincipalRules.INCLUDED);
Assert.equal(
provider.getPermissionObject(principal)?.capability,
Ci.nsIPermissionManager.ALLOW_ACTION,
"an inclusion is stored as ALLOW"
);
Assert.equal(
provider.getRule(principal),
IPPPrincipalRules.INCLUDED,
"ALLOW reads back as INCLUDED"
);
Assert.equal(
Services.perms.getAllByTypes([PERM_NAME]).length,
1,
"the exclusion was replaced, not stacked"
);
provider.setRule(principal, IPPPrincipalRules.DEFAULT);
Assert.ok(
!provider.getPermissionObject(principal),
"DEFAULT removes the permission"
);
Assert.equal(
provider.getRule(principal),
IPPPrincipalRules.DEFAULT,
"a site with no permission has no stored rule"
);
});
});
/**
* Rules are scoped to the exact host, not the base domain, so entering
* example.com in the preferences dialog does not silently cover
*/
add_task(async function test_rules_are_scoped_to_the_exact_host() {
await withProvider(provider => {
provider.setRule(bare, IPPPrincipalRules.EXCLUDED);
Assert.equal(
provider.getRule(bare),
IPPPrincipalRules.EXCLUDED,
"example.com is excluded"
);
Assert.equal(
provider.getRule(www),
IPPPrincipalRules.DEFAULT,
"www.example.com is untouched"
);
});
});
/**
* A store update tells the manager, including when a rule is cleared, so the
* UI can react to a site being removed as well as added.
*/
add_task(async function test_writes_notify() {
await withProvider(async provider => {
let changed = waitForEvent(provider, "change");
provider.setRule(principal, IPPPrincipalRules.EXCLUDED);
await changed;
Assert.ok(true, "adding a rule notifies");
changed = waitForEvent(provider, "change");
provider.setRule(principal, IPPPrincipalRules.DEFAULT);
await changed;
Assert.ok(true, "clearing a rule notifies");
});
});
/**
* A write that would not change anything is dropped, so the UI does not get
* woken up for a no-op.
*/
add_task(async function test_redundant_write_is_dropped() {
await withProvider(provider => {
provider.setRule(principal, IPPPrincipalRules.EXCLUDED);
let seen = 0;
provider.addEventListener("change", () => seen++);
provider.setRule(principal, IPPPrincipalRules.EXCLUDED);
Assert.equal(seen, 0, "re-setting the same rule does not notify");
});
});
/**
* count reports each rule separately, so the UI can show how many sites the
* user has excluded without also counting the ones they included.
*/
add_task(async function test_count_is_per_rule() {
await withProvider(provider => {
Assert.equal(
provider.count(IPPPrincipalRules.INCLUDED),
0,
"no inclusions to start with"
);
provider.setRule(
IPPPrincipalRules.EXCLUDED
);
provider.setRule(included, IPPPrincipalRules.INCLUDED);
Assert.equal(
provider.count(IPPPrincipalRules.INCLUDED),
1,
"the inclusion is counted"
);
Assert.equal(
provider.count(IPPPrincipalRules.EXCLUDED),
1,
"the exclusion is counted separately"
);
provider.setRule(included, IPPPrincipalRules.DEFAULT);
Assert.equal(
provider.count(IPPPrincipalRules.INCLUDED),
0,
"removing the inclusion drops the count"
);
});
});
/**
* This is the writable provider, so it accepts any principal it can hang a
* permission on. Whether the write is worth making is the manager's call,
* since only the manager knows what would mask it.
*/
add_task(async function test_canSet_needs_only_a_principal() {
await withProvider(provider => {
Assert.ok(
"a content principal can be written"
);
Assert.ok(!provider.canSet(null), "a missing principal cannot");
});
});
/**
* uninit drops the perm-changed observer, so a torn-down provider stops
* notifying.
*/
add_task(async function test_uninit_stops_observing() {
const provider = new IPPPermissionRuleProvider();
provider.init();
provider.uninit();
let seen = 0;
provider.addEventListener("change", () => seen++);
provider.setRule(principal, IPPPrincipalRules.EXCLUDED);
Assert.equal(seen, 0, "an uninited provider does not notify");
Services.perms.removeByType(PERM_NAME);
});