diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js b/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js new file mode 100644 index 0000000000..93eeac729a --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js @@ -0,0 +1,607 @@ +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + ContextualIdentityService: + "resource://gre/modules/ContextualIdentityService.sys.mjs", + ExtensionPreferencesManager: + "resource://gre/modules/ExtensionPreferencesManager.sys.mjs", + AddonManager: "resource://gre/modules/AddonManager.sys.mjs", +}); + +const CONTAINERS_PREF = "privacy.userContext.enabled"; + +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "42", + "42" +); + +add_task(async function startup() { + await AddonTestUtils.promiseStartupManager(); +}); + +add_task(async function test_contextualIdentities_without_permissions() { + function background() { + browser.test.assertTrue( + !browser.contextualIdentities, + "contextualIdentities API is not available when the contextualIdentities permission is not required" + ); + browser.test.notifyPass("contextualIdentities_without_permission"); + } + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + background, + manifest: { + browser_specific_settings: { + gecko: { id: "testing@thing.com" }, + }, + permissions: [], + }, + }); + + await extension.startup(); + await extension.awaitFinish("contextualIdentities_without_permission"); + await extension.unload(); +}); + +add_task(async function test_contextualIdentity_events() { + async function background() { + function createOneTimeListener(type) { + return new Promise((resolve, reject) => { + try { + browser.test.assertTrue( + type in browser.contextualIdentities, + `Found API object browser.contextualIdentities.${type}` + ); + const listener = change => { + browser.test.assertTrue( + "contextualIdentity" in change, + `Found identity in change` + ); + browser.contextualIdentities[type].removeListener(listener); + resolve(change); + }; + browser.contextualIdentities[type].addListener(listener); + } catch (e) { + reject(e); + } + }); + } + + function assertExpected(expected, container) { + // Number of keys that are added by the APIs + const createdCount = 2; + for (let key of Object.keys(container)) { + browser.test.assertTrue(key in expected, `found property ${key}`); + browser.test.assertEq( + expected[key], + container[key], + `property value for ${key} is correct` + ); + } + const hexMatch = /^#[0-9a-f]{6}$/; + browser.test.assertTrue( + hexMatch.test(expected.colorCode), + "Color code property was expected Hex shape" + ); + const iconMatch = /^resource:\/\/usercontext-content\/[a-z]+[.]svg$/; + browser.test.assertTrue( + iconMatch.test(expected.iconUrl), + "Icon url property was expected shape" + ); + browser.test.assertEq( + Object.keys(expected).length, + Object.keys(container).length + createdCount, + "all expected properties found" + ); + } + + let onCreatePromise = createOneTimeListener("onCreated"); + + let containerObj = { name: "foobar", color: "red", icon: "circle" }; + let ci = await browser.contextualIdentities.create(containerObj); + browser.test.assertTrue(!!ci, "We have an identity"); + const onCreateListenerResponse = await onCreatePromise; + const cookieStoreId = ci.cookieStoreId; + assertExpected( + onCreateListenerResponse.contextualIdentity, + Object.assign(containerObj, { cookieStoreId }) + ); + + let onUpdatedPromise = createOneTimeListener("onUpdated"); + let updateContainerObj = { name: "testing", color: "blue", icon: "dollar" }; + ci = await browser.contextualIdentities.update( + cookieStoreId, + updateContainerObj + ); + browser.test.assertTrue(!!ci, "We have an update identity"); + const onUpdatedListenerResponse = await onUpdatedPromise; + assertExpected( + onUpdatedListenerResponse.contextualIdentity, + Object.assign(updateContainerObj, { cookieStoreId }) + ); + + let onRemovePromise = createOneTimeListener("onRemoved"); + ci = await browser.contextualIdentities.remove( + updateContainerObj.cookieStoreId + ); + browser.test.assertTrue(!!ci, "We have an remove identity"); + const onRemoveListenerResponse = await onRemovePromise; + assertExpected( + onRemoveListenerResponse.contextualIdentity, + Object.assign(updateContainerObj, { cookieStoreId }) + ); + + browser.test.notifyPass("contextualIdentities_events"); + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { + gecko: { id: "testing@thing.com" }, + }, + permissions: ["contextualIdentities"], + }, + }); + + Services.prefs.setBoolPref(CONTAINERS_PREF, true); + + await extension.startup(); + await extension.awaitFinish("contextualIdentities_events"); + await extension.unload(); + + Services.prefs.clearUserPref(CONTAINERS_PREF); +}); + +add_task(async function test_contextualIdentity_with_permissions() { + async function background() { + let ci; + await browser.test.assertRejects( + browser.contextualIdentities.get("foobar"), + "Invalid contextual identity: foobar", + "API should reject here" + ); + await browser.test.assertRejects( + browser.contextualIdentities.update("foobar", { name: "testing" }), + "Invalid contextual identity: foobar", + "API should reject for unknown updates" + ); + await browser.test.assertRejects( + browser.contextualIdentities.remove("foobar"), + "Invalid contextual identity: foobar", + "API should reject for removing unknown containers" + ); + + ci = await browser.contextualIdentities.get("firefox-container-1"); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertTrue("name" in ci, "We have an identity.name"); + browser.test.assertTrue("color" in ci, "We have an identity.color"); + browser.test.assertTrue("icon" in ci, "We have an identity.icon"); + browser.test.assertEq("Personal", ci.name, "identity.name is correct"); + browser.test.assertEq( + "firefox-container-1", + ci.cookieStoreId, + "identity.cookieStoreId is correct" + ); + + function listenForMessage(messageName, stateChangeBool) { + return new Promise(resolve => { + browser.test.onMessage.addListener(function listener(msg) { + browser.test.log(`Got message from background: ${msg}`); + if (msg === messageName + "-response") { + browser.test.onMessage.removeListener(listener); + resolve(); + } + }); + browser.test.log( + `Sending message to background: ${messageName} ${stateChangeBool}` + ); + browser.test.sendMessage(messageName, stateChangeBool); + }); + } + + await listenForMessage("containers-state-change", false); + + browser.test.assertRejects( + browser.contextualIdentities.query({}), + "Contextual identities are currently disabled", + "Throws when containers are disabled" + ); + + await listenForMessage("containers-state-change", true); + + let cis = await browser.contextualIdentities.query({}); + browser.test.assertEq( + 4, + cis.length, + "by default we should have 4 containers" + ); + + cis = await browser.contextualIdentities.query({ name: "Personal" }); + browser.test.assertEq( + 1, + cis.length, + "by default we should have 1 container called Personal" + ); + + cis = await browser.contextualIdentities.query({ name: "foobar" }); + browser.test.assertEq( + 0, + cis.length, + "by default we should have 0 container called foobar" + ); + + ci = await browser.contextualIdentities.create({ + name: "foobar", + color: "red", + icon: "gift", + }); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertEq("foobar", ci.name, "identity.name is correct"); + browser.test.assertEq("red", ci.color, "identity.color is correct"); + browser.test.assertEq("gift", ci.icon, "identity.icon is correct"); + browser.test.assertTrue( + !!ci.cookieStoreId, + "identity.cookieStoreId is correct" + ); + + browser.test.assertRejects( + browser.contextualIdentities.create({ + name: "foobar", + color: "red", + icon: "firefox", + }), + "Invalid icon firefox for container", + "Create container called with an invalid icon" + ); + + browser.test.assertRejects( + browser.contextualIdentities.create({ + name: "foobar", + color: "firefox-orange", + icon: "gift", + }), + "Invalid color name firefox-orange for container", + "Create container called with an invalid color" + ); + + cis = await browser.contextualIdentities.query({}); + browser.test.assertEq( + 5, + cis.length, + "we should still have have 5 containers" + ); + + ci = await browser.contextualIdentities.get(ci.cookieStoreId); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertEq("foobar", ci.name, "identity.name is correct"); + browser.test.assertEq("red", ci.color, "identity.color is correct"); + browser.test.assertEq("gift", ci.icon, "identity.icon is correct"); + + browser.test.assertRejects( + browser.contextualIdentities.update(ci.cookieStoreId, { + name: "foobar", + color: "red", + icon: "firefox", + }), + "Invalid icon firefox for container", + "Create container called with an invalid icon" + ); + + browser.test.assertRejects( + browser.contextualIdentities.update(ci.cookieStoreId, { + name: "foobar", + color: "firefox-orange", + icon: "gift", + }), + "Invalid color name firefox-orange for container", + "Create container called with an invalid color" + ); + + cis = await browser.contextualIdentities.query({}); + browser.test.assertEq(5, cis.length, "now we have 5 identities"); + + ci = await browser.contextualIdentities.update(ci.cookieStoreId, { + name: "barfoo", + color: "blue", + icon: "cart", + }); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertEq("barfoo", ci.name, "identity.name is correct"); + browser.test.assertEq("blue", ci.color, "identity.color is correct"); + browser.test.assertEq("cart", ci.icon, "identity.icon is correct"); + + ci = await browser.contextualIdentities.get(ci.cookieStoreId); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertEq("barfoo", ci.name, "identity.name is correct"); + browser.test.assertEq("blue", ci.color, "identity.color is correct"); + browser.test.assertEq("cart", ci.icon, "identity.icon is correct"); + + ci = await browser.contextualIdentities.remove(ci.cookieStoreId); + browser.test.assertTrue(!!ci, "We have an identity"); + browser.test.assertEq("barfoo", ci.name, "identity.name is correct"); + browser.test.assertEq("blue", ci.color, "identity.color is correct"); + browser.test.assertEq("cart", ci.icon, "identity.icon is correct"); + + cis = await browser.contextualIdentities.query({}); + browser.test.assertEq(4, cis.length, "we are back to 4 identities"); + + browser.test.notifyPass("contextualIdentities"); + } + + function makeExtension(id) { + return ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + background, + manifest: { + browser_specific_settings: { + gecko: { id }, + }, + permissions: ["contextualIdentities"], + }, + }); + } + + let extension = makeExtension("containers-test@mozilla.org"); + + extension.onMessage("containers-state-change", stateBool => { + Cu.reportError(`Got message "containers-state-change", ${stateBool}`); + Services.prefs.setBoolPref(CONTAINERS_PREF, stateBool); + Cu.reportError("Changed pref"); + extension.sendMessage("containers-state-change-response"); + }); + + await extension.startup(); + await extension.awaitFinish("contextualIdentities"); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should now be enabled, whatever it's initial state" + ); + await extension.unload(); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should remain enabled" + ); + + Services.prefs.clearUserPref(CONTAINERS_PREF); +}); + +add_task(async function test_contextualIdentity_extensions_enable_containers() { + async function background() { + let ci = await browser.contextualIdentities.get("firefox-container-1"); + browser.test.assertTrue(!!ci, "We have an identity"); + + browser.test.notifyPass("contextualIdentities"); + } + function makeExtension(id) { + return ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + background, + manifest: { + browser_specific_settings: { + gecko: { id }, + }, + permissions: ["contextualIdentities"], + }, + }); + } + async function testSetting(expect, message) { + let setting = await ExtensionPreferencesManager.getSetting( + "privacy.containers" + ); + if (expect === null) { + equal(setting, null, message); + } else { + equal(setting.value, expect, message); + } + } + function testPref(expect, message) { + equal(Services.prefs.getBoolPref(CONTAINERS_PREF), expect, message); + } + + let extension = makeExtension("containers-test@mozilla.org"); + await extension.startup(); + await extension.awaitFinish("contextualIdentities"); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should now be enabled, whatever it's initial state" + ); + await extension.unload(); + await testSetting(null, "setting should be unset"); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should remain enabled" + ); + + // Lets set containers explicitly to be off and test we keep it that way after removal + Services.prefs.setBoolPref(CONTAINERS_PREF, false); + + let extension1 = makeExtension("containers-test-1@mozilla.org"); + await extension1.startup(); + await extension1.awaitFinish("contextualIdentities"); + await testSetting(extension1.id, "setting should be controlled"); + testPref(true, "Pref should now be enabled, whatever it's initial state"); + + // Test that disabling leaves containers on, and that re-enabling with containers off + // will re-enable containers. + const addon = await AddonManager.getAddonByID(extension1.id); + await addon.disable(); + await testSetting(undefined, "setting should not be an addon"); + testPref(true, "Pref should remain enabled, whatever it's initial state"); + + Services.prefs.setBoolPref(CONTAINERS_PREF, false); + + await addon.enable(); + await testSetting(extension1.id, "setting should be controlled"); + testPref(true, "Pref should be enabled"); + + await extension1.unload(); + await testSetting(null, "setting should be unset"); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should remain enabled" + ); + + // Lets set containers explicitly to be on and test we keep it that way after removal. + Services.prefs.setBoolPref(CONTAINERS_PREF, true); + + let extension2 = makeExtension("containers-test-2@mozilla.org"); + let extension3 = makeExtension("containers-test-3@mozilla.org"); + await extension2.startup(); + await extension2.awaitFinish("contextualIdentities"); + await extension3.startup(); + await extension3.awaitFinish("contextualIdentities"); + + // Flip the ordering to check it's still enabled + await testSetting(extension3.id, "setting should still be controlled by 3"); + testPref(true, "Pref should now be enabled 1"); + await extension3.unload(); + await testSetting(extension2.id, "setting should still be controlled by 2"); + testPref(true, "Pref should now be enabled 2"); + await extension2.unload(); + await testSetting(null, "setting should be unset"); + testPref(true, "Pref should now be enabled 3"); + + Services.prefs.clearUserPref(CONTAINERS_PREF); +}); + +add_task(async function test_contextualIdentity_preference_change() { + async function background() { + let extensionInfo = await browser.management.getSelf(); + if (extensionInfo.version == "1.0.0") { + const containers = await browser.contextualIdentities.query({}); + browser.test.assertEq( + containers.length, + 4, + "We still have the original containers" + ); + await browser.contextualIdentities.create({ + name: "foobar", + color: "red", + icon: "circle", + }); + } + const containers = await browser.contextualIdentities.query({}); + browser.test.assertEq(containers.length, 5, "We have a new container"); + if (extensionInfo.version == "1.1.0") { + await browser.contextualIdentities.remove(containers[4].cookieStoreId); + } + browser.test.notifyPass("contextualIdentities"); + } + function makeExtension(id, version) { + return ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + background, + manifest: { + version, + browser_specific_settings: { + gecko: { id }, + }, + permissions: ["contextualIdentities"], + }, + }); + } + + Services.prefs.setBoolPref(CONTAINERS_PREF, false); + let extension = makeExtension("containers-pref-test@mozilla.org", "1.0.0"); + await extension.startup(); + await extension.awaitFinish("contextualIdentities"); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should now be enabled, whatever it's initial state" + ); + + let extension2 = makeExtension("containers-pref-test@mozilla.org", "1.1.0"); + await extension2.startup(); + await extension2.awaitFinish("contextualIdentities"); + + await extension.unload(); + equal( + Services.prefs.getBoolPref(CONTAINERS_PREF), + true, + "Pref should remain enabled" + ); + + Services.prefs.clearUserPref(CONTAINERS_PREF); +}); + +add_task( + { pref_set: [["extensions.eventPages.enabled", true]] }, + async function test_contextualIdentity_event_page() { + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + browser_specific_settings: { + gecko: { id: "eventpage@mochitest" }, + }, + permissions: ["contextualIdentities"], + background: { persistent: false }, + }, + background() { + browser.contextualIdentities.onCreated.addListener(() => { + browser.test.sendMessage("created"); + }); + browser.contextualIdentities.onUpdated.addListener(() => {}); + browser.contextualIdentities.onRemoved.addListener(() => { + browser.test.sendMessage("removed"); + }); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = ["onCreated", "onUpdated", "onRemoved"]; + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "contextualIdentities", event, { + primed: false, + }); + } + + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "contextualIdentities", event, { + primed: true, + }); + } + + // test events waken background + let identity = ContextualIdentityService.create("foobar", "circle", "red"); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("created"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "contextualIdentities", event, { + primed: false, + }); + } + + ContextualIdentityService.remove(identity.userContextId); + await extension.awaitMessage("removed"); + + // check primed listeners after startup + await AddonTestUtils.promiseRestartManager(); + await extension.awaitStartup(); + + for (let event of EVENTS) { + assertPersistentListeners(extension, "contextualIdentities", event, { + primed: true, + }); + } + + await extension.unload(); + } +); |