diff options
Diffstat (limited to 'toolkit/components/forgetaboutsite')
7 files changed, 815 insertions, 0 deletions
diff --git a/toolkit/components/forgetaboutsite/ForgetAboutSite.sys.mjs b/toolkit/components/forgetaboutsite/ForgetAboutSite.sys.mjs new file mode 100644 index 0000000000..603d121403 --- /dev/null +++ b/toolkit/components/forgetaboutsite/ForgetAboutSite.sys.mjs @@ -0,0 +1,127 @@ +/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +export var ForgetAboutSite = { + /** + * Clear data associated with a base domain. This includes partitioned storage + * associated with the domain. If a base domain can not be computed from + * aDomainOrHost, data will be cleared by host instead. + * + * @param {string} aDomainOrHost - Domain or host to clear data for. Will be + * converted to base domain if needed. + * @returns {Promise} - Resolves once all matching data has been cleared. + * Throws if any of the internal cleaners fail. + */ + async removeDataFromBaseDomain(aDomainOrHost) { + if (!aDomainOrHost) { + throw new Error("aDomainOrHost can not be empty."); + } + + let baseDomain; + try { + baseDomain = Services.eTLD.getBaseDomainFromHost(aDomainOrHost); + } catch (e) {} + + let errorCount; + if (baseDomain) { + errorCount = await new Promise(resolve => { + Services.clearData.deleteDataFromBaseDomain( + baseDomain, + true /* user request */, + Ci.nsIClearDataService.CLEAR_FORGET_ABOUT_SITE, + errorCode => resolve(bitCounting(errorCode)) + ); + }); + } else { + // If we can't get a valid base domain for aDomainOrHost, fall back to + // delete by host. + errorCount = await new Promise(resolve => { + Services.clearData.deleteDataFromHost( + aDomainOrHost, + true /* user request */, + Ci.nsIClearDataService.CLEAR_FORGET_ABOUT_SITE, + errorCode => resolve(bitCounting(errorCode)) + ); + }); + } + + if (errorCount !== 0) { + throw new Error( + `There were a total of ${errorCount} errors during removal` + ); + } + }, + + /** + * @deprecated This is a legacy method which clears by host only. Also it does + * not clear all storage partitioned via dFPI. Use removeDataFromBaseDomain + * instead. + */ + async removeDataFromDomain(aDomain) { + let promises = [ + new Promise(resolve => + Services.clearData.deleteDataFromHost( + aDomain, + true /* user request */, + Ci.nsIClearDataService.CLEAR_FORGET_ABOUT_SITE, + errorCode => resolve(bitCounting(errorCode)) + ) + ), + ]; + + try { + let baseDomain = Services.eTLD.getBaseDomainFromHost(aDomain); + + let cookies = Services.cookies.cookies; + let hosts = new Set(); + for (let cookie of cookies) { + if (Services.eTLD.hasRootDomain(cookie.rawHost, baseDomain)) { + hosts.add(cookie.rawHost); + } + } + + for (let host of hosts) { + promises.push( + new Promise(resolve => + Services.clearData.deleteDataFromHost( + host, + true /* user request */, + Ci.nsIClearDataService.CLEAR_COOKIES, + errorCode => resolve(bitCounting(errorCode)) + ) + ) + ); + } + } catch (e) { + // - NS_ERROR_HOST_IS_IP_ADDRESS: the host is in ipv4/ipv6. + // - NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS: not enough domain parts to extract, + // i.e. the host is on the PSL. + // In both these cases we should probably not try to use the host as a base + // domain to remove more data, but we can still (try to) continue deleting the host. + if ( + e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS && + e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + ) { + throw e; + } + } + + let errorCount = (await Promise.all(promises)).reduce((a, b) => a + b); + + if (errorCount !== 0) { + throw new Error( + `There were a total of ${errorCount} errors during removal` + ); + } + }, +}; + +function bitCounting(value) { + // To know more about how to count bits set to 1 in a numeric value, see this + // interesting article: + // https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/ + const count = + value - ((value >> 1) & 0o33333333333) - ((value >> 2) & 0o11111111111); + return ((count + (count >> 3)) & 0o30707070707) % 63; +} diff --git a/toolkit/components/forgetaboutsite/moz.build b/toolkit/components/forgetaboutsite/moz.build new file mode 100644 index 0000000000..e8080540da --- /dev/null +++ b/toolkit/components/forgetaboutsite/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"] +XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.toml"] + +EXTRA_JS_MODULES += [ + "ForgetAboutSite.sys.mjs", +] + +with Files("**"): + BUG_COMPONENT = ("Toolkit", "Data Sanitization") diff --git a/toolkit/components/forgetaboutsite/test/browser/browser.toml b/toolkit/components/forgetaboutsite/test/browser/browser.toml new file mode 100644 index 0000000000..a58397bba0 --- /dev/null +++ b/toolkit/components/forgetaboutsite/test/browser/browser.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["browser_cookieDomain.js"] diff --git a/toolkit/components/forgetaboutsite/test/browser/browser_cookieDomain.js b/toolkit/components/forgetaboutsite/test/browser/browser_cookieDomain.js new file mode 100644 index 0000000000..21dff6ada0 --- /dev/null +++ b/toolkit/components/forgetaboutsite/test/browser/browser_cookieDomain.js @@ -0,0 +1,63 @@ +const { ForgetAboutSite } = ChromeUtils.importESModule( + "resource://gre/modules/ForgetAboutSite.sys.mjs" +); +const { SiteDataTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SiteDataTestUtils.sys.mjs" +); + +function checkCookie(host, originAttributes) { + for (let cookie of Services.cookies.cookies) { + if ( + ChromeUtils.isOriginAttributesEqual( + originAttributes, + cookie.originAttributes + ) && + cookie.host.includes(host) + ) { + return true; + } + } + return false; +} + +add_task(async function test_singleDomain() { + info("Test single cookie domain"); + + // Let's clean up all the data. + await SiteDataTestUtils.clear(); + + SiteDataTestUtils.addToCookies({ origin: "https://example.com" }); + + // Cleaning up. + await ForgetAboutSite.removeDataFromDomain("example.com"); + + // All good. + ok(!checkCookie("example.com", {}), "No cookies"); + + // Clean up. + await SiteDataTestUtils.clear(); +}); + +add_task(async function test_subDomain() { + info("Test cookies for sub domains"); + + // Let's clean up all the data. + await SiteDataTestUtils.clear(); + + SiteDataTestUtils.addToCookies({ origin: "https://example.com" }); + SiteDataTestUtils.addToCookies({ origin: "https://sub.example.com" }); + SiteDataTestUtils.addToCookies({ origin: "https://sub2.example.com" }); + SiteDataTestUtils.addToCookies({ origin: "https://sub2.example.com" }); + + SiteDataTestUtils.addToCookies({ origin: "https://example.org" }); + + // Cleaning up. + await ForgetAboutSite.removeDataFromDomain("sub.example.com"); + + // All good. + ok(!checkCookie("example.com", {}), "No cookies for example.com"); + ok(checkCookie("example.org", {}), "Has cookies for example.org"); + + // Clean up. + await SiteDataTestUtils.clear(); +}); diff --git a/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js b/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js new file mode 100644 index 0000000000..0e0e178e96 --- /dev/null +++ b/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js @@ -0,0 +1,28 @@ +/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var profileDir = do_get_profile(); + +/** + * Removes any files that could make our tests fail. + */ +async function cleanUp() { + let files = ["places.sqlite", "cookies.sqlite"]; + + for (let i = 0; i < files.length; i++) { + let file = Services.dirsvc.get("ProfD", Ci.nsIFile); + file.append(files[i]); + if (file.exists()) { + file.remove(false); + } + } + + await new Promise(resolve => { + Services.clearData.deleteData( + Ci.nsIClearDataService.CLEAR_PERMISSIONS, + value => resolve() + ); + }); +} +cleanUp(); diff --git a/toolkit/components/forgetaboutsite/test/unit/test_removeDataFromDomain.js b/toolkit/components/forgetaboutsite/test/unit/test_removeDataFromDomain.js new file mode 100644 index 0000000000..84894d1ec5 --- /dev/null +++ b/toolkit/components/forgetaboutsite/test/unit/test_removeDataFromDomain.js @@ -0,0 +1,574 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 + * 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test added with bug 460086 to test the behavior of the new API that was added + * to remove all traces of visiting a site. + */ + +// Globals + +const { ForgetAboutSite } = ChromeUtils.importESModule( + "resource://gre/modules/ForgetAboutSite.sys.mjs" +); + +const { PlacesUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PlacesUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", +}); + +const COOKIE_EXPIRY = Math.round(Date.now() / 1000) + 60; +const COOKIE_NAME = "testcookie"; +const COOKIE_PATH = "/"; + +const LOGIN_USERNAME = "username"; +const LOGIN_PASSWORD = "password"; +const LOGIN_USERNAME_FIELD = "username_field"; +const LOGIN_PASSWORD_FIELD = "password_field"; + +const PERMISSION_TYPE = "test-perm"; +const PERMISSION_VALUE = Ci.nsIPermissionManager.ALLOW_ACTION; + +const PREFERENCE_NAME = "test-pref"; + +// Utility Functions + +/** + * Add a cookie to the cookie service. + * + * @param aDomain + */ +function add_cookie(aDomain) { + check_cookie_exists(aDomain, false); + Services.cookies.add( + aDomain, + COOKIE_PATH, + COOKIE_NAME, + "", + false, + false, + false, + COOKIE_EXPIRY, + {}, + Ci.nsICookie.SAMESITE_NONE, + Ci.nsICookie.SCHEME_HTTPS + ); + check_cookie_exists(aDomain, true); +} + +/** + * Checks to ensure that a cookie exists or not for a domain. + * + * @param aDomain + * The domain to check for the cookie. + * @param aExists + * True if the cookie should exist, false otherwise. + */ +function check_cookie_exists(aDomain, aExists) { + Assert.equal( + aExists, + Services.cookies.cookieExists(aDomain, COOKIE_PATH, COOKIE_NAME, {}) + ); +} + +/** + * Adds a disabled host to the login manager. + * + * @param aHost + * The host to add to the list of disabled hosts. + */ +function add_disabled_host(aHost) { + check_disabled_host(aHost, false); + Services.logins.setLoginSavingEnabled(aHost, false); + check_disabled_host(aHost, true); +} + +/** + * Checks to see if a host is disabled for storing logins or not. + * + * @param aHost + * The host to check if it is disabled. + * @param aIsDisabled + * True if the host should be disabled, false otherwise. + */ +function check_disabled_host(aHost, aIsDisabled) { + Assert.equal(!aIsDisabled, Services.logins.getLoginSavingEnabled(aHost)); +} + +/** + * Adds a login for the specified host to the login manager. + * + * @param aHost + * The host to add the login for. + */ +async function add_login(aHost) { + await check_login_exists(aHost, false); + let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance( + Ci.nsILoginInfo + ); + login.init( + aHost, + "", + null, + LOGIN_USERNAME, + LOGIN_PASSWORD, + LOGIN_USERNAME_FIELD, + LOGIN_PASSWORD_FIELD + ); + await Services.logins.addLoginAsync(login); + await check_login_exists(aHost, true); +} + +/** + * Checks to see if a login exists for a host. + * + * @param aHost + * The host to check for the test login. + * @param aExists + * True if the login should exist, false otherwise. + */ +async function check_login_exists(aHost, aExists) { + let logins = await Services.logins.searchLoginsAsync({ origin: aHost }); + Assert.equal(logins.length, aExists ? 1 : 0); +} + +/** + * Adds a permission for the specified URI to the permission manager. + * + * @param aURI + * The URI to add the test permission for. + */ +function add_permission(aURI) { + check_permission_exists(aURI, false); + let principal = Services.scriptSecurityManager.createContentPrincipal( + aURI, + {} + ); + + Services.perms.addFromPrincipal(principal, PERMISSION_TYPE, PERMISSION_VALUE); + check_permission_exists(aURI, true); +} + +/** + * Checks to see if a permission exists for the given URI. + * + * @param aURI + * The URI to check if a permission exists. + * @param aExists + * True if the permission should exist, false otherwise. + */ +function check_permission_exists(aURI, aExists) { + let principal = Services.scriptSecurityManager.createContentPrincipal( + aURI, + {} + ); + + let perm = Services.perms.testExactPermissionFromPrincipal( + principal, + PERMISSION_TYPE + ); + let checker = aExists ? "equal" : "notEqual"; + Assert[checker](perm, PERMISSION_VALUE); +} + +/** + * Adds a content preference for the specified URI. + * + * @param aURI + * The URI to add a preference for. + */ +function add_preference(aURI) { + return new Promise(resolve => { + let cp = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + cp.set(aURI.spec, PREFERENCE_NAME, "foo", null, { + handleCompletion: () => resolve(), + }); + }); +} + +/** + * Checks to see if a content preference exists for the given URI. + * + * @param aURI + * The URI to check if a preference exists. + */ +function preference_exists(aURI) { + return new Promise(resolve => { + let cp = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + let exists = false; + cp.getByDomainAndName(aURI.spec, PREFERENCE_NAME, null, { + handleResult: () => (exists = true), + handleCompletion: () => resolve(exists), + }); + }); +} + +// Test Functions + +// History +async function test_history_cleared_with_direct_match() { + const TEST_URI = Services.io.newURI("http://mozilla.org/foo"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); + await PlacesTestUtils.addVisits(TEST_URI); + Assert.ok(await PlacesUtils.history.hasVisits(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); +} + +async function test_history_cleared_with_subdomain() { + const TEST_URI = Services.io.newURI("http://www.mozilla.org/foo"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); + await PlacesTestUtils.addVisits(TEST_URI); + Assert.ok(await PlacesUtils.history.hasVisits(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); +} + +async function test_history_not_cleared_with_uri_contains_domain() { + const TEST_URI = Services.io.newURI("http://ilovemozilla.org/foo"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); + await PlacesTestUtils.addVisits(TEST_URI); + Assert.ok(await PlacesUtils.history.hasVisits(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.ok(await PlacesUtils.history.hasVisits(TEST_URI)); + + // Clear history since we left something there from this test. + await PlacesUtils.history.clear(); +} + +async function test_history_cleared_base_domain() { + const TEST_URI = Services.io.newURI("http://mozilla.org/foo"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); + await PlacesTestUtils.addVisits(TEST_URI); + Assert.ok(await PlacesUtils.history.hasVisits(TEST_URI)); + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + Assert.equal(false, await PlacesUtils.history.hasVisits(TEST_URI)); +} + +// Cookie Service +async function test_cookie_cleared_with_direct_match() { + const TEST_DOMAIN = "mozilla.org"; + add_cookie(TEST_DOMAIN); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_cookie_exists(TEST_DOMAIN, false); +} + +async function test_cookie_cleared_with_subdomain() { + const TEST_DOMAIN = "www.mozilla.org"; + add_cookie(TEST_DOMAIN); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_cookie_exists(TEST_DOMAIN, false); +} + +async function test_cookie_not_cleared_with_uri_contains_domain() { + const TEST_DOMAIN = "ilovemozilla.org"; + add_cookie(TEST_DOMAIN); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_cookie_exists(TEST_DOMAIN, true); +} + +async function test_cookie_cleared_base_domain() { + const TEST_DOMAIN = "mozilla.org"; + add_cookie(TEST_DOMAIN); + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + check_cookie_exists(TEST_DOMAIN, false); +} + +// Login Manager +async function test_login_manager_disabled_hosts_cleared_with_direct_match() { + const TEST_HOST = "http://mozilla.org"; + add_disabled_host(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_disabled_host(TEST_HOST, false); +} + +async function test_login_manager_disabled_hosts_cleared_with_subdomain() { + const TEST_HOST = "http://www.mozilla.org"; + add_disabled_host(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_disabled_host(TEST_HOST, false); +} + +async function test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain() { + const TEST_HOST = "http://ilovemozilla.org"; + add_disabled_host(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_disabled_host(TEST_HOST, true); + + // Reset state + Services.logins.setLoginSavingEnabled(TEST_HOST, true); + check_disabled_host(TEST_HOST, false); +} + +async function test_login_manager_logins_cleared_with_direct_match() { + const TEST_HOST = "http://mozilla.org"; + await add_login(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + await check_login_exists(TEST_HOST, true); +} + +async function test_login_manager_logins_cleared_with_subdomain() { + const TEST_HOST = "http://www.mozilla.org"; + await add_login(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + await check_login_exists(TEST_HOST, true); +} + +async function test_login_manager_logins_not_cleared_with_uri_contains_domain() { + const TEST_HOST = "http://ilovemozilla.org"; + await add_login(TEST_HOST); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + await check_login_exists(TEST_HOST, true); + + Services.logins.removeAllUserFacingLogins(); + await check_login_exists(TEST_HOST, false); +} + +async function test_login_manager_disabled_hosts_cleared_base_domain() { + const TEST_HOST = "http://mozilla.org"; + add_disabled_host(TEST_HOST); + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + check_disabled_host(TEST_HOST, false); +} + +// Permission Manager +async function test_permission_manager_cleared_with_direct_match() { + const TEST_URI = Services.io.newURI("http://mozilla.org"); + add_permission(TEST_URI); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_permission_exists(TEST_URI, false); +} + +async function test_permission_manager_cleared_with_subdomain() { + const TEST_URI = Services.io.newURI("http://www.mozilla.org"); + add_permission(TEST_URI); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_permission_exists(TEST_URI, false); +} + +async function test_permission_manager_not_cleared_with_uri_contains_domain() { + const TEST_URI = Services.io.newURI("http://ilovemozilla.org"); + add_permission(TEST_URI); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + check_permission_exists(TEST_URI, true); + + // Reset state + Services.perms.removeAll(); + check_permission_exists(TEST_URI, false); +} + +async function test_permission_manager_cleared_base_domain() { + const TEST_URI = Services.io.newURI("http://mozilla.org"); + add_permission(TEST_URI); + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + check_permission_exists(TEST_URI, false); +} + +// Content Preferences +async function test_content_preferences_cleared_with_direct_match() { + const TEST_URI = Services.io.newURI("http://mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); + await add_preference(TEST_URI); + Assert.ok(await preference_exists(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); +} + +async function test_content_preferences_cleared_with_subdomain() { + const TEST_URI = Services.io.newURI("http://www.mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); + await add_preference(TEST_URI); + Assert.ok(await preference_exists(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); +} + +async function test_content_preferences_not_cleared_with_uri_contains_domain() { + const TEST_URI = Services.io.newURI("http://ilovemozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); + await add_preference(TEST_URI); + Assert.ok(await preference_exists(TEST_URI)); + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + Assert.ok(await preference_exists(TEST_URI)); + + // Reset state + await ForgetAboutSite.removeDataFromDomain("ilovemozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); +} + +async function test_content_preferences_cleared_base_domain() { + const TEST_URI = Services.io.newURI("http://mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); + await add_preference(TEST_URI); + Assert.ok(await preference_exists(TEST_URI)); + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + Assert.equal(false, await preference_exists(TEST_URI)); +} + +// Push +async function test_push_cleared() { + return helper_push_cleared(false); +} + +async function test_push_cleared_base_domain() { + return helper_push_cleared(true); +} + +async function helper_push_cleared(aBaseDomainTest) { + let ps; + try { + ps = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService); + } catch (e) { + // No push service, skip test. + return; + } + + // Enable the push service, but disable the connection, so that accessing + // `pushImpl.service` doesn't try to connect. + Services.prefs.setBoolPref("dom.push.connection.enabled", false); + Services.prefs.setBoolPref("dom.push.enabled", true); + let pushImpl = ps.wrappedJSObject; + if (typeof pushImpl != "object" || !pushImpl || !("service" in pushImpl)) { + // GeckoView, for example, uses a different implementation; bail if it's + // not something we can replace. + info("Can't test with this push service implementation"); + return; + } + // Otherwise, tear down the old one and replace it with our mock backend, + // so that we don't have to initialize an entire mock WebSocket only to + // test clearing data. + await pushImpl.service.uninit?.(); + let wasCleared = false; + pushImpl.service = { + async clear({ domain } = {}) { + Assert.equal( + domain, + "mozilla.org", + "Should pass domain to clear to push service" + ); + wasCleared = true; + }, + }; + Services.prefs.setBoolPref("dom.push.enabled", true); + + if (aBaseDomainTest) { + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + } else { + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + } + + Assert.ok(wasCleared, "Should have cleared push data"); +} + +function test_storage_cleared() { + return helper_storage_cleared(false); +} +function test_storage_cleared_base_domain() { + return helper_storage_cleared(true); +} + +async function helper_storage_cleared(aBaseDomainTest) { + function getStorageForURI(aURI) { + let principal = Services.scriptSecurityManager.createContentPrincipal( + aURI, + {} + ); + + return Services.domStorageManager.createStorage( + null, + principal, + principal, + "" + ); + } + + Services.prefs.setBoolPref("dom.storage.client_validation", false); + + let s = [ + getStorageForURI(Services.io.newURI("http://mozilla.org")), + getStorageForURI(Services.io.newURI("http://my.mozilla.org")), + getStorageForURI(Services.io.newURI("http://ilovemozilla.org")), + ]; + + for (let i = 0; i < s.length; ++i) { + let storage = s[i]; + storage.setItem("test", "value" + i); + Assert.equal(storage.length, 1); + Assert.equal(storage.key(0), "test"); + Assert.equal(storage.getItem("test"), "value" + i); + } + + if (aBaseDomainTest) { + await ForgetAboutSite.removeDataFromBaseDomain("mozilla.org"); + } else { + await ForgetAboutSite.removeDataFromDomain("mozilla.org"); + } + + Assert.equal(s[0].getItem("test"), null); + Assert.equal(s[0].length, 0); + Assert.equal(s[1].getItem("test"), null); + Assert.equal(s[1].length, 0); + Assert.equal(s[2].getItem("test"), "value2"); + Assert.equal(s[2].length, 1); +} + +var tests = [ + // History + test_history_cleared_with_direct_match, + test_history_cleared_with_subdomain, + test_history_not_cleared_with_uri_contains_domain, + test_history_cleared_base_domain, + + // Cookie Service + test_cookie_cleared_with_direct_match, + test_cookie_cleared_with_subdomain, + test_cookie_not_cleared_with_uri_contains_domain, + test_cookie_cleared_base_domain, + + // Login Manager + test_login_manager_disabled_hosts_cleared_with_direct_match, + test_login_manager_disabled_hosts_cleared_with_subdomain, + test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain, + test_login_manager_logins_cleared_with_direct_match, + test_login_manager_logins_cleared_with_subdomain, + test_login_manager_logins_not_cleared_with_uri_contains_domain, + test_login_manager_disabled_hosts_cleared_base_domain, + + // Permission Manager + test_permission_manager_cleared_with_direct_match, + test_permission_manager_cleared_with_subdomain, + test_permission_manager_not_cleared_with_uri_contains_domain, + test_permission_manager_cleared_base_domain, + + // Content Preferences + test_content_preferences_cleared_with_direct_match, + test_content_preferences_cleared_with_subdomain, + test_content_preferences_not_cleared_with_uri_contains_domain, + test_content_preferences_cleared_base_domain, + + // Push + test_push_cleared, + test_push_cleared_base_domain, + + // Storage + test_storage_cleared, + test_storage_cleared_base_domain, +]; + +function run_test() { + for (let i = 0; i < tests.length; i++) { + add_task(tests[i]); + } + + run_next_test(); +} diff --git a/toolkit/components/forgetaboutsite/test/unit/xpcshell.toml b/toolkit/components/forgetaboutsite/test/unit/xpcshell.toml new file mode 100644 index 0000000000..475e73cda1 --- /dev/null +++ b/toolkit/components/forgetaboutsite/test/unit/xpcshell.toml @@ -0,0 +1,5 @@ +[DEFAULT] +head = "head_forgetaboutsite.js" +skip-if = ["os == 'android'"] + +["test_removeDataFromDomain.js"] |