diff options
Diffstat (limited to 'toolkit/components/contentprefs/tests')
19 files changed, 2699 insertions, 0 deletions
diff --git a/toolkit/components/contentprefs/tests/browser/browser.toml b/toolkit/components/contentprefs/tests/browser/browser.toml new file mode 100644 index 0000000000..6901c18348 --- /dev/null +++ b/toolkit/components/contentprefs/tests/browser/browser.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["browser_remoteContentPrefs.js"] diff --git a/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js b/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js new file mode 100644 index 0000000000..00c845e488 --- /dev/null +++ b/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js @@ -0,0 +1,242 @@ +"use strict"; + +const childFrameURL = Services.io.newURI( + "data:text/html,<!DOCTYPE HTML><html><body></body></html>" +); + +async function runTestsForFrame(browser, isPrivate) { + let loadContext = Cu.createLoadContext(); + let privateLoadContext = Cu.createPrivateLoadContext(); + + await SpecialPowers.spawn(browser, [], async () => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + Assert.ok(cps !== null, "got the content pref service"); + + await new Promise(resolve => { + cps.setGlobal("testing", 42, null, { + handleCompletion(reason) { + Assert.equal(reason, 0, "set a pref?"); + resolve(); + }, + }); + }); + + let numResults = 0; + await new Promise(resolve => { + cps.getGlobal("testing", null, { + handleResult(pref) { + numResults++; + Assert.equal(pref.name, "testing", "pref has the right name"); + Assert.equal(pref.value, 42, "pref has the right value"); + }, + + handleCompletion(reason) { + Assert.equal(reason, 0, "get a pref?"); + Assert.equal(numResults, 1, "got the right number of prefs"); + resolve(); + }, + }); + }); + }); + + await SpecialPowers.spawn(browser, [isPrivate], async isFramePrivate => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + + if (!content._result) { + content._result = []; + } + + let observer; + cps.addObserverForName( + "testName", + (observer = { + onContentPrefSet(group, name, value, isPrivate) { + Assert.equal(group, null, "group should be null"); + Assert.equal(name, "testName", "should only see testName"); + Assert.equal(value, 42, "value should be correct"); + Assert.equal(isPrivate, isFramePrivate, "privacy should match"); + + content._result.push("set"); + }, + + onContentPrefRemoved(group, name, isPrivate) { + Assert.equal(group, null, "group should be null"); + Assert.equal(name, "testName", "name should match"); + Assert.equal(isPrivate, isFramePrivate, "privacy should match"); + + content._result.push("removed"); + }, + }) + ); + content._observer = observer; + }); + + for (let expectedResponse of ["set", "removed"]) { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + + if (expectedResponse == "set") { + cps.setGlobal( + "testName", + 42, + isPrivate ? privateLoadContext : loadContext + ); + } else { + cps.removeGlobal( + "testName", + isPrivate ? privateLoadContext : loadContext + ); + } + + let response = await SpecialPowers.spawn( + browser, + [expectedResponse], + async expected => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + await ContentTaskUtils.waitForCondition( + () => content._result.length == 1, + "expecting one notification" + ); + + if (expected == "removed") { + cps.removeObserverForName("testName", content._observer); + } + + Assert.equal( + content._result.length, + 1, + "correct number of notifications" + ); + let result = content._result[0]; + content._result = []; + return result; + } + ); + + is(response, expectedResponse, "got correct observer notification"); + } + + await SpecialPowers.spawn(browser, [], async () => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + + await new Promise(resolve => { + cps.setGlobal("testName", 42, null, { + handleCompletion(reason) { + Assert.equal(reason, 0, "set a pref"); + cps.set("http://mochi.test", "testpref", "str", null, { + /* eslint no-shadow: 0 */ + handleCompletion(reason) { + Assert.equal(reason, 0, "set a pref"); + resolve(); + }, + }); + }, + }); + }); + + await new Promise(resolve => { + cps.removeByDomain("http://mochi.test", null, { + handleCompletion(reason) { + Assert.equal(reason, 0, "remove succeeded"); + cps.getByDomainAndName("http://mochi.test", "testpref", null, { + handleResult() { + throw new Error("got removed pref in test3"); + }, + handleCompletion() { + resolve(); + }, + handleError(rv) { + throw new Error(`got a pref error ${rv}`); + }, + }); + }, + }); + }); + }); + + await SpecialPowers.spawn(browser, [], async () => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + + let event = await new Promise((resolve, reject) => { + let prefObserver = { + onContentPrefSet(group, name, value, isPrivate) { + resolve({ group, name, value, isPrivate }); + }, + onContentPrefRemoved(group, name, isPrivate) { + reject("got unexpected notification"); + }, + }; + + cps.addObserverForName("test", prefObserver); + + let privateLoadContext = Cu.createPrivateLoadContext(); + cps.set("http://mochi.test", "test", 42, privateLoadContext); + + cps.addObserverForName("test", prefObserver); + }); + + Assert.equal(event.name, "test", "got the right event"); + Assert.equal(event.isPrivate, true, "the event was for an isPrivate pref"); + }); + + await SpecialPowers.spawn(browser, [], async () => { + var cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + + let results = []; + await new Promise(resolve => { + cps.getByDomainAndName("http://mochi.test", "test", null, { + handleResult(pref) { + info("received handleResult"); + results.push(pref); + }, + handleCompletion(reason) { + resolve(); + }, + handleError(rv) { + ok(false, `failed to get pref ${rv}`); + throw new Error("got unexpected error"); + }, + }); + }); + + Assert.equal(results.length, 0, "should not have seen the pb pref"); + }); +} + +async function runTest(isPrivate) { + /* globals Services */ + info("testing with isPrivate=" + isPrivate); + + let newWindow = await BrowserTestUtils.openNewBrowserWindow({ + private: isPrivate, + }); + + const system = Services.scriptSecurityManager.getSystemPrincipal(); + + let browser = newWindow.gBrowser.selectedBrowser; + let loadedPromise = BrowserTestUtils.browserLoaded(browser); + browser.loadURI(childFrameURL, { triggeringPrincipal: system }); + await loadedPromise; + + await runTestsForFrame(browser, isPrivate); + + newWindow.close(); +} + +add_task(async function () { + await runTest(false); + await runTest(true); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.sys.mjs b/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.sys.mjs new file mode 100644 index 0000000000..3fab7c981f --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.sys.mjs @@ -0,0 +1,59 @@ +/* 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 function AsyncRunner(callbacks) { + this._callbacks = callbacks; + this._iteratorQueue = []; + + // This catches errors reported to the console, e.g., via Cu.reportError. + Services.console.registerListener(this); +} + +AsyncRunner.prototype = { + appendIterator: function AR_appendIterator(iter) { + this._iteratorQueue.push(iter); + }, + + next: function AR_next(arg) { + if (!this._iteratorQueue.length) { + this.destroy(); + this._callbacks.done(); + return; + } + + try { + var { done, value } = this._iteratorQueue[0].next(arg); + if (done) { + this._iteratorQueue.shift(); + this.next(); + return; + } + } catch (err) { + this._callbacks.error(err); + } + + // val is truthy => call next + // val is an iterator => prepend it to the queue and start on it + if (value) { + if (typeof value != "boolean") { + this._iteratorQueue.unshift(value); + } + this.next(); + } + }, + + destroy: function AR_destroy() { + Services.console.unregisterListener(this); + this.destroy = function AR_alreadyDestroyed() {}; + }, + + observe: function AR_consoleServiceListener(msg) { + if ( + msg instanceof Ci.nsIScriptError && + !(msg.flags & Ci.nsIScriptError.warningFlag) + ) { + this._callbacks.consoleError(msg); + } + }, +}; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/head.js b/toolkit/components/contentprefs/tests/unit_cps2/head.js new file mode 100644 index 0000000000..c500d2bf01 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/head.js @@ -0,0 +1,360 @@ +/* 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/. */ + +let loadContext = Cu.createLoadContext(); +let privateLoadContext = Cu.createPrivateLoadContext(); + +const CURRENT_DB_VERSION = 6; + +// There has to be a profile directory before the CPS service is gotten. +do_get_profile(); +let cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 +); + +function makeCallback(resolve, callbacks, success = null) { + callbacks = callbacks || {}; + if (!callbacks.handleError) { + callbacks.handleError = function (error) { + do_throw("handleError call was not expected, error: " + error); + }; + } + if (!callbacks.handleResult) { + callbacks.handleResult = function () { + do_throw("handleResult call was not expected"); + }; + } + if (!callbacks.handleCompletion) { + callbacks.handleCompletion = function (reason) { + equal(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK); + if (success) { + success(); + } else { + resolve(); + } + }; + } + return callbacks; +} + +async function do_check_throws(fn) { + let threw = false; + try { + await fn(); + } catch (err) { + threw = true; + } + ok(threw); +} + +function sendMessage(msg, callback) { + let obj = callback || {}; + let ref = Cu.getWeakReference(obj); + cps.QueryInterface(Ci.nsIObserver).observe(ref, "test:" + msg, null); + return "value" in obj ? obj.value : undefined; +} + +function reset() { + return new Promise(resolve => { + sendMessage("reset", resolve); + }); +} + +function setWithDate(group, name, val, timestamp, context) { + return new Promise(resolve => { + async function updateDate() { + let conn = await sendMessage("db"); + await conn.execute( + ` + UPDATE prefs SET timestamp = :timestamp + WHERE + settingID = (SELECT id FROM settings WHERE name = :name) + AND groupID = (SELECT id FROM groups WHERE name = :group) + `, + { name, group, timestamp: timestamp / 1000 } + ); + + resolve(); + } + + cps.set(group, name, val, context, makeCallback(null, null, updateDate)); + }); +} + +async function getDate(group, name, context) { + let conn = await sendMessage("db"); + let [result] = await conn.execute( + ` + SELECT timestamp FROM prefs + WHERE + settingID = (SELECT id FROM settings WHERE name = :name) + AND groupID = (SELECT id FROM groups WHERE name = :group) + `, + { name, group } + ); + + return result.getResultByName("timestamp") * 1000; +} + +function set(group, name, val, context) { + return new Promise(resolve => { + cps.set(group, name, val, context, makeCallback(resolve)); + }); +} + +function setGlobal(name, val, context) { + return new Promise(resolve => { + cps.setGlobal(name, val, context, makeCallback(resolve)); + }); +} + +function prefOK(actual, expected, strict) { + ok(actual instanceof Ci.nsIContentPref); + equal(actual.domain, expected.domain); + equal(actual.name, expected.name); + if (strict) { + strictEqual(actual.value, expected.value); + } else { + equal(actual.value, expected.value); + } +} + +async function getOK(args, expectedVal, expectedGroup, strict) { + if (args.length == 2) { + args.push(undefined); + } + let expectedPrefs = + expectedVal === undefined + ? [] + : [ + { + domain: expectedGroup || args[0], + name: args[1], + value: expectedVal, + }, + ]; + await getOKEx("getByDomainAndName", args, expectedPrefs, strict); +} + +async function getSubdomainsOK(args, expectedGroupValPairs) { + if (args.length == 2) { + args.push(undefined); + } + let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) { + return { domain: group, name: args[1], value: val }; + }); + await getOKEx("getBySubdomainAndName", args, expectedPrefs); +} + +async function getGlobalOK(args, expectedVal) { + if (args.length == 1) { + args.push(undefined); + } + let expectedPrefs = + expectedVal === undefined + ? [] + : [{ domain: null, name: args[0], value: expectedVal }]; + await getOKEx("getGlobal", args, expectedPrefs); +} + +async function getOKEx(methodName, args, expectedPrefs, strict, context) { + let actualPrefs = []; + await new Promise(resolve => { + args.push( + makeCallback(resolve, { + handleResult: pref => actualPrefs.push(pref), + }) + ); + cps[methodName].apply(cps, args); + }); + arraysOfArraysOK([actualPrefs], [expectedPrefs], function (actual, expected) { + prefOK(actual, expected, strict); + }); +} + +function getCachedOK( + args, + expectedIsCached, + expectedVal, + expectedGroup, + strict +) { + if (args.length == 2) { + args.push(undefined); + } + let expectedPref = !expectedIsCached + ? null + : { + domain: expectedGroup || args[0], + name: args[1], + value: expectedVal, + }; + getCachedOKEx("getCachedByDomainAndName", args, expectedPref, strict); +} + +function getCachedSubdomainsOK(args, expectedGroupValPairs) { + if (args.length == 2) { + args.push(undefined); + } + let actualPrefs = cps.getCachedBySubdomainAndName.apply(cps, args); + actualPrefs = actualPrefs.sort(function (a, b) { + return a.domain.localeCompare(b.domain); + }); + let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) { + return { domain: group, name: args[1], value: val }; + }); + arraysOfArraysOK([actualPrefs], [expectedPrefs], prefOK); +} + +function getCachedGlobalOK(args, expectedIsCached, expectedVal) { + if (args.length == 1) { + args.push(undefined); + } + let expectedPref = !expectedIsCached + ? null + : { + domain: null, + name: args[0], + value: expectedVal, + }; + getCachedOKEx("getCachedGlobal", args, expectedPref); +} + +function getCachedOKEx(methodName, args, expectedPref, strict) { + let actualPref = cps[methodName].apply(cps, args); + if (expectedPref) { + prefOK(actualPref, expectedPref, strict); + } else { + strictEqual(actualPref, null); + } +} + +function arraysOK(actual, expected, cmp) { + if (actual.length != expected.length) { + do_throw( + "Length is not equal: " + + JSON.stringify(actual) + + "==" + + JSON.stringify(expected) + ); + } else { + actual.forEach(function (actualElt, j) { + let expectedElt = expected[j]; + cmp(actualElt, expectedElt); + }); + } +} + +function arraysOfArraysOK(actual, expected, cmp) { + cmp = cmp || equal; + arraysOK(actual, expected, function (act, exp) { + arraysOK(act, exp, cmp); + }); +} + +async function dbOK(expectedRows) { + let conn = await sendMessage("db"); + let stmt = ` + SELECT groups.name AS grp, settings.name AS name, prefs.value AS value + FROM prefs + LEFT JOIN groups ON groups.id = prefs.groupID + LEFT JOIN settings ON settings.id = prefs.settingID + UNION + + /* + These second two SELECTs get the rows of the groups and settings tables + that aren't referenced by the prefs table. Neither should return any + rows if the component is working properly. + */ + SELECT groups.name AS grp, NULL AS name, NULL AS value + FROM groups + WHERE id NOT IN ( + SELECT DISTINCT groupID + FROM prefs + WHERE groupID NOTNULL + ) + UNION + SELECT NULL AS grp, settings.name AS name, NULL AS value + FROM settings + WHERE id NOT IN ( + SELECT DISTINCT settingID + FROM prefs + WHERE settingID NOTNULL + ) + + ORDER BY value ASC, grp ASC, name ASC + `; + + let cols = ["grp", "name", "value"]; + + let actualRows = (await conn.execute(stmt)).map(row => + cols.map(c => row.getResultByName(c)) + ); + arraysOfArraysOK(actualRows, expectedRows); +} + +function on(event, names, dontRemove) { + return onEx(event, names, dontRemove).promise; +} + +function onEx(event, names, dontRemove) { + let args = { + reset() { + for (let prop in this) { + if (Array.isArray(this[prop])) { + this[prop].splice(0, this[prop].length); + } + } + }, + }; + + let observers = {}; + let deferred = null; + let triggered = false; + + names.forEach(function (name) { + let obs = {}; + ["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) { + obs[meth] = () => do_throw(meth + " should not be called for " + name); + }); + obs["onContentPref" + event] = function () { + args[name].push(Array.from(arguments)); + + if (!triggered) { + triggered = true; + executeSoon(function () { + if (!dontRemove) { + names.forEach(n => cps.removeObserverForName(n, observers[n])); + } + deferred.resolve(args); + }); + } + }; + observers[name] = obs; + args[name] = []; + args[name].observer = obs; + cps.addObserverForName(name, obs); + }); + + return { + observers, + promise: new Promise(resolve => { + deferred = { resolve }; + }), + }; +} + +async function schemaVersionIs(expectedVersion) { + let db = await sendMessage("db"); + equal(await db.getSchemaVersion(), expectedVersion); +} + +function wait() { + return new Promise(resolve => executeSoon(resolve)); +} + +function observerArgsOK(actualArgs, expectedArgs) { + notEqual(actualArgs, undefined); + arraysOfArraysOK(actualArgs, expectedArgs); +} diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_extractDomain.js b/toolkit/components/contentprefs/tests/unit_cps2/test_extractDomain.js new file mode 100644 index 0000000000..2e3fe5675a --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_extractDomain.js @@ -0,0 +1,26 @@ +/* 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/. */ + +function run_test() { + let tests = { + "blob:": "blob:", + "blob:https://chat.mozilla.org/35d6a992-6e18-4957-8216-070c53b9bc83": + "chat.mozilla.org", + "blob:resource://pdf.js/ed645567-3eea-4ff1-94fd-efb04812afe0": + "resource://pdf.js", + "http://example.com": "example.com", + "http://example.com/": "example.com", + "http://example.com/foo/bar/baz": "example.com", + "http://subdomain.example.com/foo/bar/baz": "subdomain.example.com", + "http://qix.quux.example.com/foo/bar/baz": "qix.quux.example.com", + "file:///home/foo/bar": "file:///home/foo/bar", + "not a url": "not a url", + }; + let cps = Cc["@mozilla.org/content-pref/service;1"].getService( + Ci.nsIContentPrefService2 + ); + for (let url in tests) { + Assert.equal(cps.extractDomain(url), tests[url]); + } +} diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js new file mode 100644 index 0000000000..d8a70a545a --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js @@ -0,0 +1,97 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + getCachedOK(["a.com", "foo"], false, undefined); + getCachedGlobalOK(["foo"], false, undefined); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["http://a.com/huh", "foo"], true, 1, "a.com"); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + getCachedOK(["a.com", "foo"], true, 1); + + await set("a.com", "bar", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + + await setGlobal("foo", 3); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + + await setGlobal("bar", 4); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["b.a.com", "foo"], true, 2); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + getCachedOK(["a.com", "foo", context], true, 6); + getCachedOK(["a.com", "bar", context], true, 2); + getCachedGlobalOK(["foo", context], true, 7); + getCachedGlobalOK(["bar", context], true, 4); + getCachedOK(["b.com", "foo", context], true, 5); + + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + getCachedOK(["b.com", "foo"], true, 5); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.getCachedByDomainAndName(null, "foo", null)); + do_check_throws(() => cps.getCachedByDomainAndName("", "foo", null)); + do_check_throws(() => cps.getCachedByDomainAndName("a.com", "", null)); + do_check_throws(() => cps.getCachedByDomainAndName("a.com", null, null)); + do_check_throws(() => cps.getCachedGlobal("", null)); + do_check_throws(() => cps.getCachedGlobal(null, null)); + await reset(); +}); + +add_task(async function casts() { + // SQLite casts booleans to integers. This makes sure the values stored in + // the cache are the same as the casted values in the database. + + await set("a.com", "foo", false); + await getOK(["a.com", "foo"], 0, "a.com", true); + getCachedOK(["a.com", "foo"], true, 0, "a.com", true); + + await set("a.com", "bar", true); + await getOK(["a.com", "bar"], 1, "a.com", true); + getCachedOK(["a.com", "bar"], true, 1, "a.com", true); + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js new file mode 100644 index 0000000000..ed15e6fb0e --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js @@ -0,0 +1,259 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + getCachedSubdomainsOK(["a.com", "foo"], []); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + + await set("a.com", "bar", 2); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + + await setGlobal("foo", 3); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + + await setGlobal("bar", 4); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", 1], + ["b.a.com", 2], + ] + ); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + await reset(); +}); + +add_task(async function populateViaGet() { + await new Promise(resolve => + cps.getByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + + await new Promise(resolve => + cps.getGlobal("foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + await reset(); +}); + +add_task(async function populateViaGetSubdomains() { + await new Promise(resolve => + cps.getBySubdomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + await reset(); +}); + +add_task(async function populateViaRemove() { + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + + await new Promise(resolve => + cps.removeBySubdomainAndName("b.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + + await new Promise(resolve => + cps.removeGlobal("foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + + await set("a.com", "foo", 1); + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + + await set("a.com", "foo", 2); + await set("b.a.com", "foo", 3); + await new Promise(resolve => + cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", undefined], + ["b.a.com", undefined], + ] + ); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", undefined]]); + + await setGlobal("foo", 4); + await new Promise(resolve => + cps.removeGlobal("foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", undefined], + ["b.a.com", undefined], + ] + ); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", undefined]]); + await reset(); +}); + +add_task(async function populateViaRemoveByDomain() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await set("b.a.com", "foo", 3); + await set("b.a.com", "bar", 4); + await new Promise(resolve => + cps.removeByDomain("a.com", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", undefined], + ["b.a.com", 3], + ] + ); + getCachedSubdomainsOK( + ["a.com", "bar"], + [ + ["a.com", undefined], + ["b.a.com", 4], + ] + ); + + await set("a.com", "foo", 5); + await set("a.com", "bar", 6); + await new Promise(resolve => + cps.removeBySubdomain("a.com", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", undefined], + ["b.a.com", undefined], + ] + ); + getCachedSubdomainsOK( + ["a.com", "bar"], + [ + ["a.com", undefined], + ["b.a.com", undefined], + ] + ); + + await setGlobal("foo", 7); + await setGlobal("bar", 8); + await new Promise(resolve => + cps.removeAllGlobals(null, makeCallback(resolve)) + ); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, undefined); + await reset(); +}); + +add_task(async function populateViaRemoveAllDomains() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await set("b.com", "foo", 3); + await set("b.com", "bar", 4); + await new Promise(resolve => + cps.removeAllDomains(null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedSubdomainsOK(["b.com", "bar"], [["b.com", undefined]]); + await reset(); +}); + +add_task(async function populateViaRemoveByName() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await new Promise(resolve => + cps.removeByName("foo", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, 4); + + await new Promise(resolve => + cps.removeByName("bar", null, makeCallback(resolve)) + ); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, undefined); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + getCachedSubdomainsOK(["a.com", "foo", context], [["a.com", 6]]); + getCachedSubdomainsOK(["a.com", "bar", context], [["a.com", 2]]); + getCachedGlobalOK(["foo", context], true, 7); + getCachedGlobalOK(["bar", context], true, 4); + getCachedSubdomainsOK(["b.com", "foo", context], [["b.com", 5]]); + + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", 5]]); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.getCachedBySubdomainAndName(null, "foo", null)); + do_check_throws(() => cps.getCachedBySubdomainAndName("", "foo", null)); + do_check_throws(() => cps.getCachedBySubdomainAndName("a.com", "", null)); + do_check_throws(() => cps.getCachedBySubdomainAndName("a.com", null, null)); + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js new file mode 100644 index 0000000000..7015174d53 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js @@ -0,0 +1,76 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function get_nonexistent() { + await getSubdomainsOK(["a.com", "foo"], []); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + await getSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + + await set("a.com", "bar", 2); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + await getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + + await setGlobal("foo", 3); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + await getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + await getSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", 1], + ["b.a.com", 2], + ] + ); + await getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + await getSubdomainsOK(["a.com", "foo", context], [["a.com", 6]]); + await getSubdomainsOK(["a.com", "bar", context], [["a.com", 2]]); + await getSubdomainsOK(["b.com", "foo", context], [["b.com", 5]]); + + await getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + await getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + await getSubdomainsOK(["b.com", "foo"], [["b.com", 5]]); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.getBySubdomainAndName(null, "foo", null, {})); + do_check_throws(() => cps.getBySubdomainAndName("", "foo", null, {})); + do_check_throws(() => cps.getBySubdomainAndName("a.com", "", null, {})); + do_check_throws(() => cps.getBySubdomainAndName("a.com", null, null, {})); + do_check_throws(() => cps.getBySubdomainAndName("a.com", "foo", null, null)); + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js new file mode 100644 index 0000000000..916fc55498 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js @@ -0,0 +1,78 @@ +/* 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/. */ + +// Dump of version we migrate from +var schema_version3 = ` +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; + CREATE TABLE groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL); + INSERT INTO "groups" VALUES(1,'foo.com'); + INSERT INTO "groups" VALUES(2,'bar.com'); + + CREATE TABLE settings (id INTEGER PRIMARY KEY, name TEXT NOT NULL); + INSERT INTO "settings" VALUES(1,'zoom-setting'); + INSERT INTO "settings" VALUES(2,'dir-setting'); + + CREATE TABLE prefs (id INTEGER PRIMARY KEY, groupID INTEGER REFERENCES groups(id), settingID INTEGER NOT NULL REFERENCES settings(id), value BLOB); + INSERT INTO "prefs" VALUES(1,1,1,0.5); + INSERT INTO "prefs" VALUES(2,1,2,'/download/dir'); + INSERT INTO "prefs" VALUES(3,2,1,0.3); + INSERT INTO "prefs" VALUES(4,NULL,1,0.1); + + CREATE INDEX groups_idx ON groups(name); + CREATE INDEX settings_idx ON settings(name); + CREATE INDEX prefs_idx ON prefs(groupID, settingID); +COMMIT;`; + +function prepareVersion3Schema(callback) { + var dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile); + dbFile.append("content-prefs.sqlite"); + + ok(!dbFile.exists(), "Db should not exist yet."); + + var dbConnection = Services.storage.openDatabase(dbFile); + equal(dbConnection.schemaVersion, 0); + + dbConnection.executeSimpleSQL(schema_version3); + dbConnection.schemaVersion = 3; + + dbConnection.close(); +} + +add_task(async function resetBeforeTests() { + prepareVersion3Schema(); +}); + +// WARNING: Database will reset after every test. This limitation comes from +// the fact that we ContentPrefService constructor is run only once per test file +// and so migration will be run only once. +add_task(async function testMigration() { + // Test migrated db content. + await schemaVersionIs(CURRENT_DB_VERSION); + let dbExpectedState = [ + [null, "zoom-setting", 0.1], + ["bar.com", "zoom-setting", 0.3], + ["foo.com", "zoom-setting", 0.5], + ["foo.com", "dir-setting", "/download/dir"], + ]; + await dbOK(dbExpectedState); + + // Migrated fields should have timestamp set to 0. + await new Promise(resolve => + cps.removeAllDomainsSince(1000, null, makeCallback(resolve)) + ); + await dbOK(dbExpectedState); + + await new Promise(resolve => + cps.removeAllDomainsSince(0, null, makeCallback(resolve)) + ); + await dbOK([[null, "zoom-setting", 0.1]]); + + // Test that dates are present after migration (column is added). + const timestamp = 1234; + await setWithDate("a.com", "pref-name", "val", timestamp); + let actualTimestamp = await getDate("a.com", "pref-name"); + equal(actualTimestamp, timestamp); + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema5.js b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema5.js new file mode 100644 index 0000000000..a904542899 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema5.js @@ -0,0 +1,66 @@ +/* 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/. */ + +ChromeUtils.defineESModuleGetters(this, { + Sqlite: "resource://gre/modules/Sqlite.sys.mjs", +}); + +const MAX_LENGTH = Ci.nsIContentPrefService2.GROUP_NAME_MAX_LENGTH; +const LONG_DATA_URL = `data:,${new Array(MAX_LENGTH).fill("x").join("")}`; + +// Dump of version we migrate from +const schema_queries = [ + "PRAGMA foreign_keys=OFF", + "CREATE TABLE groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL)", + `INSERT INTO groups VALUES (1,'foo.com'), + (2,'bar.com'), + (3,'data:image/png;base64,1234'), + (4,'file:///d/test.file'), + (5,'${LONG_DATA_URL}')`, + "CREATE TABLE settings (id INTEGER PRIMARY KEY, name TEXT NOT NULL)", + `INSERT INTO settings VALUES (1,'zoom-setting'), + (2,'browser.download.lastDir')`, + `CREATE TABLE prefs (id INTEGER PRIMARY KEY, + groupID INTEGER REFERENCES groups(id), + settingID INTEGER NOT NULL REFERENCES settings(id), + value BLOB, + timestamp INTEGER NOT NULL DEFAULT 0)`, + `INSERT INTO prefs VALUES (1,1,1,0.5,0), + (2,1,2,'/download/dir',0), + (3,2,1,0.3,0), + (4,NULL,1,0.1,0), + (5,3,2,'/download/dir',0), + (6,4,2,'/download/dir',0), + (7,5,1,0.7,0)`, + "CREATE INDEX groups_idx ON groups(name)", + "CREATE INDEX settings_idx ON settings(name)", + "CREATE INDEX prefs_idx ON prefs(timestamp, groupID, settingID)", +]; + +add_setup(async function () { + let conn = await Sqlite.openConnection({ + path: PathUtils.join(PathUtils.profileDir, "content-prefs.sqlite"), + }); + Assert.equal(await conn.getSchemaVersion(), 0); + await conn.executeTransaction(async () => { + for (let query of schema_queries) { + await conn.execute(query); + } + }); + await conn.setSchemaVersion(4); + await conn.close(); +}); + +add_task(async function test() { + // Test migrated db content. + await schemaVersionIs(CURRENT_DB_VERSION); + let dbExpectedState = [ + [null, "zoom-setting", 0.1], + ["bar.com", "zoom-setting", 0.3], + ["foo.com", "zoom-setting", 0.5], + [LONG_DATA_URL.substring(0, MAX_LENGTH - 1), "zoom-setting", 0.7], + ["foo.com", "browser.download.lastDir", "/download/dir"], + ]; + await dbOK(dbExpectedState); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema6.js b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema6.js new file mode 100644 index 0000000000..0dabd5c136 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema6.js @@ -0,0 +1,61 @@ +/* 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/. */ + +ChromeUtils.defineESModuleGetters(this, { + Sqlite: "resource://gre/modules/Sqlite.sys.mjs", +}); + +// Dump of version we migrate from +const schema_queries = [ + "PRAGMA foreign_keys=OFF", + "CREATE TABLE groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL)", + `INSERT INTO groups VALUES (1,'foo.com'), + (2,'bar.com'), + (3,'blob:resource://pdf.js/ed645567-3eea-4ff1-94fd-efb04812afe0'), + (4,'blob:https://chat.mozilla.org/35d6a992-6e18-4957-8216-070c53b9bc83')`, + "CREATE TABLE settings (id INTEGER PRIMARY KEY, name TEXT NOT NULL)", + `INSERT INTO settings VALUES (1,'zoom-setting'), + (2,'browser.download.lastDir')`, + `CREATE TABLE prefs (id INTEGER PRIMARY KEY, + groupID INTEGER REFERENCES groups(id), + settingID INTEGER NOT NULL REFERENCES settings(id), + value BLOB, + timestamp INTEGER NOT NULL DEFAULT 0)`, + `INSERT INTO prefs VALUES (1,1,1,0.5,0), + (2,1,2,'/download/dir',0), + (3,2,1,0.3,0), + (4,NULL,1,0.1,0), + (5,3,2,'/download/dir',0), + (6,4,2,'/download/dir',0), + (7,4,1,0.1,0)`, + "CREATE INDEX groups_idx ON groups(name)", + "CREATE INDEX settings_idx ON settings(name)", + "CREATE INDEX prefs_idx ON prefs(timestamp, groupID, settingID)", +]; + +add_setup(async function () { + let conn = await Sqlite.openConnection({ + path: PathUtils.join(PathUtils.profileDir, "content-prefs.sqlite"), + }); + Assert.equal(await conn.getSchemaVersion(), 0); + await conn.executeTransaction(async () => { + for (let query of schema_queries) { + await conn.execute(query); + } + }); + await conn.setSchemaVersion(5); + await conn.close(); +}); + +add_task(async function test() { + // Test migrated db content. + await schemaVersionIs(CURRENT_DB_VERSION); + let dbExpectedState = [ + [null, "zoom-setting", 0.1], + ["bar.com", "zoom-setting", 0.3], + ["foo.com", "zoom-setting", 0.5], + ["foo.com", "browser.download.lastDir", "/download/dir"], + ]; + await dbOK(dbExpectedState); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js new file mode 100644 index 0000000000..cc31cd740a --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js @@ -0,0 +1,291 @@ +/* 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/. */ + +Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); +}); + +add_task(async function resetBeforeTests() { + await reset(); +}); + +function raceWithIdleDispatch(promise) { + return Promise.race([ + promise, + new Promise(resolve => { + Services.tm.idleDispatchToMainThread(() => resolve(null)); + }), + ]); +} + +var tests = [ + async function observerForName_set(context) { + let argsPromise; + argsPromise = on("Set", ["foo", null, "bar"]); + await set("a.com", "foo", 1, context); + let args = await argsPromise; + observerArgsOK(args.foo, [["a.com", "foo", 1, context.usePrivateBrowsing]]); + observerArgsOK(args.null, [ + ["a.com", "foo", 1, context.usePrivateBrowsing], + ]); + observerArgsOK(args.bar, []); + + argsPromise = on("Set", ["foo", null, "bar"]); + await setGlobal("foo", 2, context); + args = await argsPromise; + observerArgsOK(args.foo, [[null, "foo", 2, context.usePrivateBrowsing]]); + observerArgsOK(args.null, [[null, "foo", 2, context.usePrivateBrowsing]]); + observerArgsOK(args.bar, []); + await reset(); + }, + + async function observerForName_remove(context) { + let argsPromise; + await set("a.com", "foo", 1, context); + await setGlobal("foo", 2, context); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeByDomainAndName( + "a.com", + "bogus", + context, + makeCallback(resolve) + ) + ); + let args = await raceWithIdleDispatch(argsPromise); + strictEqual(args, null); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", context, makeCallback(resolve)) + ); + args = await argsPromise; + observerArgsOK(args.foo, [["a.com", "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.null, [["a.com", "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.bar, []); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeGlobal("foo", context, makeCallback(resolve)) + ); + args = await argsPromise; + observerArgsOK(args.foo, [[null, "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.null, [[null, "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.bar, []); + await reset(); + }, + + async function observerForName_removeByDomain(context) { + let argsPromise; + await set("a.com", "foo", 1, context); + await set("b.a.com", "bar", 2, context); + await setGlobal("foo", 3, context); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeByDomain("bogus", context, makeCallback(resolve)) + ); + let args = await raceWithIdleDispatch(argsPromise); + strictEqual(args, null); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeBySubdomain("a.com", context, makeCallback(resolve)) + ); + args = await argsPromise; + observerArgsOK(args.foo, [["a.com", "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.null, [ + ["a.com", "foo", context.usePrivateBrowsing], + ["b.a.com", "bar", context.usePrivateBrowsing], + ]); + observerArgsOK(args.bar, [["b.a.com", "bar", context.usePrivateBrowsing]]); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeAllGlobals(context, makeCallback(resolve)) + ); + args = await argsPromise; + observerArgsOK(args.foo, [[null, "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.null, [[null, "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.bar, []); + await reset(); + }, + + async function observerForName_removeAllDomains(context) { + let argsPromise; + await set("a.com", "foo", 1, context); + await setGlobal("foo", 2, context); + await set("b.com", "bar", 3, context); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeAllDomains(context, makeCallback(resolve)) + ); + let args = await argsPromise; + observerArgsOK(args.foo, [["a.com", "foo", context.usePrivateBrowsing]]); + observerArgsOK(args.null, [ + ["a.com", "foo", context.usePrivateBrowsing], + ["b.com", "bar", context.usePrivateBrowsing], + ]); + observerArgsOK(args.bar, [["b.com", "bar", context.usePrivateBrowsing]]); + await reset(); + }, + + async function observerForName_removeByName(context) { + let argsPromise; + await set("a.com", "foo", 1, context); + await set("a.com", "bar", 2, context); + await setGlobal("foo", 3, context); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeByName("bogus", context, makeCallback(resolve)) + ); + let args = await raceWithIdleDispatch(argsPromise); + strictEqual(args, null); + + argsPromise = on("Removed", ["foo", null, "bar"]); + await new Promise(resolve => + cps.removeByName("foo", context, makeCallback(resolve)) + ); + args = await argsPromise; + observerArgsOK(args.foo, [ + ["a.com", "foo", context.usePrivateBrowsing], + [null, "foo", context.usePrivateBrowsing], + ]); + observerArgsOK(args.null, [ + ["a.com", "foo", context.usePrivateBrowsing], + [null, "foo", context.usePrivateBrowsing], + ]); + observerArgsOK(args.bar, []); + await reset(); + }, + + async function removeObserverForName(context) { + let { promise, observers } = onEx("Set", ["foo", null, "bar"]); + cps.removeObserverForName("foo", observers.foo); + await set("a.com", "foo", 1, context); + await wait(); + let args = await promise; + observerArgsOK(args.foo, []); + observerArgsOK(args.null, [ + ["a.com", "foo", 1, context.usePrivateBrowsing], + ]); + observerArgsOK(args.bar, []); + args.reset(); + + cps.removeObserverForName(null, args.null.observer); + await set("a.com", "foo", 2, context); + await wait(); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, []); + observerArgsOK(args.bar, []); + args.reset(); + await reset(); + }, +]; + +// These tests are for functionality that doesn't behave the same way in private and public +// contexts, so the expected results cannot be automatically generated like the previous tests. +var specialTests = [ + async function observerForName_removeAllDomainsSince() { + let argsPromise; + await setWithDate("a.com", "foo", 1, 100, null); + await setWithDate("b.com", "foo", 2, 200, null); + await setWithDate("c.com", "foo", 3, 300, null); + + await setWithDate("a.com", "bar", 1, 0, null); + await setWithDate("b.com", "bar", 2, 100, null); + await setWithDate("c.com", "bar", 3, 200, null); + await setGlobal("foo", 2, null); + + argsPromise = on("Removed", ["foo", "bar", null]); + await new Promise(resolve => + cps.removeAllDomainsSince(200, null, makeCallback(resolve)) + ); + + let args = await argsPromise; + + observerArgsOK(args.foo, [ + ["b.com", "foo", false], + ["c.com", "foo", false], + ]); + observerArgsOK(args.bar, [["c.com", "bar", false]]); + observerArgsOK(args.null, [ + ["b.com", "foo", false], + ["c.com", "bar", false], + ["c.com", "foo", false], + ]); + await reset(); + }, + + async function observerForName_removeAllDomainsSince_private() { + let argsPromise; + let context = privateLoadContext; + await setWithDate("a.com", "foo", 1, 100, context); + await setWithDate("b.com", "foo", 2, 200, context); + await setWithDate("c.com", "foo", 3, 300, context); + + await setWithDate("a.com", "bar", 1, 0, context); + await setWithDate("b.com", "bar", 2, 100, context); + await setWithDate("c.com", "bar", 3, 200, context); + await setGlobal("foo", 2, context); + + argsPromise = on("Removed", ["foo", "bar", null]); + await new Promise(resolve => + cps.removeAllDomainsSince(200, context, makeCallback(resolve)) + ); + + let args = await argsPromise; + + observerArgsOK(args.foo, [ + ["a.com", "foo", true], + ["b.com", "foo", true], + ["c.com", "foo", true], + ]); + observerArgsOK(args.bar, [ + ["a.com", "bar", true], + ["b.com", "bar", true], + ["c.com", "bar", true], + ]); + observerArgsOK(args.null, [ + ["a.com", "foo", true], + ["a.com", "bar", true], + ["b.com", "foo", true], + ["b.com", "bar", true], + ["c.com", "foo", true], + ["c.com", "bar", true], + ]); + await reset(); + }, +]; + +for (let i = 0; i < tests.length; i++) { + // Generate two wrappers of each test function that invoke the original test with an + // appropriate privacy context. + /* eslint-disable no-eval */ + var pub = eval( + "var f = async function " + + tests[i].name + + "() { await tests[" + + i + + "](privateLoadContext); }; f" + ); + var priv = eval( + "var f = async function " + + tests[i].name + + "_private() { await tests[" + + i + + "](privateLoadContext); }; f" + ); + /* eslint-enable no-eval */ + add_task(pub); + add_task(priv); +} + +for (let test of specialTests) { + add_task(test); +} diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js b/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js new file mode 100644 index 0000000000..0c9e797903 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js @@ -0,0 +1,267 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + await set("a.com", "foo", 1); + await setGlobal("foo", 2); + + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + + await new Promise(resolve => + cps.removeBySubdomainAndName("a.com", "bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + + await new Promise(resolve => + cps.removeGlobal("bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + + await new Promise(resolve => + cps.removeByDomainAndName("bogus", "bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + + await set("a.com", "foo", 2); + await new Promise(resolve => + cps.removeByDomainAndName( + "http://a.com/huh", + "foo", + null, + makeCallback(resolve) + ) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + + await new Promise(resolve => + cps.removeGlobal("foo", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], 4); + + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "bar", null, makeCallback(resolve)) + ); + await dbOK([[null, "bar", 4]]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], 4); + + await new Promise(resolve => + cps.removeGlobal("bar", null, makeCallback(resolve)) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], undefined); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([["b.a.com", "foo", 2]]); + await getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]); + await getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + + await set("a.com", "foo", 3); + await new Promise(resolve => + cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([]); + await getSubdomainsOK(["a.com", "foo"], []); + await getSubdomainsOK(["b.a.com", "foo"], []); + + await set("a.com", "foo", 4); + await set("b.a.com", "foo", 5); + await new Promise(resolve => + cps.removeByDomainAndName("b.a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([["a.com", "foo", 4]]); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + await getSubdomainsOK(["b.a.com", "foo"], []); + + await set("b.a.com", "foo", 6); + await new Promise(resolve => + cps.removeBySubdomainAndName("b.a.com", "foo", null, makeCallback(resolve)) + ); + await dbOK([["a.com", "foo", 4]]); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + await getSubdomainsOK(["b.a.com", "foo"], []); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await setGlobal("qux", 5); + await set("b.com", "foo", 6); + await set("b.com", "bar", 7); + + let context = privateLoadContext; + await set("a.com", "foo", 8, context); + await setGlobal("foo", 9, context); + await new Promise(resolve => + cps.removeByDomainAndName("a.com", "foo", context, makeCallback(resolve)) + ); + await new Promise(resolve => + cps.removeGlobal("foo", context, makeCallback(resolve)) + ); + await new Promise(resolve => + cps.removeGlobal("qux", context, makeCallback(resolve)) + ); + await new Promise(resolve => + cps.removeByDomainAndName("b.com", "foo", context, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ["b.com", "bar", 7], + ]); + await getOK(["a.com", "foo", context], undefined); + await getOK(["a.com", "bar", context], 2); + await getGlobalOK(["foo", context], undefined); + await getGlobalOK(["bar", context], 4); + await getGlobalOK(["qux", context], undefined); + await getOK(["b.com", "foo", context], undefined); + await getOK(["b.com", "bar", context], 7); + + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], 4); + await getGlobalOK(["qux"], undefined); + await getOK(["b.com", "foo"], undefined); + await getOK(["b.com", "bar"], 7); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.removeByDomainAndName(null, "foo", null)); + do_check_throws(() => cps.removeByDomainAndName("", "foo", null)); + do_check_throws(() => + cps.removeByDomainAndName("a.com", "foo", null, "bogus") + ); + do_check_throws(() => cps.removeBySubdomainAndName(null, "foo", null)); + do_check_throws(() => cps.removeBySubdomainAndName("", "foo", null)); + do_check_throws(() => + cps.removeBySubdomainAndName("a.com", "foo", null, "bogus") + ); + do_check_throws(() => cps.removeGlobal("", null)); + do_check_throws(() => cps.removeGlobal(null, null)); + do_check_throws(() => cps.removeGlobal("foo", null, "bogus")); + await reset(); +}); + +add_task(async function removeByDomainAndName_invalidateCache() { + await set("a.com", "foo", 1); + getCachedOK(["a.com", "foo"], true, 1); + let promiseRemoved = new Promise(resolve => { + cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve)); + }); + getCachedOK(["a.com", "foo"], false); + await promiseRemoved; + await reset(); +}); + +add_task(async function removeBySubdomainAndName_invalidateCache() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", 1], + ["b.a.com", 2], + ] + ); + let promiseRemoved = new Promise(resolve => { + cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback(resolve)); + }); + getCachedSubdomainsOK(["a.com", "foo"], []); + await promiseRemoved; + await reset(); +}); + +add_task(async function removeGlobal_invalidateCache() { + await setGlobal("foo", 1); + getCachedGlobalOK(["foo"], true, 1); + let promiseRemoved = new Promise(resolve => { + cps.removeGlobal("foo", null, makeCallback(resolve)); + }); + getCachedGlobalOK(["foo"], false); + await promiseRemoved; + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js new file mode 100644 index 0000000000..7615757f97 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js @@ -0,0 +1,94 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + await setGlobal("foo", 1); + await new Promise(resolve => + cps.removeAllDomains(null, makeCallback(resolve)) + ); + await dbOK([[null, "foo", 1]]); + await getGlobalOK(["foo"], 1); + await reset(); +}); + +add_task(async function domains() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + await set("b.com", "bar", 6); + + await new Promise(resolve => + cps.removeAllDomains(null, makeCallback(resolve)) + ); + await dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], undefined); + await getOK(["b.com", "bar"], undefined); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + await new Promise(resolve => + cps.removeAllDomains(context, makeCallback(resolve)) + ); + await dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo", context], undefined); + await getOK(["a.com", "bar", context], undefined); + await getGlobalOK(["foo", context], 7); + await getGlobalOK(["bar", context], 4); + await getOK(["b.com", "foo", context], undefined); + + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], undefined); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.removeAllDomains(null, "bogus")); + await reset(); +}); + +add_task(async function invalidateCache() { + await set("a.com", "foo", 1); + await set("b.com", "bar", 2); + await setGlobal("baz", 3); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["b.com", "bar"], true, 2); + getCachedGlobalOK(["baz"], true, 3); + let promiseRemoved = new Promise(resolve => + cps.removeAllDomains(null, makeCallback(resolve)) + ); + getCachedOK(["a.com", "foo"], false); + getCachedOK(["b.com", "bar"], false); + getCachedGlobalOK(["baz"], true, 3); + await promiseRemoved; + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomainsSince.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomainsSince.js new file mode 100644 index 0000000000..a78d35054b --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomainsSince.js @@ -0,0 +1,123 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + await setGlobal("foo", 1); + await new Promise(resolve => + cps.removeAllDomainsSince(0, null, makeCallback(resolve)) + ); + await getGlobalOK(["foo"], 1); + await reset(); +}); + +add_task(async function domainsAll() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + await set("b.com", "bar", 6); + + await new Promise(resolve => + cps.removeAllDomainsSince(0, null, makeCallback(resolve)) + ); + await dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], undefined); + await getOK(["b.com", "bar"], undefined); + await reset(); +}); + +add_task(async function domainsWithDate() { + await setWithDate("a.com", "foobar", 0, 0); + await setWithDate("a.com", "foo", 1, 1000); + await setWithDate("a.com", "bar", 2, 4000); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await setWithDate("b.com", "foo", 5, 2000); + await setWithDate("b.com", "bar", 6, 3000); + await setWithDate("b.com", "foobar", 7, 1000); + + await new Promise(resolve => + cps.removeAllDomainsSince(2000, null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foobar", 0], + ["a.com", "foo", 1], + [null, "foo", 3], + [null, "bar", 4], + ["b.com", "foobar", 7], + ]); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + await new Promise(resolve => + cps.removeAllDomainsSince(0, context, makeCallback(resolve)) + ); + await dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo", context], undefined); + await getOK(["a.com", "bar", context], undefined); + await getGlobalOK(["foo", context], 7); + await getGlobalOK(["bar", context], 4); + await getOK(["b.com", "foo", context], undefined); + + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], undefined); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.removeAllDomainsSince(null, "bogus")); + await reset(); +}); + +add_task(async function invalidateCache() { + await setWithDate("a.com", "foobar", 0, 0); + await setWithDate("a.com", "foo", 1, 1000); + await setWithDate("a.com", "bar", 2, 4000); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await setWithDate("b.com", "foo", 5, 2000); + await setWithDate("b.com", "bar", 6, 3000); + await setWithDate("b.com", "foobar", 7, 1000); + let clearPromise = new Promise(resolve => { + cps.removeAllDomainsSince(0, null, makeCallback(resolve)); + }); + getCachedOK(["a.com", "foobar"], false); + getCachedOK(["a.com", "foo"], false); + getCachedOK(["a.com", "bar"], false); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + getCachedOK(["b.com", "foo"], false); + getCachedOK(["b.com", "bar"], false); + getCachedOK(["b.com", "foobar"], false); + await clearPromise; + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js new file mode 100644 index 0000000000..b68d589dbf --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js @@ -0,0 +1,229 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + await set("a.com", "foo", 1); + await setGlobal("foo", 2); + + await new Promise(resolve => + cps.removeByDomain("bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + + await new Promise(resolve => + cps.removeBySubdomain("bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + await new Promise(resolve => + cps.removeByDomain("a.com", null, makeCallback(resolve)) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + + await set("a.com", "foo", 2); + await new Promise(resolve => + cps.removeByDomain("http://a.com/huh", null, makeCallback(resolve)) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + await reset(); +}); + +add_task(async function domains() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + await set("b.com", "bar", 6); + + await new Promise(resolve => + cps.removeByDomain("a.com", null, makeCallback(resolve)) + ); + await dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ["b.com", "foo", 5], + ["b.com", "bar", 6], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], 5); + await getOK(["b.com", "bar"], 6); + + await new Promise(resolve => + cps.removeAllGlobals(null, makeCallback(resolve)) + ); + await dbOK([ + ["b.com", "foo", 5], + ["b.com", "bar", 6], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], undefined); + await getOK(["b.com", "foo"], 5); + await getOK(["b.com", "bar"], 6); + + await new Promise(resolve => + cps.removeByDomain("b.com", null, makeCallback(resolve)) + ); + await dbOK([]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], undefined); + await getOK(["b.com", "foo"], undefined); + await getOK(["b.com", "bar"], undefined); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + await new Promise(resolve => + cps.removeByDomain("a.com", null, makeCallback(resolve)) + ); + await dbOK([["b.a.com", "foo", 2]]); + await getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]); + await getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + + await set("a.com", "foo", 3); + await new Promise(resolve => + cps.removeBySubdomain("a.com", null, makeCallback(resolve)) + ); + await dbOK([]); + await getSubdomainsOK(["a.com", "foo"], []); + await getSubdomainsOK(["b.a.com", "foo"], []); + + await set("a.com", "foo", 4); + await set("b.a.com", "foo", 5); + await new Promise(resolve => + cps.removeByDomain("b.a.com", null, makeCallback(resolve)) + ); + await dbOK([["a.com", "foo", 4]]); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + await getSubdomainsOK(["b.a.com", "foo"], []); + + await set("b.a.com", "foo", 6); + await new Promise(resolve => + cps.removeBySubdomain("b.a.com", null, makeCallback(resolve)) + ); + await dbOK([["a.com", "foo", 4]]); + await getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + await getSubdomainsOK(["b.a.com", "foo"], []); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await set("b.com", "foo", 7, context); + await setGlobal("foo", 8, context); + await new Promise(resolve => + cps.removeByDomain("a.com", context, makeCallback(resolve)) + ); + await getOK(["b.com", "foo", context], 7); + await getGlobalOK(["foo", context], 8); + await new Promise(resolve => + cps.removeAllGlobals(context, makeCallback(resolve)) + ); + await dbOK([["b.com", "foo", 5]]); + await getOK(["a.com", "foo", context], undefined); + await getOK(["a.com", "bar", context], undefined); + await getGlobalOK(["foo", context], undefined); + await getGlobalOK(["bar", context], undefined); + await getOK(["b.com", "foo", context], 5); + + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], undefined); + await getOK(["b.com", "foo"], 5); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.removeByDomain(null, null)); + do_check_throws(() => cps.removeByDomain("", null)); + do_check_throws(() => cps.removeByDomain("a.com", null, "bogus")); + do_check_throws(() => cps.removeBySubdomain(null, null)); + do_check_throws(() => cps.removeBySubdomain("", null)); + do_check_throws(() => cps.removeBySubdomain("a.com", null, "bogus")); + do_check_throws(() => cps.removeAllGlobals(null, "bogus")); + await reset(); +}); + +add_task(async function removeByDomain_invalidateCache() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + let promiseRemoved = new Promise(resolve => { + cps.removeByDomain("a.com", null, makeCallback(resolve)); + }); + getCachedOK(["a.com", "foo"], false); + getCachedOK(["a.com", "bar"], false); + await promiseRemoved; + await reset(); +}); + +add_task(async function removeBySubdomain_invalidateCache() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + getCachedSubdomainsOK( + ["a.com", "foo"], + [ + ["a.com", 1], + ["b.a.com", 2], + ] + ); + let promiseRemoved = new Promise(resolve => { + cps.removeBySubdomain("a.com", null, makeCallback(resolve)); + }); + getCachedSubdomainsOK(["a.com", "foo"], []); + await promiseRemoved; + await reset(); +}); + +add_task(async function removeAllGlobals_invalidateCache() { + await setGlobal("foo", 1); + await setGlobal("bar", 2); + getCachedGlobalOK(["foo"], true, 1); + getCachedGlobalOK(["bar"], true, 2); + let promiseRemoved = new Promise(resolve => { + cps.removeAllGlobals(null, makeCallback(resolve)); + }); + getCachedGlobalOK(["foo"], false); + getCachedGlobalOK(["bar"], false); + await promiseRemoved; + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js new file mode 100644 index 0000000000..b7fe310802 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js @@ -0,0 +1,102 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function nonexistent() { + await set("a.com", "foo", 1); + await setGlobal("foo", 2); + + await new Promise(resolve => + cps.removeByName("bogus", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getGlobalOK(["foo"], 2); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + await set("b.com", "bar", 6); + + await new Promise(resolve => + cps.removeByName("foo", null, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ["b.com", "bar", 6], + ]); + await getOK(["a.com", "foo"], undefined); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], undefined); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], undefined); + await getOK(["b.com", "bar"], 6); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + await set("b.com", "bar", 6); + + let context = privateLoadContext; + await set("a.com", "foo", 7, context); + await setGlobal("foo", 8, context); + await set("b.com", "bar", 9, context); + await new Promise(resolve => + cps.removeByName("bar", context, makeCallback(resolve)) + ); + await dbOK([ + ["a.com", "foo", 1], + [null, "foo", 3], + ["b.com", "foo", 5], + ]); + await getOK(["a.com", "foo", context], 7); + await getOK(["a.com", "bar", context], undefined); + await getGlobalOK(["foo", context], 8); + await getGlobalOK(["bar", context], undefined); + await getOK(["b.com", "foo", context], 5); + await getOK(["b.com", "bar", context], undefined); + + await getOK(["a.com", "foo"], 1); + await getOK(["a.com", "bar"], undefined); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], undefined); + await getOK(["b.com", "foo"], 5); + await getOK(["b.com", "bar"], undefined); + await reset(); +}); + +add_task(async function erroneous() { + do_check_throws(() => cps.removeByName("", null)); + do_check_throws(() => cps.removeByName(null, null)); + do_check_throws(() => cps.removeByName("foo", null, "bogus")); + await reset(); +}); + +add_task(async function invalidateCache() { + await set("a.com", "foo", 1); + await set("b.com", "foo", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["b.com", "foo"], true, 2); + cps.removeByName("foo", null, makeCallback()); + getCachedOK(["a.com", "foo"], false); + getCachedOK(["b.com", "foo"], false); + await reset(); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js b/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js new file mode 100644 index 0000000000..9d8dff9fda --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js @@ -0,0 +1,234 @@ +/* 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/. */ + +add_task(async function resetBeforeTests() { + await reset(); +}); + +add_task(async function get_nonexistent() { + await getOK(["a.com", "foo"], undefined); + await getGlobalOK(["foo"], undefined); + await reset(); +}); + +add_task(async function isomorphicDomains() { + await set("a.com", "foo", 1); + await dbOK([["a.com", "foo", 1]]); + await getOK(["a.com", "foo"], 1); + await getOK(["http://a.com/huh", "foo"], 1, "a.com"); + + await set("http://a.com/huh", "foo", 2); + await dbOK([["a.com", "foo", 2]]); + await getOK(["a.com", "foo"], 2); + await getOK(["http://a.com/yeah", "foo"], 2, "a.com"); + await reset(); +}); + +add_task(async function names() { + await set("a.com", "foo", 1); + await dbOK([["a.com", "foo", 1]]); + await getOK(["a.com", "foo"], 1); + + await set("a.com", "bar", 2); + await dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getOK(["a.com", "bar"], 2); + + await setGlobal("foo", 3); + await dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + ]); + await getOK(["a.com", "foo"], 1); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], 3); + + await setGlobal("bar", 4); + await dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ]); + await getOK(["a.com", "foo"], 1); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await reset(); +}); + +add_task(async function subdomains() { + await set("a.com", "foo", 1); + await set("b.a.com", "foo", 2); + await dbOK([ + ["a.com", "foo", 1], + ["b.a.com", "foo", 2], + ]); + await getOK(["a.com", "foo"], 1); + await getOK(["b.a.com", "foo"], 2); + await reset(); +}); + +add_task(async function privateBrowsing() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await setGlobal("foo", 3); + await setGlobal("bar", 4); + await set("b.com", "foo", 5); + + let context = privateLoadContext; + await set("a.com", "foo", 6, context); + await setGlobal("foo", 7, context); + await dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ["b.com", "foo", 5], + ]); + await getOK(["a.com", "foo", context], 6, "a.com"); + await getOK(["a.com", "bar", context], 2); + await getGlobalOK(["foo", context], 7); + await getGlobalOK(["bar", context], 4); + await getOK(["b.com", "foo", context], 5); + + await getOK(["a.com", "foo"], 1); + await getOK(["a.com", "bar"], 2); + await getGlobalOK(["foo"], 3); + await getGlobalOK(["bar"], 4); + await getOK(["b.com", "foo"], 5); + await reset(); +}); + +add_task(async function set_erroneous() { + do_check_throws(() => cps.set(null, "foo", 1, null)); + do_check_throws(() => cps.set("", "foo", 1, null)); + do_check_throws(() => cps.set("a.com", "", 1, null)); + do_check_throws(() => cps.set("a.com", null, 1, null)); + do_check_throws(() => cps.set("a.com", "foo", undefined, null)); + do_check_throws(() => cps.set("a.com", "foo", 1, null, "bogus")); + do_check_throws(() => cps.setGlobal("", 1, null)); + do_check_throws(() => cps.setGlobal(null, 1, null)); + do_check_throws(() => cps.setGlobal("foo", undefined, null)); + do_check_throws(() => cps.setGlobal("foo", 1, null, "bogus")); + await reset(); +}); + +add_task(async function get_erroneous() { + do_check_throws(() => cps.getByDomainAndName(null, "foo", null, {})); + do_check_throws(() => cps.getByDomainAndName("", "foo", null, {})); + do_check_throws(() => cps.getByDomainAndName("a.com", "", null, {})); + do_check_throws(() => cps.getByDomainAndName("a.com", null, null, {})); + do_check_throws(() => cps.getByDomainAndName("a.com", "foo", null, null)); + do_check_throws(() => cps.getGlobal("", null, {})); + do_check_throws(() => cps.getGlobal(null, null, {})); + do_check_throws(() => cps.getGlobal("foo", null, null)); + await reset(); +}); + +add_task(async function set_invalidateCache() { + // (1) Set a pref and wait for it to finish. + await set("a.com", "foo", 1); + + // (2) It should be cached. + getCachedOK(["a.com", "foo"], true, 1); + + // (3) Set the pref to a new value but don't wait for it to finish. + cps.set("a.com", "foo", 2, null, { + handleCompletion() { + // (6) The pref should be cached after setting it. + getCachedOK(["a.com", "foo"], true, 2); + }, + }); + + // (4) Group "a.com" and name "foo" should no longer be cached. + getCachedOK(["a.com", "foo"], false); + + // (5) Call getByDomainAndName. + let fetchedPref; + let getPromise = new Promise(resolve => + cps.getByDomainAndName("a.com", "foo", null, { + handleResult(pref) { + fetchedPref = pref; + }, + handleCompletion() { + // (7) Finally, this callback should be called after set's above. + Assert.ok(!!fetchedPref); + Assert.equal(fetchedPref.value, 2); + resolve(); + }, + }) + ); + + await getPromise; + await reset(); +}); + +add_task(async function get_nameOnly() { + await set("a.com", "foo", 1); + await set("a.com", "bar", 2); + await set("b.com", "foo", 3); + await setGlobal("foo", 4); + + await getOKEx( + "getByName", + ["foo", undefined], + [ + { domain: "a.com", name: "foo", value: 1 }, + { domain: "b.com", name: "foo", value: 3 }, + { domain: null, name: "foo", value: 4 }, + ] + ); + + let context = privateLoadContext; + await set("b.com", "foo", 5, context); + + await getOKEx( + "getByName", + ["foo", context], + [ + { domain: "a.com", name: "foo", value: 1 }, + { domain: null, name: "foo", value: 4 }, + { domain: "b.com", name: "foo", value: 5 }, + ] + ); + await reset(); +}); + +add_task(async function setSetsCurrentDate() { + // Because Date.now() is not guaranteed to be monotonically increasing + // we just do here rough sanity check with one minute tolerance. + const MINUTE = 60 * 1000; + let now = Date.now(); + let start = now - MINUTE; + let end = now + MINUTE; + await set("a.com", "foo", 1); + let timestamp = await getDate("a.com", "foo"); + Assert.lessOrEqual( + start, + timestamp, + "Timestamp is not too early (" + start + "<=" + timestamp + ")." + ); + Assert.lessOrEqual( + timestamp, + end, + "Timestamp is not too late (" + timestamp + "<=" + end + ")." + ); + await reset(); +}); + +add_task(async function maxLength() { + const MAX_LENGTH = Ci.nsIContentPrefService2.GROUP_NAME_MAX_LENGTH; + const LONG_DATA_URL = `data:,${new Array(MAX_LENGTH).fill("x").join("")}`; + await set(LONG_DATA_URL, "foo", 1); + await getOK( + [LONG_DATA_URL, "foo"], + 1, + LONG_DATA_URL.substring(0, MAX_LENGTH - 1) + ); +}); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.toml b/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.toml new file mode 100644 index 0000000000..2b679c16f9 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.toml @@ -0,0 +1,32 @@ +[DEFAULT] +head = "head.js" +skip-if = ["os == 'android'"] +support-files = ["AsyncRunner.sys.mjs"] + +["test_extractDomain.js"] + +["test_getCached.js"] + +["test_getCachedSubdomains.js"] + +["test_getSubdomains.js"] + +["test_migrationToSchema4.js"] + +["test_migrationToSchema5.js"] + +["test_migrationToSchema6.js"] + +["test_observers.js"] + +["test_remove.js"] + +["test_removeAllDomains.js"] + +["test_removeAllDomainsSince.js"] + +["test_removeByDomain.js"] + +["test_removeByName.js"] + +["test_setGet.js"] |