Bug 699859 - Create nsIContentPrefService2, an async version of nsIContentPrefService. r=mak, sr=mossop

This commit is contained in:
Drew Willcoxon 2012-12-20 17:37:56 -08:00
parent d75825f8d9
commit 3c55f766f6
21 changed files with 2906 additions and 165 deletions

View File

@ -27,6 +27,7 @@ XPIDLSRCS = \
nsIBrowserDOMWindow.idl \
nsIContentPermissionPrompt.idl \
nsIContentPrefService.idl \
nsIContentPrefService2.idl \
nsIContentURIGrouper.idl \
nsIDOMClientInformation.idl \
nsIDOMConstructor.idl \

View File

@ -0,0 +1,365 @@
/* 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/. */
#include "nsISupports.idl"
interface nsIVariant;
interface nsIContentPrefObserver;
interface nsIContentPrefCallback2;
interface nsILoadContext;
interface nsIContentPref;
/**
* Content Preferences
*
* Content preferences allow the application to associate arbitrary data, or
* "preferences", with specific domains, or web "content". Specifically, a
* content preference is a structure with three values: a domain with which the
* preference is associated, a name that identifies the preference within its
* domain, and a value. (See nsIContentPref below.)
*
* For example, if you want to remember the user's preference for a certain zoom
* level on www.mozilla.org pages, you might store a preference whose domain is
* "www.mozilla.org", whose name is "zoomLevel", and whose value is the numeric
* zoom level.
*
* A preference need not have a domain, and in that case the preference is
* called a "global" preference. This interface doesn't impart any special
* significance to global preferences; they're simply name-value pairs that
* aren't associated with any particular domain. As a consumer of this
* interface, you might choose to let a global preference override all non-
* global preferences of the same name, for example, for whatever definition of
* "override" is appropriate for your use case.
*
*
* Domain Parameters
*
* Many methods of this interface accept a "domain" parameter. Domains may be
* specified either exactly, like "example.com", or as full URLs, like
* "http://example.com/foo/bar". In the latter case the API extracts the full
* domain from the URL, so if you specify "http://foo.bar.example.com/baz", the
* domain is taken to be "foo.bar.example.com", not "example.com".
*
*
* Private-Browsing Context Parameters
*
* Many methods also accept a "context" parameter. This parameter relates to
* private browsing and determines the kind of storage that a method uses,
* either the usual permanent storage or temporary storage set aside for private
* browsing sessions.
*
* Pass null to unconditionally use permanent storage. Pass an nsILoadContext
* to use storage appropriate to the context's usePrivateBrowsing attribute: if
* usePrivateBrowsing is true, temporary private-browsing storage is used, and
* otherwise permanent storage is used. A context can be obtained from the
* window or channel whose content pertains to the preferences being modified or
* retrieved.
*
*
* Callbacks
*
* The methods of callback objects are always called asynchronously. See
* nsIContentPrefCallback2 below for more information about callbacks.
*/
[scriptable, uuid(51e1d34a-5e9d-4b77-b14c-0f8346e264ca)]
interface nsIContentPrefService2 : nsISupports
{
/**
* Gets the preference with the given domain and name.
*
* @param domain The preference's domain.
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param callback handleResult is called once unless no such preference
* exists, in which case handleResult is not called at all.
*/
void getByDomainAndName(in AString domain,
in AString name,
in nsILoadContext context,
in nsIContentPrefCallback2 callback);
/**
* Gets all preferences with the given name whose domains are either the same
* as or subdomains of the given domain.
*
* @param domain The preferences' domain.
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param callback handleResult is called once for each preference. If no
* such preferences exist, handleResult is not called at all.
*/
void getBySubdomainAndName(in AString domain,
in AString name,
in nsILoadContext context,
in nsIContentPrefCallback2 callback);
/**
* Gets the preference with no domain and the given name.
*
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param callback handleResult is called once unless no such preference
* exists, in which case handleResult is not called at all.
*/
void getGlobal(in AString name,
in nsILoadContext context,
in nsIContentPrefCallback2 callback);
/**
* Synchronously retrieves from the in-memory cache the preference with the
* given domain and name.
*
* In addition to caching preference values, the cache also keeps track of
* preferences that are known not to exist. If the preference is known not to
* exist, the value attribute of the returned object will be undefined
* (nsIDataType::VTYPE_VOID).
*
* If the preference is neither cached nor known not to exist, then null is
* returned, and get() must be called to determine whether the preference
* exists.
*
* @param domain The preference's domain.
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @return The preference, or null if no such preference is known to
* exist.
*/
nsIContentPref getCachedByDomainAndName(in AString domain,
in AString name,
in nsILoadContext context);
/**
* Synchronously retrieves from the in-memory cache all preferences with the
* given name whose domains are either the same as or subdomains of the given
* domain.
*
* The preferences are returned in an array through the out-parameter. If a
* preference for a particular subdomain is known not to exist, then an object
* corresponding to that preference will be present in the array, and, as with
* getCachedByDomainAndName, its value attribute will be undefined.
*
* @param domain The preferences' domain.
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param len The length of the returned array.
* @param prefs The array of preferences.
*/
void getCachedBySubdomainAndName(in AString domain,
in AString name,
in nsILoadContext context,
out unsigned long len,
[retval,array,size_is(len)] out nsIContentPref prefs);
/**
* Synchronously retrieves from the in-memory cache the preference with no
* domain and the given name.
*
* As with getCachedByDomainAndName, if the preference is cached then it is
* returned; if the preference is known not to exist, then the value attribute
* of the returned object will be undefined; if the preference is neither
* cached nor known not to exist, then null is returned.
*
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @return The preference, or null if no such preference is known to
* exist.
*/
nsIContentPref getCachedGlobal(in AString name,
in nsILoadContext context);
/**
* Sets a preference.
*
* @param domain The preference's domain.
* @param name The preference's name.
* @param value The preference's value.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the preference has been
* stored.
*/
void set(in AString domain,
in AString name,
in nsIVariant value,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Sets a preference with no domain.
*
* @param name The preference's name.
* @param value The preference's value.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the preference has been
* stored.
*/
void setGlobal(in AString name,
in nsIVariant value,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes the preference with the given domain and name.
*
* @param domain The preference's domain.
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByDomainAndName(in AString domain,
in AString name,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all the preferences with the given name whose domains are either
* the same as or subdomains of the given domain.
*
* @param domain The preferences' domain.
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeBySubdomainAndName(in AString domain,
in AString name,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes the preference with no domain and the given name.
*
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeGlobal(in AString name,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all preferences with the given domain.
*
* @param domain The preferences' domain.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByDomain(in AString domain,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all preferences whose domains are either the same as or subdomains
* of the given domain.
*
* @param domain The preferences' domain.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeBySubdomain(in AString domain,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all preferences with the given name regardless of domain, including
* global preferences with the given name.
*
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByName(in AString name,
in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all non-global preferences -- in other words, all preferences that
* have a domain.
*
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllDomains(in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Removes all global preferences -- in other words, all preferences that have
* no domain.
*
* @param context The private-browsing context, if any.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllGlobals(in nsILoadContext context,
[optional] in nsIContentPrefCallback2 callback);
/**
* Registers an observer that will be notified whenever a preference with the
* given name is set or removed.
*
* When a set or remove method is called, observers are notified after the set
* or removal completes but before method's callback is called.
*
* The service holds a strong reference to the observer, so the observer must
* be removed later to avoid leaking it.
*
* @param name The name of the preferences to observe. Pass null to
* observe all preference changes regardless of name.
* @param observer The observer.
*/
void addObserverForName(in AString name,
in nsIContentPrefObserver observer);
/**
* Unregisters an observer for the given name.
*
* @param name The name for which the observer was registered. Pass null
* if the observer was added with a null name.
* @param observer The observer.
*/
void removeObserverForName(in AString name,
in nsIContentPrefObserver observer);
};
/**
* The callback used by the above methods.
*/
[scriptable, uuid(1a12cf41-79e8-4d0f-9899-2f7b27c5d9a1)]
interface nsIContentPrefCallback2 : nsISupports
{
/**
* For the retrieval methods, this is called once for each retrieved
* preference. It is not called for other methods.
*
* @param pref The retrieved preference.
*/
void handleResult(in nsIContentPref pref);
/**
* Called when an error occurs. This may be called multiple times before
* onComplete is called.
*
* @param error A number in Components.results describing the error.
*/
void handleError(in nsresult error);
/**
* Called when the method finishes. This will be called exactly once for
* each method invocation, and afterward no other callback methods will be
* called.
*
* @param reason One of the COMPLETE_* values indicating the manner in which
* the method completed.
*/
void handleCompletion(in unsigned short reason);
const unsigned short COMPLETE_OK = 0;
const unsigned short COMPLETE_ERROR = 1;
};
[scriptable, function, uuid(9f24948d-24b5-4b1b-b554-7dbd58c1d792)]
interface nsIContentPref : nsISupports
{
readonly attribute AString domain;
readonly attribute AString name;
readonly attribute nsIVariant value;
};

View File

@ -26,6 +26,7 @@
[include:embedding/tests/unit/xpcshell.ini]
[include:toolkit/components/commandlines/test/unit/xpcshell.ini]
[include:toolkit/components/contentprefs/tests/unit/xpcshell.ini]
[include:toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini]
[include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
[include:toolkit/devtools/sourcemap/tests/unit/xpcshell.ini]
[include:toolkit/components/passwordmgr/test/unit/xpcshell.ini]

View File

@ -0,0 +1,798 @@
/* 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
let 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/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ContentPrefStore.jsm");
function ContentPrefService2(cps) {
this._cps = cps;
this._cache = cps._cache;
this._pbStore = cps._privModeStorage;
}
ContentPrefService2.prototype = {
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] in
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] in 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 ($)"
) :
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) {
let stmt = this._stmt(joinArgs(Array.slice(arguments, 2)).replace("$",
"SELECT id " +
"FROM groups " +
"WHERE name = :group OR " +
"(:includeSubdomains AND name LIKE :pattern ESCAPE '/')"
));
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] in store.match(group, name, includeSubdomains)) {
outStore.set(sgroup, name, val);
}
});
let prefs = [];
for (let [sgroup, sname, val] in 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 () {
this._cps._notifyPrefSet(group, name, value);
cbHandleCompletion(callback, Ci.nsIContentPrefCallback2.COMPLETE_OK);
});
return;
}
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)",
"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",
")"
);
stmt.params.group = group;
}
else {
stmt = this._stmt(
"INSERT OR REPLACE INTO prefs (id, groupID, settingID, value)",
"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",
")"
);
}
stmt.params.name = name;
stmt.params.value = value;
stmts.push(stmt);
this._execStmts(stmts, {
onDone: function onDone(reason, ok) {
if (ok) {
this._cache.setWithCast(group, name, value);
this._cps._notifyPrefSet(group, name, value);
}
cbHandleCompletion(callback, reason);
},
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);
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 ($)",
"END"
);
stmt.params.name = name;
stmts.push(stmt);
// Delete settings and groups that are no longer used. 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.
stmts.push(this._stmt(
"DELETE FROM settings",
"WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)"
));
stmts.push(this._stmt(
"DELETE FROM groups WHERE id NOT IN (",
"SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL",
")"
));
let prefs = new ContentPrefStore();
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
prefs.set(grp, name);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok) {
this._cache.set(group, name, undefined);
if (context && context.usePrivateBrowsing) {
for (let [sgroup, ] in
this._pbStore.match(group, name, includeSubdomains)) {
prefs.set(sgroup, name);
this._pbStore.remove(sgroup, name);
}
}
for (let [sgroup, , ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, name);
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
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);
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 ($)"
));
stmts.push(this._stmtWithGroupClause(group, includeSubdomains,
"DELETE FROM groups WHERE id IN ($)"
));
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();
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok) {
if (context && context.usePrivateBrowsing) {
for (let [sgroup, sname, ] in this._pbStore) {
prefs.set(sgroup, sname);
this._pbStore.remove(sgroup, sname);
}
}
for (let [sgroup, sname, ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, sname);
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
removeAllDomains: function CPS2_removeAllDomains(context, callback) {
checkCallbackArg(callback, false);
let stmts = [];
// First get the matching prefs.
stmts.push(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"
));
stmts.push(this._stmt(
"DELETE FROM prefs WHERE groupID NOTNULL"
));
stmts.push(this._stmt(
"DELETE FROM groups"
));
stmts.push(this._stmt(
"DELETE FROM settings",
"WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)"
));
let prefs = new ContentPrefStore();
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok) {
if (context && context.usePrivateBrowsing) {
for (let [sgroup, sname, ] in this._pbStore) {
prefs.set(sgroup, sname);
}
this._pbStore.removeGrouped();
}
for (let [sgroup, sname, ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, sname);
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
removeByName: function CPS2_removeByName(name, context, callback) {
checkNameArg(name);
checkCallbackArg(callback, false);
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();
this._execStmts(stmts, {
onRow: function onRow(row) {
let grp = row.getResultByName("grp");
prefs.set(grp, name);
this._cache.set(grp, name, undefined);
},
onDone: function onDone(reason, ok) {
if (ok) {
if (context && context.usePrivateBrowsing) {
for (let [sgroup, sname, ] in this._pbStore) {
if (sname === name) {
prefs.set(sgroup, name);
this._pbStore.remove(sgroup, name);
}
}
}
for (let [sgroup, , ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, name);
}
}
cbHandleCompletion(callback, reason);
},
onError: function onError(nsresult) {
cbHandleError(callback, nsresult);
}
});
},
destroy: function CPS2_destroy() {
for each (let stmt in this._statements) {
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. If more than one string is given, then
* all are concatenated. The concatenation process inserts
* spaces where appropriate and removes unnecessary contiguous
* spaces. Call like _stmt("SELECT *", "FROM foo").
* @return The cached, possibly new, statement.
*/
_stmt: function CPS2__stmt(sql /*, sql2, sql3, ... */) {
let sql = joinArgs(arguments);
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);
},
/**
* 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(function (t) this._stmt("DELETE FROM", t), this);
this._execStmts(stmts, { onDone: function () callback() });
},
QueryInterface: function CPS2_QueryInterface(iid) {
let supportedIIDs = [
Ci.nsIContentPrefService,
Ci.nsIObserver,
Ci.nsISupports,
];
if (supportedIIDs.some(function (i) iid.equals(i)))
return this;
if (iid.equals(Ci.nsIContentPrefService))
return this._cps;
throw Cr.NS_ERROR_NO_INTERFACE;
},
};
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);
}
}
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);
}
function joinArgs(args) {
return Array.join(args, " ").trim().replace(/\s{2,}/g, " ");
}

View File

@ -0,0 +1,115 @@
/* 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 EXPORTED_SYMBOLS = [
"ContentPrefStore",
];
function ContentPrefStore() {
this._groups = {};
this._globalNames = {};
}
ContentPrefStore.prototype = {
set: function CPS_set(group, name, val) {
if (group) {
if (!this._groups.hasOwnProperty(group))
this._groups[group] = {};
this._groups[group][name] = val;
}
else {
this._globalNames[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) {
return (group &&
this._groups.hasOwnProperty(group) &&
this._groups[group].hasOwnProperty(name)) ||
(!group &&
this._globalNames.hasOwnProperty(name));
},
get: function CPS_get(group, name) {
if (group) {
if (this._groups.hasOwnProperty(group) &&
this._groups[group].hasOwnProperty(name))
return this._groups[group][name];
}
else if (this._globalNames.hasOwnProperty(name)) {
return this._globalNames[name];
}
return undefined;
},
remove: function CPS_remove(group, name) {
if (group) {
if (this._groups.hasOwnProperty(group)) {
delete this._groups[group][name];
if (!Object.keys(this._groups[group]).length)
delete this._groups[group];
}
}
else {
delete this._globalNames[name];
}
},
removeGrouped: function CPS_removeGrouped() {
this._groups = {};
},
removeAll: function CPS_removeAll() {
this.removeGrouped();
this._globalNames = {};
},
__iterator__: function CPS___iterator__() {
for (let [group, names] in Iterator(this._groups)) {
for (let [name, val] in Iterator(names)) {
yield [group, name, val];
}
}
for (let [name, val] in Iterator(this._globalNames)) {
yield [null, name, val];
}
},
match: function CPS_match(group, name, includeSubdomains) {
for (let sgroup in this.matchGroups(group, includeSubdomains)) {
if (this.has(sgroup, name))
yield [sgroup, this.get(sgroup, name)];
}
},
matchGroups: function CPS_matchGroups(group, includeSubdomains) {
if (group) {
if (includeSubdomains) {
for (let [sgroup, , ] in this) {
if (sgroup) {
let idx = sgroup.indexOf(group);
if (idx == sgroup.length - group.length &&
(idx == 0 || sgroup[idx - 1] == "."))
yield sgroup;
}
}
}
else if (this._groups.hasOwnProperty(group)) {
yield group;
}
}
else if (Object.keys(this._globalNames).length) {
yield null;
}
},
};

View File

@ -11,9 +11,16 @@ include $(DEPTH)/config/autoconf.mk
MODULE = contentprefs
EXTRA_COMPONENTS = nsContentPrefService.js nsContentPrefService.manifest
EXTRA_COMPONENTS = \
nsContentPrefService.js \
nsContentPrefService.manifest \
$(NULL)
EXTRA_JS_MODULES = ContentPrefInstance.jsm
EXTRA_JS_MODULES = \
ContentPrefInstance.jsm \
ContentPrefService2.jsm \
ContentPrefStore.jsm \
$(NULL)
TEST_DIRS += tests

View File

@ -106,61 +106,48 @@ function ContentPrefService() {
this._observerSvc.addObserver(this, "xpcom-shutdown", false);
}
var inMemoryPrefsProto = {
getPref: function(aName, aGroup) {
aGroup = aGroup || "__GlobalPrefs__";
if (this._prefCache[aGroup] && this._prefCache[aGroup].has(aName)) {
let value = this._prefCache[aGroup].get(aName);
return [true, value];
}
return [false, undefined];
},
setPref: function(aName, aValue, aGroup) {
if (typeof aValue == "boolean")
aValue = aValue ? 1 : 0;
else if (aValue === undefined)
aValue = null;
this.cachePref(aName, aValue, aGroup);
},
removePref: function(aName, aGroup) {
aGroup = aGroup || "__GlobalPrefs__";
if (this._prefCache[aGroup].has(aName)) {
this._prefCache[aGroup].delete(aName);
if (this._prefCache[aGroup].size == 0) {
// remove empty group
delete this._prefCache[aGroup];
}
}
},
invalidate: function(aKeepGlobal) {
if (!aKeepGlobal) {
this._prefCache = {};
return;
}
if (this._prefCache.hasOwnProperty("__GlobalPrefs__")) {
let globals = this._prefCache["__GlobalPrefs__"];
this._prefCache = {"__GlobalPrefs__": globals};
} else {
this._prefCache = {};
Cu.import("resource://gre/modules/ContentPrefStore.jsm");
const cache = new ContentPrefStore();
cache.set = function CPS_cache_set(group, name, val) {
Object.getPrototypeOf(this).set.apply(this, arguments);
let groupCount = Object.keys(this._groups).length;
if (groupCount >= CACHE_MAX_GROUP_ENTRIES) {
// Clean half of the entries
for (let [group, name, ] in this) {
this.remove(group, name);
groupCount--;
if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2)
break;
}
}
};
const privModeStorage = new ContentPrefStore();
ContentPrefService.prototype = {
//**************************************************************************//
// XPCOM Plumbing
classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPrefService,
Ci.nsIMessageListener]),
classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"),
QueryInterface: function CPS_QueryInterface(iid) {
let supportedIIDs = [
Ci.nsIContentPrefService,
Ci.nsIFrameMessageListener,
Ci.nsISupports,
];
if (supportedIIDs.some(function (i) iid.equals(i)))
return this;
if (iid.equals(Ci.nsIContentPrefService2)) {
if (!this._contentPrefService2) {
let s = {};
Cu.import("resource://gre/modules/ContentPrefService2.jsm", s);
this._contentPrefService2 = new s.ContentPrefService2(this);
}
return this._contentPrefService2;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
//**************************************************************************//
// Convenience Getters
@ -263,6 +250,9 @@ ContentPrefService.prototype = {
this.__stmtUpdatePref = null;
}
if (this._contentPrefService2)
this._contentPrefService2.destroy();
this._dbConnection.asyncClose();
// Delete references to XPCOM components to make sure we don't leak them
@ -286,94 +276,17 @@ ContentPrefService.prototype = {
this._destroy();
break;
case "last-pb-context-exited":
this._privModeStorage.invalidate();
this._privModeStorage.removeAll();
break;
}
},
//**************************************************************************//
// Prefs cache
_cache: Object.create(inMemoryPrefsProto, {
_prefCache: {
value: {}, configurable: true, writable: true, enumerable: true
},
// in-memory cache and private-browsing stores
cachePref: { value:
function(aName, aValue, aGroup) {
aGroup = aGroup || "__GlobalPrefs__";
if (!this._prefCache[aGroup]) {
this._possiblyCleanCache();
this._prefCache[aGroup] = new Map();
}
this._prefCache[aGroup].set(aName, aValue);
}
},
_possiblyCleanCache: { value:
function() {
let groupCount = Object.keys(this._prefCache).length;
if (groupCount >= CACHE_MAX_GROUP_ENTRIES) {
// Clean half of the entries
for (let entry in this._prefCache) {
delete this._prefCache[entry];
groupCount--;
if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2)
break;
}
}
}
}
}),
//**************************************************************************//
// Private mode storage
_privModeStorage: Object.create(inMemoryPrefsProto, {
_prefCache: {
value: {}, configurable: true, writable: true, enumerable: true
},
cachePref: { value:
function(aName, aValue, aGroup) {
aGroup = aGroup || "__GlobalPrefs__";
if (!this._prefCache[aGroup]) {
this._prefCache[aGroup] = new Map();
}
this._prefCache[aGroup].set(aName, aValue);
}
},
getPrefs: { value:
function(aGroup) {
aGroup = aGroup || "__GlobalPrefs__";
if (this._prefCache[aGroup]) {
return [true, this._prefCache[aGroup]];
}
return [false, undefined];
}
},
groupsForName: { value:
function(aName) {
var res = [];
for (let entry in this._prefCache) {
if (this._prefCache[entry]) {
if (entry === "__GlobalPrefs__") {
entry = null;
}
res.push(entry);
}
}
return res;
}
}
}),
_cache: cache,
_privModeStorage: privModeStorage,
//**************************************************************************//
// nsIContentPrefService
@ -386,8 +299,8 @@ ContentPrefService.prototype = {
var group = this._parseGroupParam(aGroup);
if (aContext && aContext.usePrivateBrowsing) {
let [haspref, value] = this._privModeStorage.getPref(aName, group);
if (haspref) {
if (this._privModeStorage.has(group, aName)) {
let value = this._privModeStorage.get(group, aName);
if (aCallback) {
this._scheduleCallback(function(){aCallback.onResult(value);});
return;
@ -414,7 +327,7 @@ ContentPrefService.prototype = {
var group = this._parseGroupParam(aGroup);
if (aContext && aContext.usePrivateBrowsing) {
this._privModeStorage.setPref(aName, aValue, group);
this._privModeStorage.setWithCast(group, aName, aValue);
this._notifyPrefSet(group, aName, aValue);
return;
}
@ -436,7 +349,7 @@ ContentPrefService.prototype = {
else
this._insertPref(groupID, settingID, aValue);
this._cache.setPref(aName, aValue, group);
this._cache.setWithCast(group, aName, aValue);
this._notifyPrefSet(group, aName, aValue);
},
@ -454,8 +367,7 @@ ContentPrefService.prototype = {
let group = this._parseGroupParam(aGroup);
let storage = aContext && aContext.usePrivateBrowsing ? this._privModeStorage: this._cache;
let [cached,] = storage.getPref(aName, group);
return cached;
return storage.has(group, aName);
},
removePref: function ContentPrefService_removePref(aGroup, aName, aContext) {
@ -466,7 +378,7 @@ ContentPrefService.prototype = {
var group = this._parseGroupParam(aGroup);
if (aContext && aContext.usePrivateBrowsing) {
this._privModeStorage.removePref(aName, group);
this._privModeStorage.remove(group, aName);
this._notifyPrefRemoved(group, aName);
return;
}
@ -489,7 +401,7 @@ ContentPrefService.prototype = {
if (groupID)
this._deleteGroupIfUnused(groupID);
this._cache.removePref(aName, group);
this._cache.remove(group, aName);
this._notifyPrefRemoved(group, aName);
},
@ -497,9 +409,9 @@ ContentPrefService.prototype = {
// will not delete global preferences
if (aContext && aContext.usePrivateBrowsing) {
// keep only global prefs
this._privModeStorage.invalidate(true);
this._privModeStorage.removeGrouped();
}
this._cache.invalidate(true);
this._cache.removeGrouped();
this._dbConnection.beginTransaction();
try {
this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL");
@ -522,11 +434,11 @@ ContentPrefService.prototype = {
Cr.NS_ERROR_ILLEGAL_VALUE);
if (aContext && aContext.usePrivateBrowsing) {
let groupNames = this._privModeStorage.groupsForName(aName);
for (var i = 0; i < groupNames.length; i++) {
let groupName = groupNames[i];
this._privModeStorage.removePref(aName, groupName);
this._notifyPrefRemoved(groupName, aName);
for (let [group, name, ] in this._privModeStorage) {
if (name === aName) {
this._privModeStorage.remove(group, aName);
this._notifyPrefRemoved(group, aName);
}
}
}
@ -563,7 +475,7 @@ ContentPrefService.prototype = {
this._dbConnection.executeSimpleSQL("DELETE FROM settings WHERE id = " + settingID);
for (var i = 0; i < groupNames.length; i++) {
this._cache.removePref(aName, groupNames[i]);
this._cache.remove(groupNames[i], aName);
if (groupNames[i]) // ie. not null, which will be last (and i == groupIDs.length)
this._deleteGroupIfUnused(groupIDs[i]);
if (!aContext || !aContext.usePrivateBrowsing) {
@ -577,9 +489,9 @@ ContentPrefService.prototype = {
if (aContext && aContext.usePrivateBrowsing) {
let prefs = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
let [hasbranch,properties] = this._privModeStorage.getPrefs(group);
for (let [entry, value] of properties) {
prefs.setProperty(entry, value);
for (let [sgroup, sname, sval] in this._privModeStorage) {
if (sgroup === group)
prefs.setProperty(sname, sval);
}
return prefs;
}
@ -597,11 +509,9 @@ ContentPrefService.prototype = {
if (aContext && aContext.usePrivateBrowsing) {
let prefs = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
let groupNames = this._privModeStorage.groupsForName(aName);
for (var i = 0; i < groupNames.length; i++) {
let groupName = groupNames[i];
prefs.setProperty(groupName,
this._privModeStorage.getPref(aName, groupName)[1]);
for (let [sgroup, sname, sval] in this._privModeStorage) {
if (sname === aName)
prefs.setProperty(sgroup, sval);
}
return prefs;
}
@ -719,8 +629,9 @@ ContentPrefService.prototype = {
},
_selectPref: function ContentPrefService__selectPref(aGroup, aSetting, aCallback) {
let [cached, value] = this._cache.getPref(aSetting, aGroup);
if (cached) {
let value = undefined;
if (this._cache.has(aGroup, aSetting)) {
value = this._cache.get(aGroup, aSetting);
if (aCallback) {
this._scheduleCallback(function(){aCallback.onResult(value);});
return;
@ -735,7 +646,7 @@ ContentPrefService.prototype = {
if (aCallback) {
let cache = this._cache;
new AsyncStatement(this._stmtSelectPref).execute({onResult: function(aResult) {
cache.cachePref(aSetting, aResult, aGroup);
cache.set(aGroup, aSetting, aResult);
aCallback.onResult(aResult);
}});
}
@ -743,7 +654,7 @@ ContentPrefService.prototype = {
if (this._stmtSelectPref.executeStep()) {
value = this._stmtSelectPref.row["value"];
}
this._cache.cachePref(aSetting, value, aGroup);
this._cache.set(aGroup, aSetting, value);
}
}
finally {
@ -768,8 +679,9 @@ ContentPrefService.prototype = {
},
_selectGlobalPref: function ContentPrefService__selectGlobalPref(aName, aCallback) {
let [cached, value] = this._cache.getPref(aName, null);
if (cached) {
let value = undefined;
if (this._cache.has(null, aName)) {
value = this._cache.get(null, aName);
if (aCallback) {
this._scheduleCallback(function(){aCallback.onResult(value);});
return;
@ -783,7 +695,7 @@ ContentPrefService.prototype = {
if (aCallback) {
let cache = this._cache;
new AsyncStatement(this._stmtSelectGlobalPref).execute({onResult: function(aResult) {
cache.cachePref(aName, aResult);
cache.set(null, aName, aResult);
aCallback.onResult(aResult);
}});
}
@ -791,7 +703,7 @@ ContentPrefService.prototype = {
if (this._stmtSelectGlobalPref.executeStep()) {
value = this._stmtSelectGlobalPref.row["value"];
}
this._cache.cachePref(aName, value);
this._cache.set(null, aName, value);
}
}
finally {

View File

@ -13,14 +13,11 @@ include $(DEPTH)/config/autoconf.mk
MODULE = test_toolkit_contentprefs
ifdef MOZ_PHOENIX
XPCSHELL_TESTS = unit
XPCSHELL_TESTS = unit unit_cps2
# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
ifneq ($(OS_ARCH),Darwin)
XPCSHELL_TESTS += unit_ipc
endif
endif
include $(topsrcdir)/config/rules.mk

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/. */
let 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(/* ... */) {
if (!this._iteratorQueue.length) {
this.destroy();
this._callbacks.done();
return;
}
// send() discards all arguments after the first, so there's no choice here
// but to send only one argument to the yielder.
let args = [arguments.length <= 1 ? arguments[0] : Array.slice(arguments)];
try {
var val = this._iteratorQueue[0].send.apply(this._iteratorQueue[0], args);
}
catch (err if err instanceof StopIteration) {
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 (val) {
if (typeof(val) != "boolean")
this._iteratorQueue.unshift(val);
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)
this._callbacks.consoleError(msg);
},
};

View File

@ -0,0 +1,311 @@
/* 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/. */
const { 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) {
do_test_pending();
cps = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
// Without this the private-browsing service tries to open a dialog when you
// change its enabled state.
Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session",
true);
let s = {};
Cu.import("resource://test/AsyncRunner.jsm", s);
asyncRunner = new s.AsyncRunner({
done: do_test_finished,
error: function (err) {
// xpcshell test functions like do_check_eq 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) {
// As much as possible make sure the error is related to the test. On the
// other hand if this fails to catch a test-related error, we'll hang.
let filename = scriptErr.sourceName || scriptErr.toString() || "";
if (/contentpref/i.test(filename))
do_throw(scriptErr);
}
});
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.
reset();
}
function makeCallback(callbacks) {
callbacks = callbacks || {};
["handleResult", "handleError"].forEach(function (meth) {
if (!callbacks[meth])
callbacks[meth] = function () {
do_throw(meth + " shouldn't be called.");
};
});
if (!callbacks.handleCompletion)
callbacks.handleCompletion = function (reason) {
do_check_eq(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK);
next();
};
return callbacks;
}
function do_check_throws(fn) {
let threw = false;
try {
fn();
}
catch (err) {
threw = true;
}
do_check_true(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 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) {
do_check_true(actual instanceof Ci.nsIContentPref);
do_check_eq(actual.domain, expected.domain);
do_check_eq(actual.name, expected.name);
if (strict)
do_check_true(actual.value === expected.value);
else
do_check_eq(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: function (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);
do_check_eq(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
do_check_true(actualPref === null);
}
function arraysOfArraysOK(actual, expected, cmp) {
cmp = cmp || function (a, b) do_check_eq(a, b);
do_check_eq(actual.length, expected.length);
actual.forEach(function (actualChildArr, i) {
let expectedChildArr = expected[i];
do_check_eq(actualChildArr.length, expectedChildArr.length);
actualChildArr.forEach(function (actualElt, j) {
let expectedElt = expectedChildArr[j];
cmp(actualElt, expectedElt);
});
});
}
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(function (c) row.getResultByName(c)));
}
},
handleError: function (err) {
do_throw(err);
}
});
stmt.finalize();
}
function on(event, names) {
let args = {
reset: function () {
for (let prop in this) {
if (Array.isArray(this[prop]))
this[prop].splice(0, this[prop].length);
}
},
destroy: function () {
names.forEach(function (n) cps.removeObserverForName(n, observers[n]));
},
};
let observers = {};
names.forEach(function (name) {
let obs = {};
["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) {
obs[meth] = function () 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);
});
return args;
}
function observerArgsOK(actualArgs, expectedArgs) {
do_check_neq(actualArgs, undefined);
arraysOfArraysOK(actualArgs, expectedArgs);
}

View File

@ -0,0 +1,99 @@
/* 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);
}
let 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(function ()
cps.getCachedByDomainAndName(null, "foo", null));
do_check_throws(function ()
cps.getCachedByDomainAndName("", "foo", null));
do_check_throws(function ()
cps.getCachedByDomainAndName("a.com", "", null));
do_check_throws(function ()
cps.getCachedByDomainAndName("a.com", null, null));
do_check_throws(function () cps.getCachedGlobal("", null));
do_check_throws(function () 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,190 @@
/* 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);
}
let 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(function ()
cps.getCachedBySubdomainAndName(null, "foo", null));
do_check_throws(function ()
cps.getCachedBySubdomainAndName("", "foo", null));
do_check_throws(function ()
cps.getCachedBySubdomainAndName("a.com", "", null));
do_check_throws(function ()
cps.getCachedBySubdomainAndName("a.com", null, null));
yield true;
},
];

View File

@ -0,0 +1,73 @@
/* 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);
}
let 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(function ()
cps.getBySubdomainAndName(null, "foo", null, {}));
do_check_throws(function ()
cps.getBySubdomainAndName("", "foo", null, {}));
do_check_throws(function ()
cps.getBySubdomainAndName("a.com", "", null, {}));
do_check_throws(function ()
cps.getBySubdomainAndName("a.com", null, null, {}));
do_check_throws(function ()
cps.getBySubdomainAndName("a.com", "foo", null, null));
yield true;
},
];

View File

@ -0,0 +1,125 @@
/* 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);
}
let tests = [
function observerForName_set() {
let args = on("Set", ["foo", null, "bar"]);
yield set("a.com", "foo", 1);
observerArgsOK(args.foo, [["a.com", "foo", 1]]);
observerArgsOK(args.null, [["a.com", "foo", 1]]);
observerArgsOK(args.bar, []);
args.reset();
yield setGlobal("foo", 2);
observerArgsOK(args.foo, [[null, "foo", 2]]);
observerArgsOK(args.null, [[null, "foo", 2]]);
observerArgsOK(args.bar, []);
args.reset();
},
function observerForName_remove() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);
let args = on("Removed", ["foo", null, "bar"]);
yield cps.removeByDomainAndName("a.com", "bogus", null, makeCallback());
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
args.reset();
yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback());
observerArgsOK(args.foo, [["a.com", "foo"]]);
observerArgsOK(args.null, [["a.com", "foo"]]);
observerArgsOK(args.bar, []);
args.reset();
yield cps.removeGlobal("foo", null, makeCallback());
observerArgsOK(args.foo, [[null, "foo"]]);
observerArgsOK(args.null, [[null, "foo"]]);
observerArgsOK(args.bar, []);
args.reset();
},
function observerForName_removeByDomain() {
yield set("a.com", "foo", 1);
yield set("b.a.com", "bar", 2);
yield setGlobal("foo", 3);
let args = on("Removed", ["foo", null, "bar"]);
yield cps.removeByDomain("bogus", null, makeCallback());
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
args.reset();
yield cps.removeBySubdomain("a.com", null, makeCallback());
observerArgsOK(args.foo, [["a.com", "foo"]]);
observerArgsOK(args.null, [["a.com", "foo"], ["b.a.com", "bar"]]);
observerArgsOK(args.bar, [["b.a.com", "bar"]]);
args.reset();
yield cps.removeAllGlobals(null, makeCallback());
observerArgsOK(args.foo, [[null, "foo"]]);
observerArgsOK(args.null, [[null, "foo"]]);
observerArgsOK(args.bar, []);
args.reset();
},
function observerForName_removeAllDomains() {
yield set("a.com", "foo", 1);
yield setGlobal("foo", 2);
yield set("b.com", "bar", 3);
let args = on("Removed", ["foo", null, "bar"]);
yield cps.removeAllDomains(null, makeCallback());
observerArgsOK(args.foo, [["a.com", "foo"]]);
observerArgsOK(args.null, [["a.com", "foo"], ["b.com", "bar"]]);
observerArgsOK(args.bar, [["b.com", "bar"]]);
args.reset();
},
function observerForName_removeByName() {
yield set("a.com", "foo", 1);
yield set("a.com", "bar", 2);
yield setGlobal("foo", 3);
let args = on("Removed", ["foo", null, "bar"]);
yield cps.removeByName("bogus", null, makeCallback());
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
args.reset();
yield cps.removeByName("foo", null, makeCallback());
observerArgsOK(args.foo, [["a.com", "foo"], [null, "foo"]]);
observerArgsOK(args.null, [["a.com", "foo"], [null, "foo"]]);
observerArgsOK(args.bar, []);
args.reset();
},
function removeObserverForName() {
let args = on("Set", ["foo", null, "bar"]);
cps.removeObserverForName("foo", args.foo.observer);
yield set("a.com", "foo", 1);
observerArgsOK(args.foo, []);
observerArgsOK(args.null, [["a.com", "foo", 1]]);
observerArgsOK(args.bar, []);
args.reset();
cps.removeObserverForName(null, args.null.observer);
yield set("a.com", "foo", 2);
observerArgsOK(args.foo, []);
observerArgsOK(args.null, []);
observerArgsOK(args.bar, []);
args.reset();
},
];

View File

@ -0,0 +1,194 @@
/* 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);
}
let 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(function () cps.removeByDomainAndName(null, "foo", null));
do_check_throws(function () cps.removeByDomainAndName("", "foo", null));
do_check_throws(function () cps.removeByDomainAndName("a.com", "foo", null,
"bogus"));
do_check_throws(function () cps.removeBySubdomainAndName(null, "foo",
null));
do_check_throws(function () cps.removeBySubdomainAndName("", "foo", null));
do_check_throws(function () cps.removeBySubdomainAndName("a.com", "foo",
null, "bogus"));
do_check_throws(function () cps.removeGlobal("", null));
do_check_throws(function () cps.removeGlobal(null, null));
do_check_throws(function () cps.removeGlobal("foo", null, "bogus"));
yield true;
},
];

View File

@ -0,0 +1,73 @@
/* 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);
}
let 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(function () cps.removeAllDomains(null, "bogus"));
yield true;
},
];

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/. */
function run_test() {
runAsyncTests(tests);
}
let 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 setGlobal("foo", 7, context);
yield cps.removeByDomain("a.com", context, makeCallback());
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(function () cps.removeByDomain(null, null));
do_check_throws(function () cps.removeByDomain("", null));
do_check_throws(function () cps.removeByDomain("a.com", null, "bogus"));
do_check_throws(function () cps.removeBySubdomain(null, null));
do_check_throws(function () cps.removeBySubdomain("", null));
do_check_throws(function () cps.removeBySubdomain("a.com", null, "bogus"));
do_check_throws(function () cps.removeAllGlobals(null, "bogus"));
yield true;
},
];

View File

@ -0,0 +1,85 @@
/* 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);
}
let 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(function () cps.removeByName("", null));
do_check_throws(function () cps.removeByName(null, null));
do_check_throws(function () cps.removeByName("foo", null, "bogus"));
yield true;
},
];

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,138 @@
/* 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);
}
let 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(function () cps.set(null, "foo", 1, null));
do_check_throws(function () cps.set("", "foo", 1, null));
do_check_throws(function () cps.set("a.com", "", 1, null));
do_check_throws(function () cps.set("a.com", null, 1, null));
do_check_throws(function () cps.set("a.com", "foo", undefined, null));
do_check_throws(function () cps.set("a.com", "foo", 1, null, "bogus"));
do_check_throws(function () cps.setGlobal("", 1, null));
do_check_throws(function () cps.setGlobal(null, 1, null));
do_check_throws(function () cps.setGlobal("foo", undefined, null));
do_check_throws(function () cps.setGlobal("foo", 1, null, "bogus"));
yield true;
},
function get_erroneous() {
do_check_throws(function () cps.getByDomainAndName(null, "foo", null, {}));
do_check_throws(function () cps.getByDomainAndName("", "foo", null, {}));
do_check_throws(function () cps.getByDomainAndName("a.com", "", null, {}));
do_check_throws(function ()
cps.getByDomainAndName("a.com", null, null, {}));
do_check_throws(function ()
cps.getByDomainAndName("a.com", "foo", null, null));
do_check_throws(function () cps.getGlobal("", null, {}));
do_check_throws(function () cps.getGlobal(null, null, {}));
do_check_throws(function () cps.getGlobal("foo", null, null));
yield true;
},
];

View File

@ -0,0 +1,14 @@
[DEFAULT]
head = head.js
tail =
[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]