Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: appname == 'thunderbird' OR os == 'android'
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell-legacy-ep.toml
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell-remote.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
"use strict";
const { ExtensionPermissions } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPermissions.sys.mjs"
);
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"1",
"42"
);
add_task(async function setup() {
// storage mode will run in xpcshell-legacy-ep.toml
await ExtensionPermissions._uninit();
Services.prefs.setBoolPref(
"extensions.webextOptionalPermissionPrompts",
false
);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts");
});
await AddonTestUtils.promiseStartupManager();
AddonTestUtils.usePrivilegedSignatures = false;
});
async function test_migrated_permission_to_optional({ manifest_version }) {
let id = "permission-upgrade@test";
let extensionData = {
manifest: {
version: "1.0",
browser_specific_settings: { gecko: { id } },
permissions: [
"webRequest",
"tabs",
],
},
useAddonManager: "permanent",
};
function checkPermissions() {
let policy = WebExtensionPolicy.getByID(id);
ok(policy.hasPermission("webRequest"), "addon has webRequest permission");
ok(policy.hasPermission("tabs"), "addon has tabs permission");
ok(
"addon has example.net host permission"
);
ok(
"addon has example.com host permission"
);
ok(
"addon does not have other.com host permission"
);
}
let extension = ExtensionTestUtils.loadExtension(extensionData);
await extension.startup();
checkPermissions();
extensionData.manifest.manifest_version = manifest_version;
// Move to using optional permission
extensionData.manifest.version = "2.0";
extensionData.manifest.permissions = ["tabs"];
// The ExtensionTestCommon.generateFiles() test helper will normalize (move)
// host permissions into the `permissions` key for the MV2 test.
extensionData.manifest.optional_permissions = [
"webRequest",
];
// Restart the addon manager to flush the AddonInternal instance created
await AddonTestUtils.promiseRestartManager();
await extension.upgrade(extensionData);
equal(extension.version, "2.0", "Expected extension version");
checkPermissions();
await extension.unload();
}
add_task(function test_migrated_permission_to_optional_mv2() {
return test_migrated_permission_to_optional({ manifest_version: 2 });
});
// Test migration of mv2 (required) to mv3 (optional) host permissions.
add_task(function test_migrated_permission_to_optional_mv3() {
return test_migrated_permission_to_optional({ manifest_version: 3 });
});
// This tests that settings are removed if a required permission is removed.
// We use two settings APIs to make sure the one we keep permission to is not
// removed inadvertantly.
add_task(async function test_required_permissions_removed() {
function cacheIsEnabled() {
return (
Services.prefs.getBoolPref("browser.cache.disk.enable") &&
Services.prefs.getBoolPref("browser.cache.memory.enable")
);
}
let extData = {
background() {
if (browser.browserSettings) {
browser.browserSettings.cacheEnabled.set({ value: false });
}
browser.privacy.services.passwordSavingEnabled.set({ value: false });
},
manifest: {
browser_specific_settings: { gecko: { id: "pref-test@test" } },
},
useAddonManager: "permanent",
};
let extension = ExtensionTestUtils.loadExtension(extData);
ok(
Services.prefs.getBoolPref("signon.rememberSignons"),
"privacy setting intial value as expected"
);
await extension.startup();
ok(!cacheIsEnabled(), "setting is set after startup");
extData.manifest.permissions = ["tabs"];
extData.manifest.optional_permissions = ["privacy"];
await extension.upgrade(extData);
ok(cacheIsEnabled(), "setting is reset after upgrade");
ok(
!Services.prefs.getBoolPref("signon.rememberSignons"),
"privacy setting is still set after upgrade"
);
await extension.unload();
});
// This separate test task covers ExtensionData.migratePermissions behaviors
// under some additional corner cases (e.g. updating an addon that was installed
// before support for the data collection permission was introduced)
add_task(async function test_migratePermissions_on_missing_data_collection() {
const { ExtensionData } = ChromeUtils.importESModule(
"resource://gre/modules/Extension.sys.mjs"
);
const { ExtensionPermissions } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPermissions.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
);
let sandbox = sinon.createSandbox();
let removeStub = sandbox.stub(ExtensionPermissions, "remove").resolves();
const emptyPerms = { permissions: [], origins: [] };
const testCases = [
{
description: "old permissions missing data_collection property",
id: "test@ext",
oldPermissions: { ...emptyPerms },
oldOptionalPermissions: { ...emptyPerms },
newPermissions: { ...emptyPerms, data_collection: ["locationInfo"] },
newOptionalPermissions: {
...emptyPerms,
data_collection: ["healthInfo"],
},
expectedRemovedPermissions: { ...emptyPerms, data_collection: [] },
},
{
description: "new permissions missing data_collection property",
id: "test@ext",
oldPermissions: { ...emptyPerms, data_collection: ["locationInfo"] },
oldOptionalPermissions: {
...emptyPerms,
data_collection: ["healthInfo"],
},
newPermissions: { ...emptyPerms },
newOptionalPermissions: { ...emptyPerms },
expectedRemovedPermissions: {
...emptyPerms,
data_collection: ["locationInfo", "healthInfo"],
},
},
];
for (const testCase of testCases) {
removeStub.resetHistory();
await ExtensionData.migratePermissions(
testCase.id,
testCase.oldPermissions,
testCase.oldOptionalPermissions,
testCase.newPermissions,
testCase.newOptionalPermissions
);
Assert.deepEqual(
removeStub.firstCall.args,
[testCase.id, testCase.expectedRemovedPermissions],
"ExtensionPermissions.remove call got the expected arguments"
);
ok(
removeStub.calledOnce,
"Expect ExtensionPermissions to have been called once"
);
}
sandbox.restore();
});
// This tests that settings are removed if a granted permission is removed.
// We use two settings APIs to make sure the one we keep permission to is not
// removed inadvertantly.
add_task(
{
pref_set: [["extensions.dataCollectionPermissions.enabled", true]],
},
async function test_granted_permissions_removed() {
function cacheIsEnabled() {
return (
Services.prefs.getBoolPref("browser.cache.disk.enable") &&
Services.prefs.getBoolPref("browser.cache.memory.enable")
);
}
let extData = {
async background() {
browser.test.onMessage.addListener(async (msg, args) => {
let result;
if (msg === "permissions-request") {
await browser.permissions.request({
permissions: args.permissions,
origins: [],
data_collection: args.data_collection,
});
if (browser.browserSettings) {
browser.browserSettings.cacheEnabled.set({ value: false });
}
browser.privacy.services.passwordSavingEnabled.set({
value: false,
});
} else if (msg === "permissions-getAll") {
result = await browser.permissions.getAll();
} else {
browser.test.fail(`Got unexpected test message ${msg}`);
}
browser.test.sendMessage(`${msg}:done`, result);
});
},
// "tabs" is never granted, it is included to exercise the removal code
// that called during the upgrade.
manifest: {
version: "1.0",
browser_specific_settings: {
gecko: {
id: "pref-test@test",
data_collection_permissions: {
optional: ["technicalAndInteraction", "locationInfo"],
},
},
},
optional_permissions: [
"tabs",
"browserSettings",
"privacy",
],
},
useAddonManager: "permanent",
};
let extension = ExtensionTestUtils.loadExtension(extData);
ok(
Services.prefs.getBoolPref("signon.rememberSignons"),
"privacy setting intial value as expected"
);
await extension.startup();
Assert.equal(
extension.extension.version,
"1.0",
"Got the expected extension version before upgrade"
);
await withHandlingUserInput(extension, async () => {
extension.sendMessage("permissions-request", {
permissions: ["browserSettings", "privacy"],
data_collection: ["technicalAndInteraction", "locationInfo"],
});
await extension.awaitMessage("permissions-request:done");
});
ok(!cacheIsEnabled(), "setting is set after startup");
extension.sendMessage("permissions-getAll");
Assert.deepEqual(
await extension.awaitMessage("permissions-getAll:done"),
{
origins: [],
permissions: ["browserSettings", "privacy"],
data_collection: ["technicalAndInteraction", "locationInfo"],
},
"got the expected granted permissions"
);
extData.manifest.version = "2.0";
extData.manifest.permissions = ["privacy"];
extData.manifest.browser_specific_settings.gecko.data_collection_permissions.required =
["locationInfo"];
delete extData.manifest.optional_permissions;
delete extData.manifest.browser_specific_settings.gecko
.data_collection_permissions.optional;
await extension.upgrade(extData);
Assert.equal(
extension.extension.version,
"2.0",
"Got the expected extension version after upgrade"
);
ok(cacheIsEnabled(), "setting is reset after upgrade");
ok(
!Services.prefs.getBoolPref("signon.rememberSignons"),
"privacy setting is still set after upgrade"
);
extension.sendMessage("permissions-getAll");
Assert.deepEqual(
await extension.awaitMessage("permissions-getAll:done"),
{
origins: [],
permissions: ["privacy"],
data_collection: ["locationInfo"],
},
"got the expected granted permissions after upgrade"
);
await extension.unload();
}
);