import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,75 @@
/* 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/. */
'use strict';
const Cc = Components.classes;
const Ci = Components.interfaces;
this.EXPORTED_SYMBOLS = ['ContentPrefInstance'];
// This is a wrapper for nsIContentPrefService that alleviates the need to pass
// an nsILoadContext argument to every method. Pass the context to the constructor
// instead and continue on your way in blissful ignorance.
this.ContentPrefInstance = function ContentPrefInstance(aContext) {
this._contentPrefSvc = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService);
this._context = aContext;
};
ContentPrefInstance.prototype = {
getPref: function ContentPrefInstance_init(aName, aGroup, aCallback) {
return this._contentPrefSvc.getPref(aName, aGroup, this._context, aCallback);
},
setPref: function ContentPrefInstance_setPref(aGroup, aName, aValue, aContext) {
return this._contentPrefSvc.setPref(aGroup, aName, aValue,
aContext ? aContext : this._context);
},
hasPref: function ContentPrefInstance_hasPref(aGroup, aName) {
return this._contentPrefSvc.hasPref(aGroup, aName, this._context);
},
hasCachedPref: function ContentPrefInstance_hasCachedPref(aGroup, aName) {
return this._contentPrefSvc.hasCachedPref(aGroup, aName, this._context);
},
removePref: function ContentPrefInstance_removePref(aGroup, aName) {
return this._contentPrefSvc.removePref(aGroup, aName, this._context);
},
removeGroupedPrefs: function ContentPrefInstance_removeGroupedPrefs() {
return this._contentPrefSvc.removeGroupedPrefs(this._context);
},
removePrefsByName: function ContentPrefInstance_removePrefsByName(aName) {
return this._contentPrefSvc.removePrefsByName(aName, this._context);
},
getPrefs: function ContentPrefInstance_getPrefs(aGroup) {
return this._contentPrefSvc.getPrefs(aGroup, this._context);
},
getPrefsByName: function ContentPrefInstance_getPrefsByName(aName) {
return this._contentPrefSvc.getPrefsByName(aName, this._context);
},
addObserver: function ContentPrefInstance_addObserver(aName, aObserver) {
return this._contentPrefSvc.addObserver(aName, aObserver);
},
removeObserver: function ContentPrefInstance_removeObserver(aName, aObserver) {
return this._contentPrefSvc.removeObserver(aName, aObserver);
},
get grouper() {
return this._contentPrefSvc.grouper;
},
get DBConnection() {
return this._contentPrefSvc.DBConnection;
}
};

View file

@ -0,0 +1,885 @@
/* 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/. */
// This file is an XPCOM component that implements nsIContentPrefService2.
// Although it's a JSM, it's not intended to be imported by consumers like JSMs
// are usually imported. It's only a JSM so that nsContentPrefService.js can
// easily use it. Consumers should access this component with the usual XPCOM
// rigmarole:
//
// Cc["@mozilla.org/content-pref/service;1"].
// getService(Ci.nsIContentPrefService2);
//
// That contract ID actually belongs to nsContentPrefService.js, which, when
// QI'ed to nsIContentPrefService2, returns an instance of this component.
//
// The plan is to eventually remove nsIContentPrefService and its
// implementation, nsContentPrefService.js. At such time this file can stop
// being a JSM, and the "_cps" parts that ContentPrefService2 relies on and
// NSGetFactory and all the other XPCOM initialization goop in
// nsContentPrefService.js can be moved here.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=699859
var EXPORTED_SYMBOLS = [
"ContentPrefService2",
];
const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/ContentPrefUtils.jsm");
Cu.import("resource://gre/modules/ContentPrefStore.jsm");
const GROUP_CLAUSE = `
SELECT id
FROM groups
WHERE name = :group OR
(:includeSubdomains AND name LIKE :pattern ESCAPE '/')
`;
function ContentPrefService2(cps) {
this._cps = cps;
this._cache = cps._cache;
this._pbStore = cps._privModeStorage;
}
ContentPrefService2.prototype = {
getByName: function CPS2_getByName(name, context, callback) {
checkNameArg(name);
checkCallbackArg(callback, true);
// Some prefs may be in both the database and the private browsing store.
// Notify the caller of such prefs only once, using the values from private
// browsing.
let pbPrefs = new ContentPrefStore();
if (context && context.usePrivateBrowsing) {
for (let [sgroup, sname, val] of this._pbStore) {
if (sname == name) {
pbPrefs.set(sgroup, sname, val);
}
}
}
let stmt1 = this._stmt(`
SELECT groups.name AS grp, prefs.value AS value
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE settings.name = :name
`);
stmt1.params.name = name;
let stmt2 = this._stmt(`
SELECT NULL AS grp, prefs.value AS value
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE settings.name = :name AND prefs.groupID ISNULL
`);
stmt2.params.name = name;
this._execStmts([stmt1, stmt2], {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let val = row.getResultByName("value");
this._cache.set(grp, name, val);
if (!pbPrefs.has(grp, name))
cbHandleResult(callback, new ContentPref(grp, name, val));
},
onDone: function onDone(reason, ok, gotRow) {
if (ok) {
for (let [pbGroup, pbName, pbVal] of pbPrefs) {
cbHandleResult(callback, new ContentPref(pbGroup, pbName, pbVal));
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
getByDomainAndName: function CPS2_getByDomainAndName(group, name, context,
callback) {
checkGroupArg(group);
this._get(group, name, false, context, callback);
},
getBySubdomainAndName: function CPS2_getBySubdomainAndName(group, name,
context,
callback) {
checkGroupArg(group);
this._get(group, name, true, context, callback);
},
getGlobal: function CPS2_getGlobal(name, context, callback) {
this._get(null, name, false, context, callback);
},
_get: function CPS2__get(group, name, includeSubdomains, context, callback) {
group = this._parseGroup(group);
checkNameArg(name);
checkCallbackArg(callback, true);
// Some prefs may be in both the database and the private browsing store.
// Notify the caller of such prefs only once, using the values from private
// browsing.
let pbPrefs = new ContentPrefStore();
if (context && context.usePrivateBrowsing) {
for (let [sgroup, val] of
this._pbStore.match(group, name, includeSubdomains)) {
pbPrefs.set(sgroup, name, val);
}
}
this._execStmts([this._commonGetStmt(group, name, includeSubdomains)], {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let val = row.getResultByName("value");
this._cache.set(grp, name, val);
if (!pbPrefs.has(group, name))
cbHandleResult(callback, new ContentPref(grp, name, val));
},
onDone: function onDone(reason, ok, gotRow) {
if (ok) {
if (!gotRow)
this._cache.set(group, name, undefined);
for (let [pbGroup, pbName, pbVal] of pbPrefs) {
cbHandleResult(callback, new ContentPref(pbGroup, pbName, pbVal));
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
_commonGetStmt: function CPS2__commonGetStmt(group, name, includeSubdomains) {
let stmt = group ?
this._stmtWithGroupClause(group, includeSubdomains, `
SELECT groups.name AS grp, prefs.value AS value
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE settings.name = :name AND prefs.groupID IN (${GROUP_CLAUSE})
`) :
this._stmt(`
SELECT NULL AS grp, prefs.value AS value
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE settings.name = :name AND prefs.groupID ISNULL
`);
stmt.params.name = name;
return stmt;
},
_stmtWithGroupClause: function CPS2__stmtWithGroupClause(group,
includeSubdomains,
sql) {
let stmt = this._stmt(sql);
stmt.params.group = group;
stmt.params.includeSubdomains = includeSubdomains || false;
stmt.params.pattern = "%." + stmt.escapeStringForLIKE(group, "/");
return stmt;
},
getCachedByDomainAndName: function CPS2_getCachedByDomainAndName(group,
name,
context) {
checkGroupArg(group);
let prefs = this._getCached(group, name, false, context);
return prefs[0] || null;
},
getCachedBySubdomainAndName: function CPS2_getCachedBySubdomainAndName(group,
name,
context,
len) {
checkGroupArg(group);
let prefs = this._getCached(group, name, true, context);
if (len)
len.value = prefs.length;
return prefs;
},
getCachedGlobal: function CPS2_getCachedGlobal(name, context) {
let prefs = this._getCached(null, name, false, context);
return prefs[0] || null;
},
_getCached: function CPS2__getCached(group, name, includeSubdomains,
context) {
group = this._parseGroup(group);
checkNameArg(name);
let storesToCheck = [this._cache];
if (context && context.usePrivateBrowsing)
storesToCheck.push(this._pbStore);
let outStore = new ContentPrefStore();
storesToCheck.forEach(function (store) {
for (let [sgroup, val] of store.match(group, name, includeSubdomains)) {
outStore.set(sgroup, name, val);
}
});
let prefs = [];
for (let [sgroup, sname, val] of outStore) {
prefs.push(new ContentPref(sgroup, sname, val));
}
return prefs;
},
set: function CPS2_set(group, name, value, context, callback) {
checkGroupArg(group);
this._set(group, name, value, context, callback);
},
setGlobal: function CPS2_setGlobal(name, value, context, callback) {
this._set(null, name, value, context, callback);
},
_set: function CPS2__set(group, name, value, context, callback) {
group = this._parseGroup(group);
checkNameArg(name);
checkValueArg(value);
checkCallbackArg(callback, false);
if (context && context.usePrivateBrowsing) {
this._pbStore.set(group, name, value);
this._schedule(function () {
cbHandleCompletion(callback, Ci.nsIContentPrefCallback2.COMPLETE_OK);
this._cps._notifyPrefSet(group, name, value, context.usePrivateBrowsing);
});
return;
}
// Invalidate the cached value so consumers accessing the cache between now
// and when the operation finishes don't get old data.
this._cache.remove(group, name);
let stmts = [];
// Create the setting if it doesn't exist.
let stmt = this._stmt(`
INSERT OR IGNORE INTO settings (id, name)
VALUES((SELECT id FROM settings WHERE name = :name), :name)
`);
stmt.params.name = name;
stmts.push(stmt);
// Create the group if it doesn't exist.
if (group) {
stmt = this._stmt(`
INSERT OR IGNORE INTO groups (id, name)
VALUES((SELECT id FROM groups WHERE name = :group), :group)
`);
stmt.params.group = group;
stmts.push(stmt);
}
// Finally create or update the pref.
if (group) {
stmt = this._stmt(`
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value, timestamp)
VALUES(
(SELECT prefs.id
FROM prefs
JOIN groups ON groups.id = prefs.groupID
JOIN settings ON settings.id = prefs.settingID
WHERE groups.name = :group AND settings.name = :name),
(SELECT id FROM groups WHERE name = :group),
(SELECT id FROM settings WHERE name = :name),
:value,
:now
)
`);
stmt.params.group = group;
}
else {
stmt = this._stmt(`
INSERT OR REPLACE INTO prefs (id, groupID, settingID, value, timestamp)
VALUES(
(SELECT prefs.id
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE prefs.groupID IS NULL AND settings.name = :name),
NULL,
(SELECT id FROM settings WHERE name = :name),
:value,
:now
)
`);
}
stmt.params.name = name;
stmt.params.value = value;
stmt.params.now = Date.now() / 1000;
stmts.push(stmt);
this._execStmts(stmts, {
onDone: function onDone(reason, ok) {
if (ok)
this._cache.setWithCast(group, name, value);
cbHandleCompletion(callback, reason);
if (ok)
this._cps._notifyPrefSet(group, name, value, context && context.usePrivateBrowsing);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
removeByDomainAndName: function CPS2_removeByDomainAndName(group, name,
context,
callback) {
checkGroupArg(group);
this._remove(group, name, false, context, callback);
},
removeBySubdomainAndName: function CPS2_removeBySubdomainAndName(group, name,
context,
callback) {
checkGroupArg(group);
this._remove(group, name, true, context, callback);
},
removeGlobal: function CPS2_removeGlobal(name, context, callback) {
this._remove(null, name, false, context, callback);
},
_remove: function CPS2__remove(group, name, includeSubdomains, context,
callback) {
group = this._parseGroup(group);
checkNameArg(name);
checkCallbackArg(callback, false);
// Invalidate the cached values so consumers accessing the cache between now
// and when the operation finishes don't get old data.
for (let sgroup of this._cache.matchGroups(group, includeSubdomains)) {
this._cache.remove(sgroup, name);
}
let stmts = [];
// First get the matching prefs.
stmts.push(this._commonGetStmt(group, name, includeSubdomains));
// Delete the matching prefs.
let stmt = this._stmtWithGroupClause(group, includeSubdomains, `
DELETE FROM prefs
WHERE settingID = (SELECT id FROM settings WHERE name = :name) AND
CASE typeof(:group)
WHEN 'null' THEN prefs.groupID IS NULL
ELSE prefs.groupID IN (${GROUP_CLAUSE})
END
`);
stmt.params.name = name;
stmts.push(stmt);
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok) {
this._cache.set(group, name, undefined);
if (isPrivate) {
for (let [sgroup, ] of
this._pbStore.match(group, name, includeSubdomains)) {
prefs.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
}
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, , ] of prefs) {
this._cps._notifyPrefRemoved(sgroup, name, isPrivate);
}
}
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
// Deletes settings and groups that are no longer used.
_settingsAndGroupsCleanupStmts: function() {
// The NOTNULL term in the subquery of the second statment is needed because of
// SQLite's weird IN behavior vis-a-vis NULLs. See http://sqlite.org/lang_expr.html.
return [
this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`),
this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`)
];
},
removeByDomain: function CPS2_removeByDomain(group, context, callback) {
checkGroupArg(group);
this._removeByDomain(group, false, context, callback);
},
removeBySubdomain: function CPS2_removeBySubdomain(group, context, callback) {
checkGroupArg(group);
this._removeByDomain(group, true, context, callback);
},
removeAllGlobals: function CPS2_removeAllGlobals(context, callback) {
this._removeByDomain(null, false, context, callback);
},
_removeByDomain: function CPS2__removeByDomain(group, includeSubdomains,
context, callback) {
group = this._parseGroup(group);
checkCallbackArg(callback, false);
// Invalidate the cached values so consumers accessing the cache between now
// and when the operation finishes don't get old data.
for (let sgroup of this._cache.matchGroups(group, includeSubdomains)) {
this._cache.removeGroup(sgroup);
}
let stmts = [];
// First get the matching prefs, then delete groups and prefs that reference
// deleted groups.
if (group) {
stmts.push(this._stmtWithGroupClause(group, includeSubdomains, `
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE prefs.groupID IN (${GROUP_CLAUSE})
`));
stmts.push(this._stmtWithGroupClause(group, includeSubdomains,
`DELETE FROM groups WHERE id IN (${GROUP_CLAUSE})`
));
stmts.push(this._stmt(`
DELETE FROM prefs
WHERE groupID NOTNULL AND groupID NOT IN (SELECT id FROM groups)
`));
}
else {
stmts.push(this._stmt(`
SELECT NULL AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE prefs.groupID IS NULL
`));
stmts.push(this._stmt(
"DELETE FROM prefs WHERE groupID IS NULL"
));
}
// Finally delete settings that are no longer referenced.
stmts.push(this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`));
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok && isPrivate) {
for (let [sgroup, sname, ] of this._pbStore) {
if (!group ||
(!includeSubdomains && group == sgroup) ||
(includeSubdomains && sgroup && this._pbStore.groupsMatchIncludingSubdomains(group, sgroup))) {
prefs.set(sgroup, sname, undefined);
this._pbStore.remove(sgroup, sname);
}
}
}
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, sname, ] of prefs) {
this._cps._notifyPrefRemoved(sgroup, sname, isPrivate);
}
}
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
_removeAllDomainsSince: function CPS2__removeAllDomainsSince(since, context, callback) {
checkCallbackArg(callback, false);
since /= 1000;
// Invalidate the cached values so consumers accessing the cache between now
// and when the operation finishes don't get old data.
// Invalidate all the group cache because we don't know which groups will be removed.
this._cache.removeAllGroups();
let stmts = [];
// Get prefs that are about to be removed to notify about their removal.
let stmt = this._stmt(`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Do the actual remove.
stmt = this._stmt(`
DELETE FROM prefs WHERE groupID NOTNULL AND timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Cleanup no longer used values.
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
// This nukes all the groups in _pbStore since we don't have their timestamp
// information.
if (ok && isPrivate) {
for (let [sgroup, sname, ] of this._pbStore) {
if (sgroup) {
prefs.set(sgroup, sname, undefined);
}
}
this._pbStore.removeAllGroups();
}
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, sname, ] of prefs) {
this._cps._notifyPrefRemoved(sgroup, sname, isPrivate);
}
}
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
removeAllDomainsSince: function CPS2_removeAllDomainsSince(since, context, callback) {
this._removeAllDomainsSince(since, context, callback);
},
removeAllDomains: function CPS2_removeAllDomains(context, callback) {
this._removeAllDomainsSince(0, context, callback);
},
removeByName: function CPS2_removeByName(name, context, callback) {
checkNameArg(name);
checkCallbackArg(callback, false);
// Invalidate the cached values so consumers accessing the cache between now
// and when the operation finishes don't get old data.
for (let [group, sname, ] of this._cache) {
if (sname == name)
this._cache.remove(group, name);
}
let stmts = [];
// First get the matching prefs. Include null if any of those prefs are
// global.
let stmt = this._stmt(`
SELECT groups.name AS grp
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE settings.name = :name
UNION
SELECT NULL AS grp
WHERE EXISTS (
SELECT prefs.id
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE settings.name = :name AND prefs.groupID IS NULL
)
`);
stmt.params.name = name;
stmts.push(stmt);
// Delete the target settings.
stmt = this._stmt(
"DELETE FROM settings WHERE name = :name"
);
stmt.params.name = name;
stmts.push(stmt);
// Delete prefs and groups that are no longer used.
stmts.push(this._stmt(
"DELETE FROM prefs WHERE settingID NOT IN (SELECT id FROM settings)"
));
stmts.push(this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`));
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok && isPrivate) {
for (let [sgroup, sname, ] of this._pbStore) {
if (sname === name) {
prefs.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
}
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, , ] of prefs) {
this._cps._notifyPrefRemoved(sgroup, name, isPrivate);
}
}
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
destroy: function CPS2_destroy() {
if (this._statements) {
for (let sql in this._statements) {
let stmt = this._statements[sql];
stmt.finalize();
}
}
},
/**
* Returns the cached mozIStorageAsyncStatement for the given SQL. If no such
* statement is cached, one is created and cached.
*
* @param sql The SQL query string.
* @return The cached, possibly new, statement.
*/
_stmt: function CPS2__stmt(sql) {
if (!this._statements)
this._statements = {};
if (!this._statements[sql])
this._statements[sql] = this._cps._dbConnection.createAsyncStatement(sql);
return this._statements[sql];
},
/**
* Executes some async statements.
*
* @param stmts An array of mozIStorageAsyncStatements.
* @param callbacks An object with the following methods:
* onRow(row) (optional)
* Called once for each result row.
* row: A mozIStorageRow.
* onDone(reason, reasonOK, didGetRow) (required)
* Called when done.
* reason: A nsIContentPrefService2.COMPLETE_* value.
* reasonOK: reason == nsIContentPrefService2.COMPLETE_OK.
* didGetRow: True if onRow was ever called.
* onError(nsresult) (optional)
* Called on error.
* nsresult: The error code.
*/
_execStmts: function CPS2__execStmts(stmts, callbacks) {
let self = this;
let gotRow = false;
this._cps._dbConnection.executeAsync(stmts, stmts.length, {
handleResult: function handleResult(results) {
try {
let row = null;
while ((row = results.getNextRow())) {
gotRow = true;
if (callbacks.onRow)
callbacks.onRow.call(self, row);
}
}
catch (err) {
Cu.reportError(err);
}
},
handleCompletion: function handleCompletion(reason) {
try {
let ok = reason == Ci.mozIStorageStatementCallback.REASON_FINISHED;
callbacks.onDone.call(self,
ok ? Ci.nsIContentPrefCallback2.COMPLETE_OK :
Ci.nsIContentPrefCallback2.COMPLETE_ERROR,
ok, gotRow);
}
catch (err) {
Cu.reportError(err);
}
},
handleError: function handleError(error) {
try {
if (callbacks.onError)
callbacks.onError.call(self, Cr.NS_ERROR_FAILURE);
}
catch (err) {
Cu.reportError(err);
}
}
});
},
/**
* Parses the domain (the "group", to use the database's term) from the given
* string.
*
* @param groupStr Assumed to be either a string or falsey.
* @return If groupStr is a valid URL string, returns the domain of
* that URL. If groupStr is some other nonempty string,
* returns groupStr itself. Otherwise returns null.
*/
_parseGroup: function CPS2__parseGroup(groupStr) {
if (!groupStr)
return null;
try {
var groupURI = Services.io.newURI(groupStr, null, null);
}
catch (err) {
return groupStr;
}
return this._cps._grouper.group(groupURI);
},
_schedule: function CPS2__schedule(fn) {
Services.tm.mainThread.dispatch(fn.bind(this),
Ci.nsIThread.DISPATCH_NORMAL);
},
addObserverForName: function CPS2_addObserverForName(name, observer) {
this._cps._addObserver(name, observer);
},
removeObserverForName: function CPS2_removeObserverForName(name, observer) {
this._cps._removeObserver(name, observer);
},
extractDomain: function CPS2_extractDomain(str) {
return this._parseGroup(str);
},
/**
* Tests use this as a backchannel by calling it directly.
*
* @param subj This value depends on topic.
* @param topic The backchannel "method" name.
* @param data This value depends on topic.
*/
observe: function CPS2_observe(subj, topic, data) {
switch (topic) {
case "test:reset":
let fn = subj.QueryInterface(Ci.xpcIJSWeakReference).get();
this._reset(fn);
break;
case "test:db":
let obj = subj.QueryInterface(Ci.xpcIJSWeakReference).get();
obj.value = this._cps._dbConnection;
break;
}
},
/**
* Removes all state from the service. Used by tests.
*
* @param callback A function that will be called when done.
*/
_reset: function CPS2__reset(callback) {
this._pbStore.removeAll();
this._cache.removeAll();
let cps = this._cps;
cps._observers = {};
cps._genericObservers = [];
let tables = ["prefs", "groups", "settings"];
let stmts = tables.map(t => this._stmt(`DELETE FROM ${t}`));
this._execStmts(stmts, { onDone: () => callback() });
},
QueryInterface: function CPS2_QueryInterface(iid) {
let supportedIIDs = [
Ci.nsIContentPrefService2,
Ci.nsIObserver,
Ci.nsISupports,
];
if (supportedIIDs.some(i => iid.equals(i)))
return this;
if (iid.equals(Ci.nsIContentPrefService))
return this._cps;
throw Cr.NS_ERROR_NO_INTERFACE;
},
};
function checkGroupArg(group) {
if (!group || typeof(group) != "string")
throw invalidArg("domain must be nonempty string.");
}
function checkNameArg(name) {
if (!name || typeof(name) != "string")
throw invalidArg("name must be nonempty string.");
}
function checkValueArg(value) {
if (value === undefined)
throw invalidArg("value must not be undefined.");
}
function checkCallbackArg(callback, required) {
if (callback && !(callback instanceof Ci.nsIContentPrefCallback2))
throw invalidArg("callback must be an nsIContentPrefCallback2.");
if (!callback && required)
throw invalidArg("callback must be given.");
}
function invalidArg(msg) {
return Components.Exception(msg, Cr.NS_ERROR_INVALID_ARG);
}

View file

@ -0,0 +1,182 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = [ "ContentPrefServiceChild" ];
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ContentPrefUtils.jsm");
Cu.import("resource://gre/modules/ContentPrefStore.jsm");
// We only need one bit of information out of the context.
function contextArg(context) {
return (context && context.usePrivateBrowsing) ?
{ usePrivateBrowsing: true } :
null;
}
function NYI() {
throw new Error("Do not add any new users of these functions");
}
function CallbackCaller(callback) {
this._callback = callback;
}
CallbackCaller.prototype = {
handleResult: function(contentPref) {
cbHandleResult(this._callback,
new ContentPref(contentPref.domain,
contentPref.name,
contentPref.value));
},
handleError: function(result) {
cbHandleError(this._callback, result);
},
handleCompletion: function(reason) {
cbHandleCompletion(this._callback, reason);
},
};
var ContentPrefServiceChild = {
QueryInterface: XPCOMUtils.generateQI([ Ci.nsIContentPrefService2 ]),
// Map from pref name -> set of observers
_observers: new Map(),
_mm: Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsIMessageSender),
_getRandomId: function() {
return Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator).generateUUID().toString();
},
// Map from random ID string -> CallbackCaller, per request
_requests: new Map(),
init: function() {
this._mm.addMessageListener("ContentPrefs:HandleResult", this);
this._mm.addMessageListener("ContentPrefs:HandleError", this);
this._mm.addMessageListener("ContentPrefs:HandleCompletion", this);
},
receiveMessage: function(msg) {
let data = msg.data;
let callback;
switch (msg.name) {
case "ContentPrefs:HandleResult":
callback = this._requests.get(data.requestId);
callback.handleResult(data.contentPref);
break;
case "ContentPrefs:HandleError":
callback = this._requests.get(data.requestId);
callback.handleError(data.error);
break;
case "ContentPrefs:HandleCompletion":
callback = this._requests.get(data.requestId);
this._requests.delete(data.requestId);
callback.handleCompletion(data.reason);
break;
case "ContentPrefs:NotifyObservers": {
let observerList = this._observers.get(data.name);
if (!observerList)
break;
for (let observer of observerList) {
safeCallback(observer, data.callback, data.args);
}
break;
}
}
},
_callFunction: function(call, args, callback) {
let requestId = this._getRandomId();
let data = { call: call, args: args, requestId: requestId };
this._mm.sendAsyncMessage("ContentPrefs:FunctionCall", data);
this._requests.set(requestId, new CallbackCaller(callback));
},
getCachedByDomainAndName: NYI,
getCachedBySubdomainAndName: NYI,
getCachedGlobal: NYI,
addObserverForName: function(name, observer) {
let set = this._observers.get(name);
if (!set) {
set = new Set();
if (this._observers.size === 0) {
// This is the first observer of any kind. Start listening for changes.
this._mm.addMessageListener("ContentPrefs:NotifyObservers", this);
}
// This is the first observer for this name. Start listening for changes
// to it.
this._mm.sendAsyncMessage("ContentPrefs:AddObserverForName", { name: name });
this._observers.set(name, set);
}
set.add(observer);
},
removeObserverForName: function(name, observer) {
let set = this._observers.get(name);
if (!set)
return;
set.delete(observer);
if (set.size === 0) {
// This was the last observer for this name. Stop listening for changes.
this._mm.sendAsyncMessage("ContentPrefs:RemoveObserverForName", { name: name });
this._observers.delete(name);
if (this._observers.size === 0) {
// This was the last observer for this process. Stop listing for all
// changes.
this._mm.removeMessageListener("ContentPrefs:NotifyObservers", this);
}
}
},
extractDomain: NYI
};
function forwardMethodToParent(method, signature, ...args) {
// Ignore superfluous arguments
args = args.slice(0, signature.length);
// Process context argument for forwarding
let contextIndex = signature.indexOf("context");
if (contextIndex > -1) {
args[contextIndex] = contextArg(args[contextIndex]);
}
// Take out the callback argument, if present.
let callbackIndex = signature.indexOf("callback");
let callback = null;
if (callbackIndex > -1 && args.length > callbackIndex) {
callback = args.splice(callbackIndex, 1)[0];
}
this._callFunction(method, args, callback);
}
for (let [method, signature] of _methodsCallableFromChild) {
ContentPrefServiceChild[method] = forwardMethodToParent.bind(ContentPrefServiceChild, method, signature);
}
ContentPrefServiceChild.init();

View file

@ -0,0 +1,137 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = [ "ContentPrefServiceParent" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/ContentPrefUtils.jsm");
var ContentPrefServiceParent = {
_cps2: null,
init: function() {
let globalMM = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
this._cps2 = Cc["@mozilla.org/content-pref/service;1"]
.getService(Ci.nsIContentPrefService2);
globalMM.addMessageListener("ContentPrefs:FunctionCall", this);
let observerChangeHandler = this.handleObserverChange.bind(this);
globalMM.addMessageListener("ContentPrefs:AddObserverForName", observerChangeHandler);
globalMM.addMessageListener("ContentPrefs:RemoveObserverForName", observerChangeHandler);
globalMM.addMessageListener("child-process-shutdown", observerChangeHandler);
},
// Map from message manager -> content pref observer.
_observers: new Map(),
handleObserverChange: function(msg) {
let observer = this._observers.get(msg.target);
if (msg.name === "child-process-shutdown") {
// If we didn't have any observers for this child process, don't do
// anything.
if (!observer)
return;
for (let i of observer._names) {
this._cps2.removeObserverForName(i, observer);
}
this._observers.delete(msg.target);
return;
}
let prefName = msg.data.name;
if (msg.name === "ContentPrefs:AddObserverForName") {
// The child process is responsible for not adding multiple parent
// observers for the same name.
if (!observer) {
observer = {
onContentPrefSet: function(group, name, value, isPrivate) {
msg.target.sendAsyncMessage("ContentPrefs:NotifyObservers",
{ name: name, callback: "onContentPrefSet",
args: [ group, name, value, isPrivate ] });
},
onContentPrefRemoved: function(group, name, isPrivate) {
msg.target.sendAsyncMessage("ContentPrefs:NotifyObservers",
{ name: name, callback: "onContentPrefRemoved",
args: [ group, name, isPrivate ] });
},
// The names we're using this observer object for, used to keep track
// of the number of names we care about as well as for removing this
// observer if its associated process goes away.
_names: new Set()
};
this._observers.set(msg.target, observer);
}
observer._names.add(prefName);
this._cps2.addObserverForName(prefName, observer);
} else {
// RemoveObserverForName
// We must have an observer.
this._cps2.removeObserverForName(prefName, observer);
observer._names.delete(prefName);
if (observer._names.size === 0) {
// This was the last use for this observer.
this._observers.delete(msg.target);
}
}
},
receiveMessage: function(msg) {
let data = msg.data;
if (!_methodsCallableFromChild.some(([method, args]) => method == data.call)) {
throw new Error(`Can't call ${data.call} from child!`);
}
let args = data.args;
let requestId = data.requestId;
let listener = {
handleResult: function(pref) {
msg.target.sendAsyncMessage("ContentPrefs:HandleResult",
{ requestId: requestId,
contentPref: {
domain: pref.domain,
name: pref.name,
value: pref.value
}
});
},
handleError: function(error) {
msg.target.sendAsyncMessage("ContentPrefs:HandleError",
{ requestId: requestId,
error: error });
},
handleCompletion: function(reason) {
msg.target.sendAsyncMessage("ContentPrefs:HandleCompletion",
{ requestId: requestId,
reason: reason });
}
};
// Push our special listener.
args.push(listener);
// And call the function.
this._cps2[data.call](...args);
}
};

View file

@ -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/. */
var EXPORTED_SYMBOLS = [
"ContentPrefStore",
];
function ContentPrefStore() {
this._groups = new Map();
this._globalNames = new Map();
}
ContentPrefStore.prototype = {
set: function CPS_set(group, name, val) {
if (group) {
if (!this._groups.has(group))
this._groups.set(group, new Map());
this._groups.get(group).set(name, val);
}
else {
this._globalNames.set(name, val);
}
},
setWithCast: function CPS_setWithCast(group, name, val) {
if (typeof(val) == "boolean")
val = val ? 1 : 0;
else if (val === undefined)
val = null;
this.set(group, name, val);
},
has: function CPS_has(group, name) {
if (group) {
return this._groups.has(group) &&
this._groups.get(group).has(name);
}
return this._globalNames.has(name);
},
get: function CPS_get(group, name) {
if (group && this._groups.has(group))
return this._groups.get(group).get(name);
return this._globalNames.get(name);
},
remove: function CPS_remove(group, name) {
if (group) {
if (this._groups.has(group)) {
this._groups.get(group).delete(name);
if (this._groups.get(group).size == 0)
this._groups.delete(group);
}
}
else {
this._globalNames.delete(name);
}
},
removeGroup: function CPS_removeGroup(group) {
if (group) {
this._groups.delete(group);
}
else {
this._globalNames.clear();
}
},
removeAllGroups: function CPS_removeAllGroups() {
this._groups.clear();
},
removeAll: function CPS_removeAll() {
this.removeAllGroups();
this._globalNames.clear();
},
groupsMatchIncludingSubdomains: function CPS_groupsMatchIncludingSubdomains(group, group2) {
let idx = group2.indexOf(group);
return (idx == group2.length - group.length &&
(idx == 0 || group2[idx - 1] == "."));
},
* [Symbol.iterator]() {
for (let [group, names] of this._groups) {
for (let [name, val] of names) {
yield [group, name, val];
}
}
for (let [name, val] of this._globalNames) {
yield [null, name, val];
}
},
* match(group, name, includeSubdomains) {
for (let sgroup of this.matchGroups(group, includeSubdomains)) {
if (this.has(sgroup, name))
yield [sgroup, this.get(sgroup, name)];
}
},
* matchGroups(group, includeSubdomains) {
if (group) {
if (includeSubdomains) {
for (let [sgroup, , ] of this) {
if (sgroup) {
if (this.groupsMatchIncludingSubdomains(group, sgroup)) {
yield sgroup;
}
}
}
}
else if (this._groups.has(group)) {
yield group;
}
}
else if (this._globalNames.size) {
yield null;
}
},
};

View file

@ -0,0 +1,70 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"ContentPref",
"cbHandleResult",
"cbHandleError",
"cbHandleCompletion",
"safeCallback",
"_methodsCallableFromChild",
];
const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function ContentPref(domain, name, value) {
this.domain = domain;
this.name = name;
this.value = value;
}
ContentPref.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPref]),
};
function cbHandleResult(callback, pref) {
safeCallback(callback, "handleResult", [pref]);
}
function cbHandleCompletion(callback, reason) {
safeCallback(callback, "handleCompletion", [reason]);
}
function cbHandleError(callback, nsresult) {
safeCallback(callback, "handleError", [nsresult]);
}
function safeCallback(callbackObj, methodName, args) {
if (!callbackObj || typeof(callbackObj[methodName]) != "function")
return;
try {
callbackObj[methodName].apply(callbackObj, args);
}
catch (err) {
Cu.reportError(err);
}
}
const _methodsCallableFromChild = Object.freeze([
["getByName", ["name", "context", "callback"]],
["getByDomainAndName", ["domain", "name", "context", "callback"]],
["getBySubdomainAndName", ["domain", "name", "context", "callback"]],
["getGlobal", ["name", "context", "callback"]],
["set", ["domain", "name", "value", "context", "callback"]],
["setGlobal", ["name", "value", "context", "callback"]],
["removeByDomainAndName", ["domain", "name", "context", "callback"]],
["removeBySubdomainAndName", ["domain", "name", "context", "callback"]],
["removeGlobal", ["name", "context", "callback"]],
["removeByDomain", ["domain", "context", "callback"]],
["removeBySubdomain", ["domain", "context", "callback"]],
["removeByName", ["name", "context", "callback"]],
["removeAllDomains", ["context", "callback"]],
["removeAllGlobals", ["context", "callback"]],
]);

View file

@ -0,0 +1,31 @@
# -*- 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/.
XPCSHELL_TESTS_MANIFESTS += [
'tests/unit/xpcshell.ini',
'tests/unit_cps2/xpcshell.ini',
]
MOCHITEST_MANIFESTS += [
'tests/mochitest/mochitest.ini'
]
EXTRA_COMPONENTS += [
'nsContentPrefService.js',
'nsContentPrefService.manifest',
]
EXTRA_JS_MODULES += [
'ContentPrefInstance.jsm',
'ContentPrefService2.jsm',
'ContentPrefServiceChild.jsm',
'ContentPrefServiceParent.jsm',
'ContentPrefStore.jsm',
'ContentPrefUtils.jsm',
]
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'Preferences')

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
component {e3f772f3-023f-4b32-b074-36cf0fd5d414} nsContentPrefService.js
contract @mozilla.org/content-pref/service;1 {e3f772f3-023f-4b32-b074-36cf0fd5d414}
component {8df290ae-dcaa-4c11-98a5-2429a4dc97bb} nsContentPrefService.js
contract @mozilla.org/content-pref/hostname-grouper;1 {8df290ae-dcaa-4c11-98a5-2429a4dc97bb}

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/mochitest/mochitest.eslintrc.js"
]
};

View file

@ -0,0 +1,4 @@
[DEFAULT]
[test_remoteContentPrefs.html]
skip-if = toolkit == 'android' || e10s # bug 783513

View file

@ -0,0 +1,311 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for nsIContentPrefService2 in child processes</title>
<script type="application/javascript"
src="/tests/SimpleTest/SimpleTest.js">
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
SimpleTest.waitForExplicitFinish();
const childFrameURL =
"data:text/html,<!DOCTYPE HTML><html><body></body></html>";
function childFrameScript(isFramePrivate) {
"use strict";
function Tester(resultArray) {
this.results = [];
}
Tester.prototype.is =
function(a, b, note) {
this.results.push([a === b, note + " (" + a + ", " + b + ")"]);
};
Tester.prototype.ok =
function(b, note) {
this.results.push([b != false, note]);
};
var cps = Components.classes["@mozilla.org/content-pref/service;1"]
.getService(Components.interfaces.nsIContentPrefService2);
let test = null;
function* test1(message) {
let tester = new Tester();
tester.ok(cps !== null, "got the content pref service");
cps.setGlobal("testing", 42, null, {
handleCompletion: function(reason) {
tester.is(reason, 0, "set a pref?");
test.next();
}
});
yield;
let numResults = 0;
cps.getGlobal("testing", null, {
handleResult: function(pref) {
numResults++;
tester.is(pref.name, "testing", "pref has the right name");
tester.is(pref.value, 42, "pref has the right value");
},
handleCompletion: function(reason) {
tester.is(reason, 0, "get a pref?");
tester.is(numResults, 1, "got the right number of prefs");
tester.is(test.next().done, true, "done with test1");
message.target.sendAsyncMessage("testRemoteContentPrefs:test1Finished",
{ results: tester.results });
}
});
yield;
}
function* test2(message) {
let tester = new Tester();
let observer;
let removed = false;
cps.addObserverForName("testName", observer = {
onContentPrefSet: function(group, name, value, isPrivate) {
if (removed) {
message.target.sendAsyncMessage("testRemoteContentPrefs:fail",
{ reason: "unexpected notification" });
}
tester.is(group, null, "group should be null");
tester.is(name, "testName", "should only see testName");
tester.is(value, 42, "value should be correct");
tester.is(isPrivate, isFramePrivate, "privacy should match");
message.target.sendAsyncMessage("testRemoteContentPrefs:test2poke2", {})
},
onContentPrefRemoved: function(group, name, isPrivate) {
tester.is(group, null, "group should be null");
tester.is(name, "testName");
tester.is(isPrivate, isFramePrivate, "privacy should match");
tester.is(test.next().done, true, "should be done with test2");
cps.removeObserverForName("testName", observer);
removed = true;
message.target.sendAsyncMessage("testRemoteContentPrefs:test2Finished",
{ results: tester.results });
}
});
message.target.sendAsyncMessage("testRemoteContentPrefs:test2poke", {});
yield;
}
function* test3(message) {
let tester = new Tester();
cps.setGlobal("testName", 42, null, {
handleCompletion: function(reason) {
tester.is(reason, 0, "set a pref");
cps.set("http://mochi.test", "testpref", "str", null, {
handleCompletion: function(reason) {
tester.is(reason, 0, "set a pref");
test.next();
}
});
}
});
yield;
cps.removeByDomain("http://mochi.test", null, {
handleCompletion: function(reason) {
tester.is(reason, 0, "remove succeeded");
cps.getByDomainAndName("http://mochi.test", "testpref", null, {
handleResult: function() {
message.target.sendAsyncMessage("testRemoteContentPrefs:fail",
{ reason: "got removed pref in test3" });
},
handleCompletion: function() {
test.next();
}
});
}
});
yield;
message.target.sendAsyncMessage("testRemoteContentPrefs:test3Finished",
{ results: tester.results });
}
function* test4(message) {
let tester = new Tester();
let prefObserver = {
onContentPrefSet: function(group, name, value, isPrivate) {
test.next({ group: group, name: name, value: value, isPrivate: isPrivate });
},
onContentPrefRemoved: function(group, name, isPrivate) {
test.next({ group: group, name: name, isPrivate: isPrivate });
}
};
addMessageListener("testRemoteContentPrefs:prefResults", (msg) => {
test.next(msg.data.results);
});
cps.addObserverForName("test", prefObserver);
cps.set("http://mochi.test", "test", 42, { usePrivateBrowsing: true });
let event = yield;
tester.is(event.name, "test");
tester.is(event.isPrivate, true);
message.target.sendAsyncMessage("testRemoteContentPrefs:getPref",
{ group: "http://mochi.test", name: "test" });
let results = yield;
tester.is(results.length, 0, "should not have seen the pb pref");
message.target.sendAsyncMessage("testRemoteContentPrefs:test4Finished",
{ results: tester.results });
}
addMessageListener("testRemoteContentPrefs:test1", function(message) {
test = test1(message);
test.next();
});
addMessageListener("testRemoteContentPrefs:test2", function(message) {
test = test2(message);
test.next();
});
addMessageListener("testRemoteContentPrefs:test3", function(message) {
test = test3(message);
test.next();
});
addMessageListener("testRemoteContentPrefs:test4", function(message) {
test = test4(message);
test.next();
});
}
function processResults(results) {
for (let i of results) {
ok(...i);
}
}
let test;
function* testStructure(mm, isPrivate, callback) {
let lastResult;
function testDone(msg) {
test.next(msg.data);
}
mm.addMessageListener("testRemoteContentPrefs:test1Finished", testDone);
mm.addMessageListener("testRemoteContentPrefs:test2Finished", testDone);
mm.addMessageListener("testRemoteContentPrefs:test3Finished", testDone);
mm.addMessageListener("testRemoteContentPrefs:test4Finished", testDone);
mm.addMessageListener("testRemoteContentPrefs:fail", function(msg) {
ok(false, msg.data.reason);
});
mm.sendAsyncMessage("testRemoteContentPrefs:test1", {});
lastResult = yield;
processResults(lastResult.results);
var cps = SpecialPowers.Cc["@mozilla.org/content-pref/service;1"]
.getService(SpecialPowers.Ci.nsIContentPrefService2);
mm.sendAsyncMessage("testRemoteContentPrefs:test2", {});
mm.addMessageListener("testRemoteContentPrefs:test2poke", function() {
cps.setGlobal("testName", 42, {usePrivateBrowsing: isPrivate});
});
mm.addMessageListener("testRemoteContentPrefs:test2poke2", function() {
cps.removeGlobal("testName", {usePrivateBrowsing: isPrivate});
});
lastResult = yield;
processResults(lastResult.results);
mm.sendAsyncMessage("testRemoteContentPrefs:test3", {});
lastResult = yield;
processResults(lastResult.results);
mm.addMessageListener("testRemoteContentPrefs:getPref", function(msg) {
let results = [];
cps.getByDomainAndName(msg.data.group, msg.data.name, null, {
handleResult: function(pref) {
results.push(pref);
},
handleCompletion: function(reason) {
mm.sendAsyncMessage("testRemoteContentPrefs:prefResults",
{ results: results });
}
});
});
mm.sendAsyncMessage("testRemoteContentPrefs:test4", {});
lastResult = yield;
processResults(lastResult.results);
document.getElementById('iframe').remove();
setTimeout(callback, 0);
}
function runTest(isPrivate, callback) {
info("testing with isPrivate=" + isPrivate);
let iframe = document.createElement("iframe");
SpecialPowers.wrap(iframe).mozbrowser = true;
if (isPrivate) {
SpecialPowers.wrap(iframe).mozprivatebrowsing = true;
}
iframe.id = "iframe";
iframe.src = childFrameURL;
iframe.addEventListener("mozbrowserloadend", function() {
info("Got iframe load event.");
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")(" + isPrivate + ");",
false);
test = testStructure(mm, isPrivate, callback);
test.next();
});
document.body.appendChild(iframe);
}
function runTests() {
info("Browser prefs set.");
runTest(false, function() {
runTest(true, function() {
SimpleTest.finish();
});
});
}
addEventListener("load", function() {
info("Got load event.");
SpecialPowers.addPermission("browser", true, document);
SpecialPowers.pushPrefEnv({
"set": [
["dom.ipc.browser_frames.oop_by_default", true],
["dom.mozBrowserFramesEnabled", true],
["browser.pagethumbnails.capturing_disabled", true]
]
}, runTests);
});
</script>
</body>
</html>

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -0,0 +1,162 @@
/* 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/. */
// Inspired by the Places infrastructure in head_bookmarks.js
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/ContentPrefInstance.jsm');
const CONTENT_PREFS_DB_FILENAME = "content-prefs.sqlite";
const CONTENT_PREFS_BACKUP_DB_FILENAME = "content-prefs.sqlite.corrupt";
var ContentPrefTest = {
// Convenience Getters
__dirSvc: null,
get _dirSvc() {
if (!this.__dirSvc)
this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
return this.__dirSvc;
},
__consoleSvc: null,
get _consoleSvc() {
if (!this.__consoleSvc)
this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
return this.__consoleSvc;
},
__ioSvc: null,
get _ioSvc() {
if (!this.__ioSvc)
this.__ioSvc = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return this.__ioSvc;
},
// nsISupports
interfaces: [Ci.nsIDirectoryServiceProvider, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
// nsIDirectoryServiceProvider
getFile: function ContentPrefTest_getFile(property, persistent) {
persistent.value = true;
if (property == "ProfD")
return this._dirSvc.get("CurProcD", Ci.nsIFile);
// This causes extraneous errors to show up in the log when the directory
// service asks us first for CurProcD and MozBinD. I wish there was a way
// to suppress those errors.
throw Cr.NS_ERROR_FAILURE;
},
// Utilities
getURI: function ContentPrefTest_getURI(spec) {
return this._ioSvc.newURI(spec, null, null);
},
/**
* Get the profile directory.
*/
getProfileDir: function ContentPrefTest_getProfileDir() {
// do_get_profile can be only called from a parent process
if (runningInParent) {
return do_get_profile();
}
// if running in a content process, this just returns the path
// profile was initialized in the ipc head file
let env = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
// the python harness sets this in the environment for us
let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(profd);
return file;
},
/**
* Delete the content pref service's persistent datastore. We do this before
* and after running tests to make sure we start from scratch each time. We
* also do it during the database creation, schema migration, and backup tests.
*/
deleteDatabase: function ContentPrefTest_deleteDatabase() {
var file = this.getProfileDir();
file.append(CONTENT_PREFS_DB_FILENAME);
if (file.exists())
try { file.remove(false); } catch (e) { /* stupid windows box */ }
return file;
},
/**
* Delete the backup of the content pref service's persistent datastore.
* We do this during the database creation, schema migration, and backup tests.
*/
deleteBackupDatabase: function ContentPrefTest_deleteBackupDatabase() {
var file = this.getProfileDir();
file.append(CONTENT_PREFS_BACKUP_DB_FILENAME);
if (file.exists())
file.remove(false);
return file;
},
/**
* Log a message to the console and the test log.
*/
log: function ContentPrefTest_log(message) {
message = "*** ContentPrefTest: " + message;
this._consoleSvc.logStringMessage(message);
print(message);
}
};
var gInPrivateBrowsing = false;
function enterPBMode() {
gInPrivateBrowsing = true;
}
function exitPBMode() {
gInPrivateBrowsing = false;
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
}
ContentPrefTest.deleteDatabase();
function inChildProcess() {
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
if (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType ==
Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
return false;
}
return true;
}
// Turn on logging for the content preferences service so we can troubleshoot
// problems with the tests. Note that we cannot do this in a child process
// without crashing (but we don't need it anyhow)
if (!inChildProcess()) {
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.preferences.content.log", true);
}

View file

@ -0,0 +1,6 @@
/* 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/. */
ContentPrefTest.deleteDatabase();
ContentPrefTest.__dirSvc = null;

View file

@ -0,0 +1,42 @@
/* 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 loadContext = { get usePrivateBrowsing() { return gInPrivateBrowsing; } };
ContentPrefTest.deleteDatabase();
var cp = new ContentPrefInstance(loadContext);
do_check_neq(cp, null, "Retrieving the content prefs service failed");
try {
const uri1 = ContentPrefTest.getURI("http://www.example.com/");
const uri2 = ContentPrefTest.getURI("http://www.anotherexample.com/");
const pref_name = "browser.content.full-zoom";
const zoomA = 1.5, zoomA_new = 0.8, zoomB = 1.3;
// save Zoom-A
cp.setPref(uri1, pref_name, zoomA);
// make sure Zoom-A is retrievable
do_check_eq(cp.getPref(uri1, pref_name), zoomA);
// enter private browsing mode
enterPBMode();
// make sure Zoom-A is retrievable
do_check_eq(cp.getPref(uri1, pref_name), zoomA);
// save Zoom-B
cp.setPref(uri2, pref_name, zoomB);
// make sure Zoom-B is retrievable
do_check_eq(cp.getPref(uri2, pref_name), zoomB);
// update Zoom-A
cp.setPref(uri1, pref_name, zoomA_new);
// make sure Zoom-A has changed
do_check_eq(cp.getPref(uri1, pref_name), zoomA_new);
// exit private browsing mode
exitPBMode();
// make sure Zoom-A change has not persisted
do_check_eq(cp.getPref(uri1, pref_name), zoomA);
// make sure Zoom-B change has not persisted
do_check_eq(cp.hasPref(uri2, pref_name), false);
} catch (e) {
do_throw("Unexpected exception: " + e);
}
}

View file

@ -0,0 +1,35 @@
/* 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() {
var cps = new ContentPrefInstance(null);
var uri = ContentPrefTest.getURI("http://www.example.com/");
do_check_thrown(function () { cps.setPref(uri, null, 8); });
do_check_thrown(function () { cps.hasPref(uri, null); });
do_check_thrown(function () { cps.getPref(uri, null); });
do_check_thrown(function () { cps.removePref(uri, null); });
do_check_thrown(function () { cps.getPrefsByName(null); });
do_check_thrown(function () { cps.removePrefsByName(null); });
do_check_thrown(function () { cps.setPref(uri, "", 21); });
do_check_thrown(function () { cps.hasPref(uri, ""); });
do_check_thrown(function () { cps.getPref(uri, ""); });
do_check_thrown(function () { cps.removePref(uri, ""); });
do_check_thrown(function () { cps.getPrefsByName(""); });
do_check_thrown(function () { cps.removePrefsByName(""); });
}
function do_check_thrown (aCallback) {
var exThrown = false;
try {
aCallback();
do_throw("NS_ERROR_ILLEGAL_VALUE should have been thrown here");
} catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_ILLEGAL_VALUE);
exThrown = true;
}
do_check_true(exThrown);
}

View file

@ -0,0 +1,103 @@
/* 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 prefObserver = {
setCalledNum: 0,
onContentPrefSet: function(aGroup, aName, aValue) {
this.setCalledNum++;
},
removedCalledNum: 0,
onContentPrefRemoved: function(aGroup, aName) {
this.removedCalledNum++;
}
};
function run_test() {
let loadContext = { get usePrivateBrowsing() { return gInPrivateBrowsing; } };
var cps = new ContentPrefInstance(loadContext);
cps.removeGroupedPrefs();
var uri = ContentPrefTest.getURI("http://www.example.com/");
var group = cps.grouper.group(uri);
// first, set a pref in normal mode
cps.setPref(uri, "value", "foo");
cps.setPref(null, "value-global", "foo-global");
var num;
cps.addObserver("value", prefObserver);
cps.addObserver("value-global", prefObserver);
enterPBMode();
// test setPref
num = prefObserver.setCalledNum;
cps.setPref(uri, "value", "foo-private-browsing");
do_check_eq(cps.hasPref(uri, "value"), true);
do_check_eq(cps.getPref(uri, "value"), "foo-private-browsing");
do_check_eq(prefObserver.setCalledNum, num + 1);
num = prefObserver.setCalledNum;
cps.setPref(null, "value-global", "foo-private-browsing-global");
do_check_eq(cps.hasPref(null, "value-global"), true);
do_check_eq(cps.getPref(null, "value-global"), "foo-private-browsing-global");
do_check_eq(prefObserver.setCalledNum, num + 1);
// test removePref
num = prefObserver.removedCalledNum;
cps.removePref(uri, "value");
do_check_eq(cps.hasPref(uri, "value"), true);
// fallback to non private mode value
do_check_eq(cps.getPref(uri, "value"), "foo");
do_check_eq(prefObserver.removedCalledNum, num + 1);
num = prefObserver.removedCalledNum;
cps.removePref(null, "value-global");
do_check_eq(cps.hasPref(null, "value-global"), true);
// fallback to non private mode value
do_check_eq(cps.getPref(null, "value-global"), "foo-global") ;
do_check_eq(prefObserver.removedCalledNum, num + 1);
// test removeGroupedPrefs
cps.setPref(uri, "value", "foo-private-browsing");
cps.removeGroupedPrefs();
do_check_eq(cps.hasPref(uri, "value"), false);
do_check_eq(cps.getPref(uri, "value"), undefined);
cps.setPref(null, "value-global", "foo-private-browsing-global");
cps.removeGroupedPrefs();
do_check_eq(cps.hasPref(null, "value-global"), true);
do_check_eq(cps.getPref(null, "value-global"), "foo-private-browsing-global");
// test removePrefsByName
num = prefObserver.removedCalledNum;
cps.setPref(uri, "value", "foo-private-browsing");
cps.removePrefsByName("value");
do_check_eq(cps.hasPref(uri, "value"), false);
do_check_eq(cps.getPref(uri, "value"), undefined);
do_check_true(prefObserver.removedCalledNum > num);
num = prefObserver.removedCalledNum;
cps.setPref(null, "value-global", "foo-private-browsing");
cps.removePrefsByName("value-global");
do_check_eq(cps.hasPref(null, "value-global"), false);
do_check_eq(cps.getPref(null, "value-global"), undefined);
do_check_true(prefObserver.removedCalledNum > num);
// test getPrefs
cps.setPref(uri, "value", "foo-private-browsing");
do_check_eq(cps.getPrefs(uri).getProperty("value"), "foo-private-browsing");
cps.setPref(null, "value-global", "foo-private-browsing-global");
do_check_eq(cps.getPrefs(null).getProperty("value-global"), "foo-private-browsing-global");
// test getPrefsByName
do_check_eq(cps.getPrefsByName("value").getProperty(group), "foo-private-browsing");
do_check_eq(cps.getPrefsByName("value-global").getProperty(null), "foo-private-browsing-global");
cps.removeObserver("value", prefObserver);
cps.removeObserver("value-global", prefObserver);
}

View file

@ -0,0 +1,463 @@
/* 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() {
// Database Creation, Schema Migration, and Backup
// Note: in these tests we use createInstance instead of getService
// so we can instantiate the service multiple times and make it run
// its database initialization code each time.
// Create a new database.
{
ContentPrefTest.deleteDatabase();
// Get the service and make sure it has a ready database connection.
let cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
do_check_true(cps.DBConnection.connectionReady);
cps.DBConnection.close();
}
// Open an existing database.
{
let dbFile = ContentPrefTest.deleteDatabase();
let cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
cps.DBConnection.close();
do_check_true(dbFile.exists());
// Get the service and make sure it has a ready database connection.
cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
do_check_true(cps.DBConnection.connectionReady);
cps.DBConnection.close();
}
// Open an empty database.
{
let dbFile = ContentPrefTest.deleteDatabase();
// Create an empty database.
let dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
let dbConnection = dbService.openDatabase(dbFile);
do_check_eq(dbConnection.schemaVersion, 0);
dbConnection.close();
do_check_true(dbFile.exists());
// Get the service and make sure it has created the schema.
let cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
do_check_neq(cps.DBConnection.schemaVersion, 0);
cps.DBConnection.close();
}
// Open a corrupted database.
{
let dbFile = ContentPrefTest.deleteDatabase();
let backupDBFile = ContentPrefTest.deleteBackupDatabase();
// Create a corrupted database.
let foStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
foStream.init(dbFile, 0x02 | 0x08 | 0x20, 0o666, 0);
let garbageData = "garbage that makes SQLite think the file is corrupted";
foStream.write(garbageData, garbageData.length);
foStream.close();
// Get the service and make sure it backs up and recreates the database.
let cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
do_check_true(backupDBFile.exists());
do_check_true(cps.DBConnection.connectionReady);
cps.DBConnection.close();
}
// Open a database with a corrupted schema.
{
let dbFile = ContentPrefTest.deleteDatabase();
let backupDBFile = ContentPrefTest.deleteBackupDatabase();
// Create an empty database and set the schema version to a number
// that will trigger a schema migration that will fail.
let dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
let dbConnection = dbService.openDatabase(dbFile);
dbConnection.schemaVersion = -1;
dbConnection.close();
do_check_true(dbFile.exists());
// Get the service and make sure it backs up and recreates the database.
let cps = Cc["@mozilla.org/content-pref/service;1"].
createInstance(Ci.nsIContentPrefService);
do_check_true(backupDBFile.exists());
do_check_true(cps.DBConnection.connectionReady);
cps.DBConnection.close();
}
// Now get the content pref service for real for use by the rest of the tests.
let cps = new ContentPrefInstance(null);
var uri = ContentPrefTest.getURI("http://www.example.com/");
// Make sure disk synchronization checking is turned off by default.
var statement = cps.DBConnection.createStatement("PRAGMA synchronous");
statement.executeStep();
do_check_eq(0, statement.getInt32(0));
// Nonexistent Pref
do_check_eq(cps.getPref(uri, "test.nonexistent.getPref"), undefined);
do_check_eq(cps.setPref(uri, "test.nonexistent.setPref", 5), undefined);
do_check_false(cps.hasPref(uri, "test.nonexistent.hasPref"));
do_check_eq(cps.removePref(uri, "test.nonexistent.removePref"), undefined);
// Existing Pref
cps.setPref(uri, "test.existing", 5);
// getPref should return the pref value
do_check_eq(cps.getPref(uri, "test.existing"), 5);
// setPref should return undefined and change the value of the pref
do_check_eq(cps.setPref(uri, "test.existing", 6), undefined);
do_check_eq(cps.getPref(uri, "test.existing"), 6);
// hasPref should return true
do_check_true(cps.hasPref(uri, "test.existing"));
// removePref should return undefined and remove the pref
do_check_eq(cps.removePref(uri, "test.existing"), undefined);
do_check_false(cps.hasPref(uri, "test.existing"));
// Round-Trip Data Integrity
// Make sure pref values remain the same from setPref to getPref.
cps.setPref(uri, "test.data-integrity.integer", 5);
do_check_eq(cps.getPref(uri, "test.data-integrity.integer"), 5);
cps.setPref(uri, "test.data-integrity.float", 5.5);
do_check_eq(cps.getPref(uri, "test.data-integrity.float"), 5.5);
cps.setPref(uri, "test.data-integrity.boolean", true);
do_check_eq(cps.getPref(uri, "test.data-integrity.boolean"), true);
cps.setPref(uri, "test.data-integrity.string", "test");
do_check_eq(cps.getPref(uri, "test.data-integrity.string"), "test");
cps.setPref(uri, "test.data-integrity.null", null);
do_check_eq(cps.getPref(uri, "test.data-integrity.null"), null);
// XXX Test arbitrary binary data.
// Make sure hasPref and removePref work on all data types.
do_check_true(cps.hasPref(uri, "test.data-integrity.integer"));
do_check_true(cps.hasPref(uri, "test.data-integrity.float"));
do_check_true(cps.hasPref(uri, "test.data-integrity.boolean"));
do_check_true(cps.hasPref(uri, "test.data-integrity.string"));
do_check_true(cps.hasPref(uri, "test.data-integrity.null"));
do_check_eq(cps.removePref(uri, "test.data-integrity.integer"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.float"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.boolean"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.string"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.null"), undefined);
do_check_false(cps.hasPref(uri, "test.data-integrity.integer"));
do_check_false(cps.hasPref(uri, "test.data-integrity.float"));
do_check_false(cps.hasPref(uri, "test.data-integrity.boolean"));
do_check_false(cps.hasPref(uri, "test.data-integrity.string"));
do_check_false(cps.hasPref(uri, "test.data-integrity.null"));
// getPrefs
cps.setPref(uri, "test.getPrefs.a", 1);
cps.setPref(uri, "test.getPrefs.b", 2);
cps.setPref(uri, "test.getPrefs.c", 3);
var prefs = cps.getPrefs(uri);
do_check_true(prefs.hasKey("test.getPrefs.a"));
do_check_eq(prefs.get("test.getPrefs.a"), 1);
do_check_true(prefs.hasKey("test.getPrefs.b"));
do_check_eq(prefs.get("test.getPrefs.b"), 2);
do_check_true(prefs.hasKey("test.getPrefs.c"));
do_check_eq(prefs.get("test.getPrefs.c"), 3);
// Site-Specificity
{
// These are all different sites, and setting a pref for one of them
// shouldn't set it for the others.
let uri1 = ContentPrefTest.getURI("http://www.domain1.com/");
let uri2 = ContentPrefTest.getURI("http://foo.domain1.com/");
let uri3 = ContentPrefTest.getURI("http://domain1.com/");
let uri4 = ContentPrefTest.getURI("http://www.domain2.com/");
cps.setPref(uri1, "test.site-specificity.uri1", 5);
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri1"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri1"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri1"));
cps.setPref(uri2, "test.site-specificity.uri2", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri2"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri2"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri2"));
cps.setPref(uri3, "test.site-specificity.uri3", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri3"));
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri3"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri3"));
cps.setPref(uri4, "test.site-specificity.uri4", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri4"));
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri4"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri4"));
}
// Observers
var specificObserver = {
interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
numTimesSetCalled: 0,
onContentPrefSet: function specificObserver_onContentPrefSet(group, name, value) {
++this.numTimesSetCalled;
do_check_eq(group, "www.example.com");
do_check_eq(name, "test.observer.1");
do_check_eq(value, "test value");
},
numTimesRemovedCalled: 0,
onContentPrefRemoved: function specificObserver_onContentPrefRemoved(group, name) {
++this.numTimesRemovedCalled;
do_check_eq(group, "www.example.com");
do_check_eq(name, "test.observer.1");
}
};
var genericObserver = {
interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
numTimesSetCalled: 0,
onContentPrefSet: function genericObserver_onContentPrefSet(group, name, value, isPrivate) {
++this.numTimesSetCalled;
do_check_eq(group, "www.example.com");
if (name == "test.observer.private")
do_check_true(isPrivate);
else if (name == "test.observer.normal")
do_check_false(isPrivate);
else if (name != "test.observer.1" && name != "test.observer.2")
do_throw("genericObserver.onContentPrefSet: " +
"name not in (test.observer.(1|2|normal|private))");
do_check_eq(value, "test value");
},
numTimesRemovedCalled: 0,
onContentPrefRemoved: function genericObserver_onContentPrefRemoved(group, name, isPrivate) {
++this.numTimesRemovedCalled;
do_check_eq(group, "www.example.com");
if (name == "test.observer.private")
do_check_true(isPrivate);
else if (name == "test.observer.normal")
do_check_false(isPrivate);
if (name != "test.observer.1" && name != "test.observer.2" &&
name != "test.observer.normal" && name != "test.observer.private") {
do_throw("genericObserver.onContentPrefSet: " +
"name not in (test.observer.(1|2|normal|private))");
}
}
};
// Make sure we can add observers, observers get notified about changes,
// specific observers only get notified about changes to the specific setting,
// and generic observers get notified about changes to all settings.
cps.addObserver("test.observer.1", specificObserver);
cps.addObserver(null, genericObserver);
cps.setPref(uri, "test.observer.1", "test value");
cps.setPref(uri, "test.observer.2", "test value");
cps.removePref(uri, "test.observer.1");
cps.removePref(uri, "test.observer.2");
do_check_eq(specificObserver.numTimesSetCalled, 1);
do_check_eq(genericObserver.numTimesSetCalled, 2);
do_check_eq(specificObserver.numTimesRemovedCalled, 1);
do_check_eq(genericObserver.numTimesRemovedCalled, 2);
// Make sure information about private context is properly
// retrieved by the observer.
cps.setPref(uri, "test.observer.private", "test value", {usePrivateBrowsing: true});
cps.setPref(uri, "test.observer.normal", "test value", {usePrivateBrowsing: false});
cps.removePref(uri, "test.observer.private");
cps.removePref(uri, "test.observer.normal");
// Make sure we can remove observers and they don't get notified
// about changes anymore.
cps.removeObserver("test.observer.1", specificObserver);
cps.removeObserver(null, genericObserver);
cps.setPref(uri, "test.observer.1", "test value");
cps.removePref(uri, "test.observer.1", "test value");
do_check_eq(specificObserver.numTimesSetCalled, 1);
do_check_eq(genericObserver.numTimesSetCalled, 4);
do_check_eq(specificObserver.numTimesRemovedCalled, 1);
do_check_eq(genericObserver.numTimesRemovedCalled, 3);
// Get/Remove Prefs By Name
{
var anObserver = {
interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
onContentPrefSet: function anObserver_onContentPrefSet(group, name, value) {
},
expectedDomains: [],
numTimesRemovedCalled: 0,
onContentPrefRemoved: function anObserver_onContentPrefRemoved(group, name) {
++this.numTimesRemovedCalled;
// remove the domain from the list of expected domains
var index = this.expectedDomains.indexOf(group);
do_check_true(index >= 0);
this.expectedDomains.splice(index, 1);
}
};
let uri1 = ContentPrefTest.getURI("http://www.domain1.com/");
let uri2 = ContentPrefTest.getURI("http://foo.domain1.com/");
let uri3 = ContentPrefTest.getURI("http://domain1.com/");
let uri4 = ContentPrefTest.getURI("http://www.domain2.com/");
cps.setPref(uri1, "test.byname.1", 1);
cps.setPref(uri1, "test.byname.2", 2);
cps.setPref(uri2, "test.byname.1", 4);
cps.setPref(uri3, "test.byname.3", 8);
cps.setPref(uri4, "test.byname.1", 16);
cps.setPref(null, "test.byname.1", 32);
cps.setPref(null, "test.byname.2", false);
function enumerateAndCheck(testName, expectedSum, expectedDomains) {
var prefsByName = cps.getPrefsByName(testName);
var enumerator = prefsByName.enumerator;
var sum = 0;
while (enumerator.hasMoreElements()) {
var property = enumerator.getNext().QueryInterface(Components.interfaces.nsIProperty);
sum += parseInt(property.value);
// remove the domain from the list of expected domains
var index = expectedDomains.indexOf(property.name);
do_check_true(index >= 0);
expectedDomains.splice(index, 1);
}
do_check_eq(sum, expectedSum);
// check all domains have been removed from the array
do_check_eq(expectedDomains.length, 0);
}
enumerateAndCheck("test.byname.1", 53,
["foo.domain1.com", null, "www.domain1.com", "www.domain2.com"]);
enumerateAndCheck("test.byname.2", 2, ["www.domain1.com", null]);
enumerateAndCheck("test.byname.3", 8, ["domain1.com"]);
cps.addObserver("test.byname.1", anObserver);
anObserver.expectedDomains = ["foo.domain1.com", null, "www.domain1.com", "www.domain2.com"];
cps.removePrefsByName("test.byname.1");
do_check_false(cps.hasPref(uri1, "test.byname.1"));
do_check_false(cps.hasPref(uri2, "test.byname.1"));
do_check_false(cps.hasPref(uri3, "test.byname.1"));
do_check_false(cps.hasPref(uri4, "test.byname.1"));
do_check_false(cps.hasPref(null, "test.byname.1"));
do_check_true(cps.hasPref(uri1, "test.byname.2"));
do_check_true(cps.hasPref(uri3, "test.byname.3"));
do_check_eq(anObserver.numTimesRemovedCalled, 4);
do_check_eq(anObserver.expectedDomains.length, 0);
cps.removeObserver("test.byname.1", anObserver);
// Clean up after ourselves
cps.removePref(uri1, "test.byname.2");
cps.removePref(uri3, "test.byname.3");
cps.removePref(null, "test.byname.2");
}
// Clear Private Data Pref Removal
{
let uri1 = ContentPrefTest.getURI("http://www.domain1.com/");
let uri2 = ContentPrefTest.getURI("http://www.domain2.com/");
let uri3 = ContentPrefTest.getURI("http://www.domain3.com/");
let dbConnection = cps.DBConnection;
let prefCount = dbConnection.createStatement("SELECT COUNT(*) AS count FROM prefs");
let groupCount = dbConnection.createStatement("SELECT COUNT(*) AS count FROM groups");
// Add some prefs for multiple domains.
cps.setPref(uri1, "test.removeAllGroups", 1);
cps.setPref(uri2, "test.removeAllGroups", 2);
cps.setPref(uri3, "test.removeAllGroups", 3);
// Add a global pref.
cps.setPref(null, "test.removeAllGroups", 1);
// Make sure there are some prefs and groups in the database.
prefCount.executeStep();
do_check_true(prefCount.row.count > 0);
prefCount.reset();
groupCount.executeStep();
do_check_true(groupCount.row.count > 0);
groupCount.reset();
// Remove all prefs and groups from the database using the same routine
// the Clear Private Data dialog uses.
cps.removeGroupedPrefs();
// Make sure there are no longer any groups in the database and the only pref
// is the global one.
prefCount.executeStep();
do_check_true(prefCount.row.count == 1);
prefCount.reset();
groupCount.executeStep();
do_check_true(groupCount.row.count == 0);
groupCount.reset();
let globalPref = dbConnection.createStatement("SELECT groupID FROM prefs");
globalPref.executeStep();
do_check_true(globalPref.row.groupID == null);
globalPref.reset();
}
}

View file

@ -0,0 +1,244 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var cps = new ContentPrefInstance(null);
function run_test() {
testCacheWorks("test1.example.com", "test-pref1");
testHasCachedPrefFunction("test2.example.com", "test-pref2");
testSetCaches("test3.example.com", "test-pref3");
testGetCaches("test4.example.com", "test-pref4");
testRemovePrefs("test5.example.com", "test-pref5");
testTypeConversions("test6.example.com", "test-pref6");
testNonExistingPrefCachesAsUndefined("test7.example.com", "test-pref7");
testCacheEviction("test8.example.com", "test-pref8");
}
function testCacheWorks(uri, prefName) {
const CACHED_VALUE = 3;
const NEW_VALUE = 5;
cps.setPref(uri, prefName, CACHED_VALUE);
do_check_eq(cps.getPref(uri, prefName), CACHED_VALUE);
// Now change the value directly through the DB and check
// that the cached value is different
let groupId = selectValue("SELECT id FROM groups WHERE name = :param1", "id", uri);
let settingId = selectValue("SELECT id FROM settings WHERE name = :param1", "id", prefName);
let prefId = selectValue("SELECT id FROM prefs WHERE groupID = :param1 AND settingID = :param2",
"id", groupId, settingId);
let stmt = cps.DBConnection.createStatement("UPDATE prefs SET value = :value WHERE id = :id");
stmt.params.value = NEW_VALUE;
stmt.params.id = prefId;
stmt.execute();
let dbValue = selectValue("SELECT value FROM prefs WHERE id = :param1", "value", prefId);
let cacheValue = cps.getPref(uri, prefName);
do_check_eq(dbValue, NEW_VALUE);
do_check_eq(cacheValue, CACHED_VALUE);
do_check_neq(cacheValue, dbValue);
do_test_pending();
cps.getPref(uri, prefName, function (value) {
do_check_eq(dbValue, NEW_VALUE);
do_check_eq(value, CACHED_VALUE);
do_check_neq(value, dbValue);
do_test_finished();
});
}
function testHasCachedPrefFunction(uri, prefName) {
const STARTING_VALUE = 3;
const NEW_VALUE = 5;
do_check_false(isCached(uri, prefName));
cps.setPref(uri, prefName, STARTING_VALUE);
let groupId = selectValue("SELECT id FROM groups WHERE name = :param1", "id", uri);
let settingId = selectValue("SELECT id FROM settings WHERE name = :param1", "id", prefName);
let prefId = selectValue("SELECT id FROM prefs WHERE groupID = :param1 AND settingID = :param2",
"id", groupId, settingId);
do_check_neq(prefId, undefined);
let originalValue = selectValue("SELECT value FROM prefs WHERE id = :param1", "value", prefId);
do_check_eq(originalValue, STARTING_VALUE);
let stmt = cps.DBConnection.createStatement("UPDATE prefs SET value = :value WHERE id = :id");
stmt.params.value = NEW_VALUE;
stmt.params.id = prefId;
stmt.execute();
let newValue = selectValue("SELECT value FROM prefs WHERE id = :param1", "value", prefId);
do_check_eq(newValue, NEW_VALUE);
let cachedValue = cps.getPref(uri, prefName);
do_check_eq(cachedValue, STARTING_VALUE);
do_check_true(isCached(uri, prefName));
}
function testSetCaches(uri, prefName) {
cps.setPref(uri, prefName, 0);
do_check_true(isCached(uri, prefName));
}
function testRemovePrefs(uri, prefName) {
/* removePref */
cps.setPref("www1." + uri, prefName, 1);
do_check_eq(cps.getPref("www1." + uri, prefName), 1);
cps.removePref("www1." + uri, prefName);
do_check_false(isCached("www1." + uri, prefName));
do_check_false(cps.hasPref("www1." + uri, prefName));
do_check_neq(cps.getPref("www1." + uri, prefName), 1);
/* removeGroupedPrefs */
cps.setPref("www2." + uri, prefName, 2);
cps.setPref("www3." + uri, prefName, 3);
do_check_eq(cps.getPref("www2." + uri, prefName), 2);
do_check_eq(cps.getPref("www3." + uri, prefName), 3);
cps.removeGroupedPrefs();
do_check_false(isCached("www2." + uri, prefName));
do_check_false(isCached("www3." + uri, prefName));
do_check_false(cps.hasPref("www2." + uri, prefName));
do_check_false(cps.hasPref("www3." + uri, prefName));
do_check_neq(cps.getPref("www2." + uri, prefName), 2);
do_check_neq(cps.getPref("www3." + uri, prefName), 3);
/* removePrefsByName */
cps.setPref("www4." + uri, prefName, 4);
cps.setPref("www5." + uri, prefName, 5);
do_check_eq(cps.getPref("www4." + uri, prefName), 4);
do_check_eq(cps.getPref("www5." + uri, prefName), 5);
cps.removePrefsByName(prefName);
do_check_false(isCached("www4." + uri, prefName));
do_check_false(isCached("www5." + uri, prefName));
do_check_false(cps.hasPref("www4." + uri, prefName));
do_check_false(cps.hasPref("www5." + uri, prefName));
do_check_neq(cps.getPref("www4." + uri, prefName), 4);
do_check_neq(cps.getPref("www5." + uri, prefName), 5);
}
function testGetCaches(uri, prefName) {
const VALUE = 4;
let insertGroup = cps.DBConnection.createStatement("INSERT INTO groups (name) VALUES (:name)");
insertGroup.params.name = uri;
insertGroup.execute();
let groupId = cps.DBConnection.lastInsertRowID;
let insertSetting = cps.DBConnection.createStatement("INSERT INTO settings (name) VALUES (:name)");
insertSetting.params.name = prefName;
insertSetting.execute();
let settingId = cps.DBConnection.lastInsertRowID;
let insertPref = cps.DBConnection.createStatement(`
INSERT INTO prefs (groupID, settingID, value)
VALUES (:groupId, :settingId, :value)
`);
insertPref.params.groupId = groupId;
insertPref.params.settingId = settingId;
insertPref.params.value = VALUE;
insertPref.execute();
let prefId = cps.DBConnection.lastInsertRowID;
let dbValue = selectValue("SELECT value FROM prefs WHERE id = :param1", "value", prefId);
// First access from service should hit the DB
let svcValue = cps.getPref(uri, prefName);
// Second time should get the value from cache
let cacheValue = cps.getPref(uri, prefName);
do_check_eq(VALUE, dbValue);
do_check_eq(VALUE, svcValue);
do_check_eq(VALUE, cacheValue);
do_check_true(isCached(uri, prefName));
}
function testTypeConversions(uri, prefName) {
let value;
cps.setPref(uri, prefName, true);
value = cps.getPref(uri, prefName);
do_check_true(value === 1);
cps.setPref(uri, prefName, false);
value = cps.getPref(uri, prefName);
do_check_true(value === 0);
cps.setPref(uri, prefName, null);
value = cps.getPref(uri, prefName);
do_check_true(value === null);
cps.setPref(uri, prefName, undefined);
value = cps.getPref(uri, prefName);
do_check_true(value === null);
}
function testNonExistingPrefCachesAsUndefined(uri, prefName) {
do_check_false(isCached(uri, prefName));
// Cache the pref
let value = cps.getPref(uri, prefName);
do_check_true(value === undefined);
do_check_true(isCached(uri, prefName));
// Cached pref
value = cps.getPref(uri, prefName);
do_check_true(value === undefined);
}
function testCacheEviction(uri, prefName) {
cps.setPref(uri, prefName, 5);
do_check_eq(cps.getPref(uri, prefName), 5);
do_check_true(isCached(uri, prefName));
// try to evict value from cache by adding various other entries
const ENTRIES_TO_ADD = 200;
for (let i = 0; i < ENTRIES_TO_ADD; i++) {
let uriToAdd = "www" + i + uri;
cps.setPref(uriToAdd, prefName, 0);
}
do_check_false(isCached(uri, prefName));
}
function selectValue(stmt, columnName, param1, param2) {
stmt = cps.DBConnection.createStatement(stmt);
if (param1)
stmt.params.param1 = param1;
if (param2)
stmt.params.param2 = param2;
stmt.executeStep();
let val = stmt.row[columnName];
stmt.reset();
stmt.finalize();
return val;
}
function isCached(uri, prefName) {
return cps.hasCachedPref(uri, prefName);
}

View file

@ -0,0 +1,34 @@
/* 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 cps = new ContentPrefInstance(null);
var uri = ContentPrefTest.getURI("http://www.example.com/");
function run_test() {
do_test_pending();
cps.setPref(uri, "asynctest", "pie");
do_check_eq(cps.getPref(uri, "asynctest"), "pie");
cps.getPref(uri, "asynctest", function(aValue) {
do_check_eq(aValue, "pie");
testCallbackObj();
});
}
function testCallbackObj() {
cps.getPref(uri, "asynctest", {
onResult: function(aValue) {
do_check_eq(aValue, "pie");
cps.removePref(uri, "asynctest");
testNoResult();
}
});
}
function testNoResult() {
cps.getPref(uri, "asynctest", function(aValue) {
do_check_eq(aValue, undefined);
do_test_finished();
});
}

View file

@ -0,0 +1,128 @@
/* 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() {
var cps = new ContentPrefInstance(null);
// Make sure disk synchronization checking is turned off by default.
var statement = cps.DBConnection.createStatement("PRAGMA synchronous");
statement.executeStep();
do_check_eq(0, statement.getInt32(0));
// These are the different types of aGroup arguments we'll test.
var anObject = {"foo":"bar"}; // a simple object
var uri = ContentPrefTest.getURI("http://www.example.com/"); // nsIURI
var stringURI = "www.example.com"; // typeof = "string"
var stringObjectURI = new String("www.example.com"); // typeof = "object"
{
// First check that all the methods work or don't work.
function simple_test_methods(aGroup, shouldThrow) {
var prefName = "test.pref.0";
var prefValue = Math.floor(Math.random() * 100);
if (shouldThrow) {
do_check_thrown(function () { cps.getPref(aGroup, prefName); });
do_check_thrown(function () { cps.setPref(aGroup, prefName, prefValue); });
do_check_thrown(function () { cps.hasPref(aGroup, prefName); });
do_check_thrown(function () { cps.removePref(aGroup, prefName); });
do_check_thrown(function () { cps.getPrefs(aGroup); });
} else {
do_check_eq(cps.setPref(aGroup, prefName, prefValue), undefined);
do_check_true(cps.hasPref(aGroup, prefName));
do_check_eq(cps.getPref(aGroup, prefName), prefValue);
do_check_eq(cps.removePref(aGroup, prefName), undefined);
do_check_false(cps.hasPref(aGroup, prefName));
}
}
simple_test_methods(cps, true); // arbitrary nsISupports object, should throw too
simple_test_methods(anObject, true);
simple_test_methods(uri, false);
simple_test_methods(stringURI, false);
simple_test_methods(stringObjectURI, false);
}
{
// Now we'll check that each argument produces the same result.
function complex_test_methods(aGroup) {
var prefName = "test.pref.1";
var prefValue = Math.floor(Math.random() * 100);
do_check_eq(cps.setPref(aGroup, prefName, prefValue), undefined);
do_check_true(cps.hasPref(uri, prefName));
do_check_true(cps.hasPref(stringURI, prefName));
do_check_true(cps.hasPref(stringObjectURI, prefName));
do_check_eq(cps.getPref(uri, prefName), prefValue);
do_check_eq(cps.getPref(stringURI, prefName), prefValue);
do_check_eq(cps.getPref(stringObjectURI, prefName), prefValue);
do_check_eq(cps.removePref(aGroup, prefName), undefined);
do_check_false(cps.hasPref(uri, prefName));
do_check_false(cps.hasPref(stringURI, prefName));
do_check_false(cps.hasPref(stringObjectURI, prefName));
}
complex_test_methods(uri);
complex_test_methods(stringURI);
complex_test_methods(stringObjectURI);
}
{
// test getPrefs returns the same prefs
do_check_eq(cps.setPref(stringObjectURI, "test.5", 5), undefined);
do_check_eq(cps.setPref(stringURI, "test.2", 2), undefined);
do_check_eq(cps.setPref(uri, "test.1", 1), undefined);
enumerateAndCheck(cps.getPrefs(uri), 8, ["test.1", "test.2", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringURI), 8, ["test.1", "test.2", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringObjectURI), 8, ["test.1", "test.2", "test.5"]);
do_check_eq(cps.setPref(uri, "test.4", 4), undefined);
do_check_eq(cps.setPref(stringObjectURI, "test.0", 0), undefined);
enumerateAndCheck(cps.getPrefs(uri), 12, ["test.0", "test.1", "test.2", "test.4", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringURI), 12, ["test.0", "test.1", "test.2", "test.4", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringObjectURI), 12, ["test.0", "test.1", "test.2", "test.4", "test.5"]);
do_check_eq(cps.setPref(stringURI, "test.3", 3), undefined);
enumerateAndCheck(cps.getPrefs(uri), 15, ["test.0", "test.1", "test.2", "test.3", "test.4", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringURI), 15, ["test.0", "test.1", "test.2", "test.3", "test.4", "test.5"]);
enumerateAndCheck(cps.getPrefs(stringObjectURI), 15, ["test.0", "test.1", "test.2", "test.3", "test.4", "test.5"]);
}
}
function do_check_thrown (aCallback) {
var exThrown = false;
try {
aCallback();
do_throw("NS_ERROR_ILLEGAL_VALUE should have been thrown here");
} catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_ILLEGAL_VALUE);
exThrown = true;
}
do_check_true(exThrown);
}
function enumerateAndCheck(prefs, expectedSum, expectedNames) {
var enumerator = prefs.enumerator;
var sum = 0;
while (enumerator.hasMoreElements()) {
var property = enumerator.getNext().QueryInterface(Components.interfaces.nsIProperty);
sum += parseInt(property.value);
// remove the pref name from the list of expected names
var index = expectedNames.indexOf(property.name);
do_check_true(index >= 0);
expectedNames.splice(index, 1);
}
do_check_eq(sum, expectedSum);
// check all pref names have been removed from the array
do_check_eq(expectedNames.length, 0);
}

View file

@ -0,0 +1,52 @@
/* 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 cps = new ContentPrefInstance(null);
function run_test() {
var uri1 = ContentPrefTest.getURI("http://www.domain1.com/");
var uri2 = ContentPrefTest.getURI("http://foo.domain1.com/");
var uri3 = ContentPrefTest.getURI("http://domain1.com/");
var uri4 = ContentPrefTest.getURI("http://www.domain2.com/");
cps.setPref(uri1, "one", 1);
cps.setPref(uri1, "two", 2);
cps.setPref(uri2, "one", 4);
cps.setPref(uri3, "three", 8);
cps.setPref(uri4, "two", 16);
cps.removePref(uri3, "three"); // uri3 should be removed now
checkForUnusedGroups();
checkForUnusedSettings();
cps.removePrefsByName("two"); // uri4 should be removed now
checkForUnusedGroups();
checkForUnusedSettings();
cps.removeGroupedPrefs();
checkForUnusedGroups();
checkForUnusedSettings();
}
function checkForUnusedGroups() {
var stmt = cps.DBConnection.createStatement(`
SELECT COUNT(*) AS count FROM groups
WHERE id NOT IN (SELECT DISTINCT groupID FROM prefs)
`);
stmt.executeStep();
do_check_eq(0, stmt.row.count);
stmt.reset();
stmt.finalize();
}
function checkForUnusedSettings() {
var stmt = cps.DBConnection.createStatement(`
SELECT COUNT(*) AS count FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`);
stmt.executeStep();
do_check_eq(0, stmt.row.count);
stmt.reset();
stmt.finalize();
}

View file

@ -0,0 +1,12 @@
[DEFAULT]
head = head_contentPrefs.js
tail = tail_contentPrefs.js
[test_bug248970.js]
[test_bug503971.js]
[test_bug679784.js]
[test_contentPrefs.js]
[test_contentPrefsCache.js]
[test_getPrefAsync.js]
[test_stringGroups.js]
[test_unusedGroupsAndSettings.js]

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -0,0 +1,69 @@
/* 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 EXPORTED_SYMBOLS = [
"AsyncRunner",
];
const { interfaces: Ci, classes: Cc } = Components;
function AsyncRunner(callbacks) {
this._callbacks = callbacks;
this._iteratorQueue = [];
// This catches errors reported to the console, e.g., via Cu.reportError.
Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService).
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() {
Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService).
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);
}
},
};

View file

@ -0,0 +1,401 @@
/* 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 { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
var cps;
var asyncRunner;
var next;
(function init() {
// There has to be a profile directory before the CPS service is gotten.
do_get_profile();
})();
function runAsyncTests(tests, dontResetBefore = false) {
do_test_pending();
cps = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
let s = {};
Cu.import("resource://test/AsyncRunner.jsm", s);
asyncRunner = new s.AsyncRunner({
done: do_test_finished,
error: function (err) {
// xpcshell test functions like equal throw NS_ERROR_ABORT on
// failure. Ignore those and catch only uncaught exceptions.
if (err !== Cr.NS_ERROR_ABORT) {
if (err.stack) {
err = err + "\n\nTraceback (most recent call first):\n" + err.stack +
"\nUseless do_throw stack:";
}
do_throw(err);
}
},
consoleError: function (scriptErr) {
// Previously, this code checked for console errors related to the test,
// and treated them as failures. This was problematic, because our current
// very-broken exception reporting machinery in XPCWrappedJSClass reports
// errors to the console even if there's actually JS on the stack above
// that will catch them. And a lot of the tests here intentionally trigger
// error conditions on the JS-implemented XPCOM component (see erroneous()
// in test_getSubdomains.js, for example). In the old world, we got lucky,
// and the errors were never reported to the console due to happenstantial
// JSContext reasons that aren't really worth going into.
//
// So. We make sure to dump this stuff so that it shows up in the logs, but
// don't turn them into duplicate failures of the exception that was already
// propagated to the caller.
dump("AsyncRunner.jsm observed console error: " + scriptErr + "\n");
}
});
next = asyncRunner.next.bind(asyncRunner);
do_register_cleanup(function () {
asyncRunner.destroy();
asyncRunner = null;
});
tests.forEach(function (test) {
function* gen() {
do_print("Running " + test.name);
yield test();
yield reset();
}
asyncRunner.appendIterator(gen());
});
// reset() ends up calling asyncRunner.next(), starting the tests.
if (dontResetBefore) {
next();
} else {
reset();
}
}
function makeCallback(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 {
next();
}
};
return callbacks;
}
function do_check_throws(fn) {
let threw = false;
try {
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() {
sendMessage("reset", next);
}
function setWithDate(group, name, val, timestamp, context) {
function updateDate() {
let db = sendMessage("db");
let stmt = db.createAsyncStatement(`
UPDATE prefs SET timestamp = :timestamp
WHERE
settingID = (SELECT id FROM settings WHERE name = :name)
AND groupID = (SELECT id FROM groups WHERE name = :group)
`);
stmt.params.timestamp = timestamp / 1000;
stmt.params.name = name;
stmt.params.group = group;
stmt.executeAsync({
handleCompletion: function (reason) {
next();
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
cps.set(group, name, val, context, makeCallback(null, updateDate));
}
function getDate(group, name, context) {
let db = sendMessage("db");
let stmt = db.createAsyncStatement(`
SELECT timestamp FROM prefs
WHERE
settingID = (SELECT id FROM settings WHERE name = :name)
AND groupID = (SELECT id FROM groups WHERE name = :group)
`);
stmt.params.name = name;
stmt.params.group = group;
let res;
stmt.executeAsync({
handleResult: function (results) {
let row = results.getNextRow();
res = row.getResultByName("timestamp");
},
handleCompletion: function (reason) {
next(res * 1000);
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
function set(group, name, val, context) {
cps.set(group, name, val, context, makeCallback());
}
function setGlobal(name, val, context) {
cps.setGlobal(name, val, context, makeCallback());
}
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);
}
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 }];
yield getOKEx("getByDomainAndName", args, expectedPrefs, strict);
}
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 };
});
yield getOKEx("getBySubdomainAndName", args, expectedPrefs);
}
function* getGlobalOK(args, expectedVal) {
if (args.length == 1)
args.push(undefined);
let expectedPrefs = expectedVal === undefined ? [] :
[{ domain: null, name: args[0], value: expectedVal }];
yield getOKEx("getGlobal", args, expectedPrefs);
}
function* getOKEx(methodName, args, expectedPrefs, strict, context) {
let actualPrefs = [];
args.push(makeCallback({
handleResult: pref => actualPrefs.push(pref)
}));
yield 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 len = {};
args.push(len);
let actualPrefs = cps.getCachedBySubdomainAndName.apply(cps, args);
actualPrefs = actualPrefs.sort(function (a, b) {
return a.domain.localeCompare(b.domain);
});
equal(actualPrefs.length, len.value);
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)
});
}
function dbOK(expectedRows) {
let db = sendMessage("db");
let stmt = db.createAsyncStatement(`
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 actualRows = [];
let cols = ["grp", "name", "value"];
db.executeAsync([stmt], 1, {
handleCompletion: function (reason) {
arraysOfArraysOK(actualRows, expectedRows);
next();
},
handleResult: function (results) {
let row = null;
while (row = results.getNextRow()) {
actualRows.push(cols.map(c => row.getResultByName(c)));
}
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
function on(event, names, dontRemove) {
let args = {
reset: function () {
for (let prop in this) {
if (Array.isArray(this[prop]))
this[prop].splice(0, this[prop].length);
}
},
};
let observers = {};
names.forEach(function (name) {
let obs = {};
["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) {
obs[meth] = () => do_throw(meth + " should not be called");
});
obs["onContentPref" + event] = function () {
args[name].push(Array.slice(arguments));
};
observers[name] = obs;
args[name] = [];
args[name].observer = obs;
cps.addObserverForName(name, obs);
});
do_execute_soon(function () {
if (!dontRemove)
names.forEach(n => cps.removeObserverForName(n, observers[n]));
next(args);
});
}
function schemaVersionIs(expectedVersion) {
let db = sendMessage("db");
equal(db.schemaVersion, expectedVersion);
}
function wait() {
do_execute_soon(next);
}
function observerArgsOK(actualArgs, expectedArgs) {
notEqual(actualArgs, undefined);
arraysOfArraysOK(actualArgs, expectedArgs);
}

View file

@ -0,0 +1,20 @@
/* 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 = {
"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) {
do_check_eq(cps.extractDomain(url), tests[url]);
}
}

View file

@ -0,0 +1,95 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
getCachedOK(["a.com", "foo"], false, undefined);
getCachedGlobalOK(["foo"], false, undefined);
yield true;
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["http://a.com/huh", "foo"], true, 1, "a.com");
},
function* names() {
yield set("a.com", "foo", 1);
getCachedOK(["a.com", "foo"], true, 1);
yield set("a.com", "bar", 2);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["a.com", "bar"], true, 2);
yield setGlobal("foo", 3);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["a.com", "bar"], true, 2);
getCachedGlobalOK(["foo"], true, 3);
yield setGlobal("bar", 4);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["a.com", "bar"], true, 2);
getCachedGlobalOK(["foo"], true, 3);
getCachedGlobalOK(["bar"], true, 4);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["b.a.com", "foo"], true, 2);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield 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);
},
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));
yield true;
},
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.
yield set("a.com", "foo", false);
yield getOK(["a.com", "foo"], 0, "a.com", true);
getCachedOK(["a.com", "foo"], true, 0, "a.com", true);
yield set("a.com", "bar", true);
yield getOK(["a.com", "bar"], 1, "a.com", true);
getCachedOK(["a.com", "bar"], true, 1, "a.com", true);
},
];

View file

@ -0,0 +1,186 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
getCachedSubdomainsOK(["a.com", "foo"], []);
yield true;
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
getCachedSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]);
},
function* names() {
yield set("a.com", "foo", 1);
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield set("a.com", "bar", 2);
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
yield setGlobal("foo", 3);
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
getCachedGlobalOK(["foo"], true, 3);
yield 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);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield 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]]);
},
function* populateViaGet() {
yield cps.getByDomainAndName("a.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
yield cps.getGlobal("foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedGlobalOK(["foo"], true, undefined);
},
function* populateViaGetSubdomains() {
yield cps.getBySubdomainAndName("a.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
},
function* populateViaRemove() {
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
yield cps.removeBySubdomainAndName("b.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]);
yield cps.removeGlobal("foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]);
getCachedGlobalOK(["foo"], true, undefined);
yield set("a.com", "foo", 1);
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]);
getCachedGlobalOK(["foo"], true, undefined);
yield set("a.com", "foo", 2);
yield set("b.a.com", "foo", 3);
yield cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback());
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]]);
yield setGlobal("foo", 4);
yield cps.removeGlobal("foo", null, makeCallback());
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]]);
},
function* populateViaRemoveByDomain() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield set("b.a.com", "foo", 3);
yield set("b.a.com", "bar", 4);
yield cps.removeByDomain("a.com", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"],
[["a.com", undefined], ["b.a.com", 3]]);
getCachedSubdomainsOK(["a.com", "bar"],
[["a.com", undefined], ["b.a.com", 4]]);
yield set("a.com", "foo", 5);
yield set("a.com", "bar", 6);
yield cps.removeBySubdomain("a.com", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"],
[["a.com", undefined], ["b.a.com", undefined]]);
getCachedSubdomainsOK(["a.com", "bar"],
[["a.com", undefined], ["b.a.com", undefined]]);
yield setGlobal("foo", 7);
yield setGlobal("bar", 8);
yield cps.removeAllGlobals(null, makeCallback());
getCachedGlobalOK(["foo"], true, undefined);
getCachedGlobalOK(["bar"], true, undefined);
},
function* populateViaRemoveAllDomains() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield set("b.com", "foo", 3);
yield set("b.com", "bar", 4);
yield cps.removeAllDomains(null, makeCallback());
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]]);
},
function* populateViaRemoveByName() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield cps.removeByName("foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
getCachedGlobalOK(["foo"], true, undefined);
getCachedGlobalOK(["bar"], true, 4);
yield cps.removeByName("bar", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]);
getCachedSubdomainsOK(["a.com", "bar"], [["a.com", undefined]]);
getCachedGlobalOK(["foo"], true, undefined);
getCachedGlobalOK(["bar"], true, undefined);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield 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]]);
},
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));
yield true;
},
];

View file

@ -0,0 +1,68 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* get_nonexistent() {
yield getSubdomainsOK(["a.com", "foo"], []);
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield getSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]);
},
function* names() {
yield set("a.com", "foo", 1);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield set("a.com", "bar", 2);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
yield setGlobal("foo", 3);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1], ["b.a.com", 2]]);
yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield setGlobal("foo", 7, context);
yield getSubdomainsOK(["a.com", "foo", context], [["a.com", 6]]);
yield getSubdomainsOK(["a.com", "bar", context], [["a.com", 2]]);
yield getSubdomainsOK(["b.com", "foo", context], [["b.com", 5]]);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]);
yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]);
yield getSubdomainsOK(["b.com", "foo"], [["b.com", 5]]);
},
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));
yield true;
},
];

View file

@ -0,0 +1,82 @@
/* 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 dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
var dbFile = dirService.get("ProfD", Ci.nsIFile);
dbFile.append("content-prefs.sqlite");
var dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
ok(!dbFile.exists(), "Db should not exist yet.");
var dbConnection = dbService.openDatabase(dbFile);
equal(dbConnection.schemaVersion, 0);
dbConnection.executeSimpleSQL(schema_version3);
dbConnection.schemaVersion = 3;
dbConnection.close();
}
function run_test() {
prepareVersion3Schema();
runAsyncTests(tests, true);
}
// 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.
var tests = [
function* testMigration() {
// Test migrated db content.
schemaVersionIs(4);
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"],
];
yield dbOK(dbExpectedState);
// Migrated fields should have timestamp set to 0.
yield cps.removeAllDomainsSince(1000, null, makeCallback());
yield dbOK(dbExpectedState);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield dbOK([[null, "zoom-setting", 0.1]]);
// Test that dates are present after migration (column is added).
const timestamp = 1234;
yield setWithDate("a.com", "pref-name", "val", timestamp);
let actualTimestamp = yield getDate("a.com", "pref-name");
equal(actualTimestamp, timestamp);
}
];

View file

@ -0,0 +1,178 @@
/* 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 global = this;
function run_test() {
var allTests = [];
for (var i = 0; i < tests.length; i++) {
// Generate two wrappers of each test function that invoke the original test with an
// appropriate privacy context.
var pub = eval('var f = function* ' + tests[i].name + '() { yield tests[' + i + ']({ usePrivateBrowsing: false }); }; f');
var priv = eval('var f = function* ' + tests[i].name + '_private() { yield tests[' + i + ']({ usePrivateBrowsing: true }); }; f');
allTests.push(pub);
allTests.push(priv);
}
allTests = allTests.concat(specialTests);
runAsyncTests(allTests);
}
var tests = [
function* observerForName_set(context) {
yield set("a.com", "foo", 1, context);
let args = yield on("Set", ["foo", null, "bar"]);
observerArgsOK(args.foo, [["a.com", "foo", 1, context.usePrivateBrowsing]]);
observerArgsOK(args.null, [["a.com", "foo", 1, context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
yield setGlobal("foo", 2, context);
args = yield on("Set", ["foo", null, "bar"]);
observerArgsOK(args.foo, [[null, "foo", 2, context.usePrivateBrowsing]]);
observerArgsOK(args.null, [[null, "foo", 2, context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
},
function* observerForName_remove(context) {
yield set("a.com", "foo", 1, context);
yield setGlobal("foo", 2, context);
yield cps.removeByDomainAndName("a.com", "bogus", context, makeCallback());
let args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
yield cps.removeByDomainAndName("a.com", "foo", context, makeCallback());
args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, [["a.com", "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.null, [["a.com", "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
yield cps.removeGlobal("foo", context, makeCallback());
args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, [[null, "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.null, [[null, "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
},
function* observerForName_removeByDomain(context) {
yield set("a.com", "foo", 1, context);
yield set("b.a.com", "bar", 2, context);
yield setGlobal("foo", 3, context);
yield cps.removeByDomain("bogus", context, makeCallback());
let args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
yield cps.removeBySubdomain("a.com", context, makeCallback());
args = yield on("Removed", ["foo", null, "bar"]);
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]]);
yield cps.removeAllGlobals(context, makeCallback());
args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, [[null, "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.null, [[null, "foo", context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
},
function* observerForName_removeAllDomains(context) {
yield set("a.com", "foo", 1, context);
yield setGlobal("foo", 2, context);
yield set("b.com", "bar", 3, context);
yield cps.removeAllDomains(context, makeCallback());
let args = yield on("Removed", ["foo", null, "bar"]);
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]]);
},
function* observerForName_removeByName(context) {
yield set("a.com", "foo", 1, context);
yield set("a.com", "bar", 2, context);
yield setGlobal("foo", 3, context);
yield cps.removeByName("bogus", context, makeCallback());
let args = yield on("Removed", ["foo", null, "bar"]);
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
yield cps.removeByName("foo", context, makeCallback());
args = yield on("Removed", ["foo", null, "bar"]);
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, []);
},
function* removeObserverForName(context) {
let args = yield on("Set", ["foo", null, "bar"], true);
cps.removeObserverForName("foo", args.foo.observer);
yield set("a.com", "foo", 1, context);
yield wait();
observerArgsOK(args.foo, []);
observerArgsOK(args.null, [["a.com", "foo", 1, context.usePrivateBrowsing]]);
observerArgsOK(args.bar, []);
args.reset();
cps.removeObserverForName(null, args.null.observer);
yield set("a.com", "foo", 2, context);
yield wait();
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
args.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 = [
function* observerForName_removeAllDomainsSince() {
yield setWithDate("a.com", "foo", 1, 100, null);
yield setWithDate("b.com", "foo", 2, 200, null);
yield setWithDate("c.com", "foo", 3, 300, null);
yield setWithDate("a.com", "bar", 1, 0, null);
yield setWithDate("b.com", "bar", 2, 100, null);
yield setWithDate("c.com", "bar", 3, 200, null);
yield setGlobal("foo", 2, null);
yield cps.removeAllDomainsSince(200, null, makeCallback());
let args = yield on("Removed", ["foo", "bar", null]);
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]]);
},
function* observerForName_removeAllDomainsSince_private() {
let context = {usePrivateBrowsing: true};
yield setWithDate("a.com", "foo", 1, 100, context);
yield setWithDate("b.com", "foo", 2, 200, context);
yield setWithDate("c.com", "foo", 3, 300, context);
yield setWithDate("a.com", "bar", 1, 0, context);
yield setWithDate("b.com", "bar", 2, 100, context);
yield setWithDate("c.com", "bar", 3, 200, context);
yield setGlobal("foo", 2, context);
yield cps.removeAllDomainsSince(200, context, makeCallback());
let args = yield on("Removed", ["foo", "bar", null]);
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]]);
},
];

View file

@ -0,0 +1,222 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);
yield cps.removeByDomainAndName("a.com", "bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
yield cps.removeBySubdomainAndName("a.com", "bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
yield cps.removeGlobal("bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
yield cps.removeByDomainAndName("bogus", "bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
yield dbOK([]);
yield getOK(["a.com", "foo"], undefined);
yield set("a.com", "foo", 2);
yield cps.removeByDomainAndName("http://a.com/huh", "foo", null,
makeCallback());
yield dbOK([]);
yield getOK(["a.com", "foo"], undefined);
},
function* names() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
yield dbOK([
["a.com", "bar", 2],
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield cps.removeGlobal("foo", null, makeCallback());
yield dbOK([
["a.com", "bar", 2],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], 4);
yield cps.removeByDomainAndName("a.com", "bar", null, makeCallback());
yield dbOK([
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], 4);
yield cps.removeGlobal("bar", null, makeCallback());
yield dbOK([
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], undefined);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
yield dbOK([
["b.a.com", "foo", 2],
]);
yield getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]);
yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]);
yield set("a.com", "foo", 3);
yield cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback());
yield dbOK([
]);
yield getSubdomainsOK(["a.com", "foo"], []);
yield getSubdomainsOK(["b.a.com", "foo"], []);
yield set("a.com", "foo", 4);
yield set("b.a.com", "foo", 5);
yield cps.removeByDomainAndName("b.a.com", "foo", null, makeCallback());
yield dbOK([
["a.com", "foo", 4],
]);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]);
yield getSubdomainsOK(["b.a.com", "foo"], []);
yield set("b.a.com", "foo", 6);
yield cps.removeBySubdomainAndName("b.a.com", "foo", null, makeCallback());
yield dbOK([
["a.com", "foo", 4],
]);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]);
yield getSubdomainsOK(["b.a.com", "foo"], []);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield setGlobal("qux", 5);
yield set("b.com", "foo", 6);
yield set("b.com", "bar", 7);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 8, context);
yield setGlobal("foo", 9, context);
yield cps.removeByDomainAndName("a.com", "foo", context, makeCallback());
yield cps.removeGlobal("foo", context, makeCallback());
yield cps.removeGlobal("qux", context, makeCallback());
yield cps.removeByDomainAndName("b.com", "foo", context, makeCallback());
yield dbOK([
["a.com", "bar", 2],
[null, "bar", 4],
["b.com", "bar", 7],
]);
yield getOK(["a.com", "foo", context], undefined);
yield getOK(["a.com", "bar", context], 2);
yield getGlobalOK(["foo", context], undefined);
yield getGlobalOK(["bar", context], 4);
yield getGlobalOK(["qux", context], undefined);
yield getOK(["b.com", "foo", context], undefined);
yield getOK(["b.com", "bar", context], 7);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], 4);
yield getGlobalOK(["qux"], undefined);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], 7);
},
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"));
yield true;
},
function* removeByDomainAndName_invalidateCache() {
yield set("a.com", "foo", 1);
getCachedOK(["a.com", "foo"], true, 1);
cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
getCachedOK(["a.com", "foo"], false);
yield;
},
function* removeBySubdomainAndName_invalidateCache() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
getCachedSubdomainsOK(["a.com", "foo"], [
["a.com", 1],
["b.a.com", 2],
]);
cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], []);
yield;
},
function* removeGlobal_invalidateCache() {
yield setGlobal("foo", 1);
getCachedGlobalOK(["foo"], true, 1);
cps.removeGlobal("foo", null, makeCallback());
getCachedGlobalOK(["foo"], false);
yield;
},
];

View file

@ -0,0 +1,87 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
yield setGlobal("foo", 1);
yield cps.removeAllDomains(null, makeCallback());
yield dbOK([
[null, "foo", 1],
]);
yield getGlobalOK(["foo"], 1);
},
function* domains() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
yield cps.removeAllDomains(null, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], undefined);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield setGlobal("foo", 7, context);
yield cps.removeAllDomains(context, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo", context], undefined);
yield getOK(["a.com", "bar", context], undefined);
yield getGlobalOK(["foo", context], 7);
yield getGlobalOK(["bar", context], 4);
yield getOK(["b.com", "foo", context], undefined);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
},
function* erroneous() {
do_check_throws(() => cps.removeAllDomains(null, "bogus"));
yield true;
},
function* invalidateCache() {
yield set("a.com", "foo", 1);
yield set("b.com", "bar", 2);
yield setGlobal("baz", 3);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["b.com", "bar"], true, 2);
getCachedGlobalOK(["baz"], true, 3);
cps.removeAllDomains(null, makeCallback());
getCachedOK(["a.com", "foo"], false);
getCachedOK(["b.com", "bar"], false);
getCachedGlobalOK(["baz"], true, 3);
yield;
},
];

View file

@ -0,0 +1,111 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
yield setGlobal("foo", 1);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield getGlobalOK(["foo"], 1);
},
function* domainsAll() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
yield cps.removeAllDomainsSince(0, null, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], undefined);
},
function* domainsWithDate() {
yield setWithDate("a.com", "foobar", 0, 0);
yield setWithDate("a.com", "foo", 1, 1000);
yield setWithDate("a.com", "bar", 2, 4000);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield setWithDate("b.com", "foo", 5, 2000);
yield setWithDate("b.com", "bar", 6, 3000);
yield setWithDate("b.com", "foobar", 7, 1000);
yield cps.removeAllDomainsSince(2000, null, makeCallback());
yield dbOK([
["a.com", "foobar", 0],
["a.com", "foo", 1],
[null, "foo", 3],
[null, "bar", 4],
["b.com", "foobar", 7],
]);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield setGlobal("foo", 7, context);
yield cps.removeAllDomainsSince(0, context, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo", context], undefined);
yield getOK(["a.com", "bar", context], undefined);
yield getGlobalOK(["foo", context], 7);
yield getGlobalOK(["bar", context], 4);
yield getOK(["b.com", "foo", context], undefined);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
},
function* erroneous() {
do_check_throws(() => cps.removeAllDomainsSince(null, "bogus"));
yield true;
},
function* invalidateCache() {
yield setWithDate("a.com", "foobar", 0, 0);
yield setWithDate("a.com", "foo", 1, 1000);
yield setWithDate("a.com", "bar", 2, 4000);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield setWithDate("b.com", "foo", 5, 2000);
yield setWithDate("b.com", "bar", 6, 3000);
yield setWithDate("b.com", "foobar", 7, 1000);
cps.removeAllDomainsSince(0, null, makeCallback());
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);
yield true;
},
];

View file

@ -0,0 +1,199 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);
yield cps.removeByDomain("bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
yield cps.removeBySubdomain("bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
yield cps.removeByDomain("a.com", null, makeCallback());
yield dbOK([]);
yield getOK(["a.com", "foo"], undefined);
yield set("a.com", "foo", 2);
yield cps.removeByDomain("http://a.com/huh", null, makeCallback());
yield dbOK([]);
yield getOK(["a.com", "foo"], undefined);
},
function* domains() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
yield cps.removeByDomain("a.com", null, makeCallback());
yield dbOK([
[null, "foo", 3],
[null, "bar", 4],
["b.com", "foo", 5],
["b.com", "bar", 6],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], 5);
yield getOK(["b.com", "bar"], 6);
yield cps.removeAllGlobals(null, makeCallback());
yield dbOK([
["b.com", "foo", 5],
["b.com", "bar", 6],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], undefined);
yield getOK(["b.com", "foo"], 5);
yield getOK(["b.com", "bar"], 6);
yield cps.removeByDomain("b.com", null, makeCallback());
yield dbOK([
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], undefined);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], undefined);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
yield cps.removeByDomain("a.com", null, makeCallback());
yield dbOK([
["b.a.com", "foo", 2],
]);
yield getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]);
yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]);
yield set("a.com", "foo", 3);
yield cps.removeBySubdomain("a.com", null, makeCallback());
yield dbOK([
]);
yield getSubdomainsOK(["a.com", "foo"], []);
yield getSubdomainsOK(["b.a.com", "foo"], []);
yield set("a.com", "foo", 4);
yield set("b.a.com", "foo", 5);
yield cps.removeByDomain("b.a.com", null, makeCallback());
yield dbOK([
["a.com", "foo", 4],
]);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]);
yield getSubdomainsOK(["b.a.com", "foo"], []);
yield set("b.a.com", "foo", 6);
yield cps.removeBySubdomain("b.a.com", null, makeCallback());
yield dbOK([
["a.com", "foo", 4],
]);
yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]);
yield getSubdomainsOK(["b.a.com", "foo"], []);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield set("b.com", "foo", 7, context);
yield setGlobal("foo", 8, context);
yield cps.removeByDomain("a.com", context, makeCallback());
yield getOK(["b.com", "foo", context], 7);
yield getGlobalOK(["foo", context], 8);
yield cps.removeAllGlobals(context, makeCallback());
yield dbOK([
["b.com", "foo", 5],
]);
yield getOK(["a.com", "foo", context], undefined);
yield getOK(["a.com", "bar", context], undefined);
yield getGlobalOK(["foo", context], undefined);
yield getGlobalOK(["bar", context], undefined);
yield getOK(["b.com", "foo", context], 5);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], undefined);
yield getOK(["b.com", "foo"], 5);
},
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"));
yield true;
},
function* removeByDomain_invalidateCache() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
getCachedOK(["a.com", "foo"], true, 1);
getCachedOK(["a.com", "bar"], true, 2);
cps.removeByDomain("a.com", null, makeCallback());
getCachedOK(["a.com", "foo"], false);
getCachedOK(["a.com", "bar"], false);
yield;
},
function* removeBySubdomain_invalidateCache() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
getCachedSubdomainsOK(["a.com", "foo"], [
["a.com", 1],
["b.a.com", 2],
]);
cps.removeBySubdomain("a.com", null, makeCallback());
getCachedSubdomainsOK(["a.com", "foo"], []);
yield;
},
function* removeAllGlobals_invalidateCache() {
yield setGlobal("foo", 1);
yield setGlobal("bar", 2);
getCachedGlobalOK(["foo"], true, 1);
getCachedGlobalOK(["bar"], true, 2);
cps.removeAllGlobals(null, makeCallback());
getCachedGlobalOK(["foo"], false);
getCachedGlobalOK(["bar"], false);
yield;
},
];

View file

@ -0,0 +1,96 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* nonexistent() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);
yield cps.removeByName("bogus", null, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getGlobalOK(["foo"], 2);
},
function* names() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
yield cps.removeByName("foo", null, makeCallback());
yield dbOK([
["a.com", "bar", 2],
[null, "bar", 4],
["b.com", "bar", 6],
]);
yield getOK(["a.com", "foo"], undefined);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], undefined);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], undefined);
yield getOK(["b.com", "bar"], 6);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
yield set("b.com", "bar", 6);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 7, context);
yield setGlobal("foo", 8, context);
yield set("b.com", "bar", 9, context);
yield cps.removeByName("bar", context, makeCallback());
yield dbOK([
["a.com", "foo", 1],
[null, "foo", 3],
["b.com", "foo", 5],
]);
yield getOK(["a.com", "foo", context], 7);
yield getOK(["a.com", "bar", context], undefined);
yield getGlobalOK(["foo", context], 8);
yield getGlobalOK(["bar", context], undefined);
yield getOK(["b.com", "foo", context], 5);
yield getOK(["b.com", "bar", context], undefined);
yield getOK(["a.com", "foo"], 1);
yield getOK(["a.com", "bar"], undefined);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], undefined);
yield getOK(["b.com", "foo"], 5);
yield getOK(["b.com", "bar"], undefined);
},
function* erroneous() {
do_check_throws(() => cps.removeByName("", null));
do_check_throws(() => cps.removeByName(null, null));
do_check_throws(() => cps.removeByName("foo", null, "bogus"));
yield true;
},
function* invalidateCache() {
yield set("a.com", "foo", 1);
yield 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);
yield;
},
];

View file

@ -0,0 +1,12 @@
/* 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 serv = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
do_check_eq(serv.QueryInterface(Ci.nsIContentPrefService2), serv);
do_check_eq(serv.QueryInterface(Ci.nsISupports), serv);
let val = serv.QueryInterface(Ci.nsIContentPrefService);
do_check_true(val instanceof Ci.nsIContentPrefService);
}

View file

@ -0,0 +1,206 @@
/* 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() {
runAsyncTests(tests);
}
var tests = [
function* get_nonexistent() {
yield getOK(["a.com", "foo"], undefined);
yield getGlobalOK(["foo"], undefined);
},
function* isomorphicDomains() {
yield set("a.com", "foo", 1);
yield dbOK([
["a.com", "foo", 1],
]);
yield getOK(["a.com", "foo"], 1);
yield getOK(["http://a.com/huh", "foo"], 1, "a.com");
yield set("http://a.com/huh", "foo", 2);
yield dbOK([
["a.com", "foo", 2],
]);
yield getOK(["a.com", "foo"], 2);
yield getOK(["http://a.com/yeah", "foo"], 2, "a.com");
},
function* names() {
yield set("a.com", "foo", 1);
yield dbOK([
["a.com", "foo", 1],
]);
yield getOK(["a.com", "foo"], 1);
yield set("a.com", "bar", 2);
yield dbOK([
["a.com", "foo", 1],
["a.com", "bar", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getOK(["a.com", "bar"], 2);
yield setGlobal("foo", 3);
yield dbOK([
["a.com", "foo", 1],
["a.com", "bar", 2],
[null, "foo", 3],
]);
yield getOK(["a.com", "foo"], 1);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], 3);
yield setGlobal("bar", 4);
yield dbOK([
["a.com", "foo", 1],
["a.com", "bar", 2],
[null, "foo", 3],
[null, "bar", 4],
]);
yield getOK(["a.com", "foo"], 1);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
},
function* subdomains() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "foo", 2);
yield dbOK([
["a.com", "foo", 1],
["b.a.com", "foo", 2],
]);
yield getOK(["a.com", "foo"], 1);
yield getOK(["b.a.com", "foo"], 2);
},
function* privateBrowsing() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
yield setGlobal("bar", 4);
yield set("b.com", "foo", 5);
let context = { usePrivateBrowsing: true };
yield set("a.com", "foo", 6, context);
yield setGlobal("foo", 7, context);
yield dbOK([
["a.com", "foo", 1],
["a.com", "bar", 2],
[null, "foo", 3],
[null, "bar", 4],
["b.com", "foo", 5],
]);
yield getOK(["a.com", "foo", context], 6, "a.com");
yield getOK(["a.com", "bar", context], 2);
yield getGlobalOK(["foo", context], 7);
yield getGlobalOK(["bar", context], 4);
yield getOK(["b.com", "foo", context], 5);
yield getOK(["a.com", "foo"], 1);
yield getOK(["a.com", "bar"], 2);
yield getGlobalOK(["foo"], 3);
yield getGlobalOK(["bar"], 4);
yield getOK(["b.com", "foo"], 5);
},
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"));
yield true;
},
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));
yield true;
},
function* set_invalidateCache() {
// (1) Set a pref and wait for it to finish.
yield 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: function () {
// (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.
var fetchedPref;
cps.getByDomainAndName("a.com", "foo", null, {
handleResult: function (pref) {
fetchedPref = pref;
},
handleCompletion: function () {
// (7) Finally, this callback should be called after set's above.
do_check_true(!!fetchedPref);
do_check_eq(fetchedPref.value, 2);
next();
},
});
yield;
},
function* get_nameOnly() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield set("b.com", "foo", 3);
yield setGlobal("foo", 4);
yield 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 = { usePrivateBrowsing: true };
yield set("b.com", "foo", 5, context);
yield getOKEx("getByName", ["foo", context], [
{"domain": "a.com", "name": "foo", "value": 1},
{"domain": null, "name": "foo", "value": 4},
{"domain": "b.com", "name": "foo", "value": 5}
]);
},
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;
yield set("a.com", "foo", 1);
let timestamp = yield getDate("a.com", "foo");
ok(start <= timestamp, "Timestamp is not too early (" + start + "<=" + timestamp + ").");
ok(timestamp <= end, "Timestamp is not too late (" + timestamp + "<=" + end + ").");
},
];

View file

@ -0,0 +1,19 @@
[DEFAULT]
head = head.js
tail =
skip-if = toolkit == 'android'
support-files = AsyncRunner.jsm
[test_service.js]
[test_setGet.js]
[test_getSubdomains.js]
[test_remove.js]
[test_removeByDomain.js]
[test_removeAllDomains.js]
[test_removeByName.js]
[test_getCached.js]
[test_getCachedSubdomains.js]
[test_observers.js]
[test_extractDomain.js]
[test_migrationToSchema4.js]
[test_removeAllDomainsSince.js]