bug 378547: site-specific preference service

r=mconnor
This commit is contained in:
2007-06-19 11:51:09 -07:00
parent 30fb0a1b6f
commit 9e7a4d5027
11 changed files with 1805 additions and 1 deletions

View File

@@ -48,7 +48,10 @@ include $(DEPTH)/config/autoconf.mk
ifdef MOZ_ENABLE_XREMOTE
DIRS += remote
endif
DIRS += urlformatter
DIRS += \
urlformatter \
contentprefs \
$(NULL)
# These component dirs are built only for XUL apps

View File

@@ -0,0 +1,50 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Content Prefs (cpref).
#
# The Initial Developer of the Original Code is Mozilla.
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Myk Melez <myk@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef ENABLE_TESTS
DIRS += tests
endif
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,50 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Content Prefs (cpref).
#
# The Initial Developer of the Original Code is Mozilla.
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Myk Melez <myk@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = contentprefs
XPIDL_MODULE = contentprefs
XPIDLSRCS = nsIContentPrefService.idl \
nsIContentURIGrouper.idl
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,142 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIVariant.idl"
#include "nsIURI.idl"
#include "nsIPropertyBag2.idl"
#include "nsIContentURIGrouper.idl"
[scriptable, uuid(746c7a02-f6c1-4869-b434-7c8b86e60e61)]
interface nsIContentPrefObserver : nsISupports
{
/**
* Called when a content pref is set to a different value.
*
* @param aGroup the group to which the pref belongs, or null
* if it's a global pref (applies to all URIs)
* @param aName the name of the pref that was set
* @param aValue the new value of the pref
*/
void onContentPrefSet(in AString aGroup, in AString aName, in nsIVariant aValue);
/**
* Called when a content pref is removed.
*
* @param aGroup the group to which the pref belongs, or null
* if it's a global pref (applies to all URIs)
* @param aName the name of the pref that was removed
*/
void onContentPrefRemoved(in AString aGroup, in AString aName);
};
[scriptable, uuid(5b1f6044-72f8-4920-9043-0c52d0ae4f4c)]
interface nsIContentPrefService : nsISupports
{
/**
* Get a pref.
*
* Besides the regular string, integer, boolean, etc. values, this method
* may return null (nsIDataType::VTYPE_EMPTY), which means the pref is set
* to NULL in the database, as well as undefined (nsIDataType::VTYPE_VOID),
* which means there is no record for this pref in the database.
*
* @param aURI the URI for which to get the pref, or null to get
* the global pref (applies to all URIs)
* @param aName the name of the pref to get
*
* @returns the value of the pref
*/
nsIVariant getPref(in nsIURI aURI, in AString aName);
/**
* Set a pref.
*
* @param aURI the URI for which to set the pref, or null to set
* the global pref (applies to all URIs)
* @param aName the name of the pref to set
* @param aValue the new value of the pref
*/
void setPref(in nsIURI aURI, in AString aName, in nsIVariant aValue);
/**
* Check whether or not a pref exists.
*
* @param aURI the URI for which to check for the pref
* @param aName the name of the pref to check for
*/
boolean hasPref(in nsIURI aURI, in AString aName);
/**
* Remove a pref.
*
* @param aURI the URI for which to remove the pref
* @param aName the name of the pref to remove
*/
void removePref(in nsIURI aURI, in AString aName);
/**
* Get the prefs that apply to the given URI.
*
* @param aURI the URI for which to retrieve prefs
*
* @returns a property bag of prefs
*/
nsIPropertyBag2 getPrefs(in nsIURI aURI);
/**
* Add an observer.
*
* @param aName the setting to observe, or null to add
* a generic observer that observes all settings
* @param aObserver the observer to add
*/
void addObserver(in AString aName, in nsIContentPrefObserver aObserver);
/**
* Remove an observer.
*
* @param aName the setting being observed, or null to remove
* a generic observer that observes all settings
* @param aObserver the observer to remove
*/
void removeObserver(in AString aName, in nsIContentPrefObserver aObserver);
// The component that the service uses to determine the groups to which
// URIs belong. By default this is the "hostname grouper", which groups
// URIs by full hostname (a.k.a. site).
readonly attribute nsIContentURIGrouper grouper;
};

View File

@@ -0,0 +1,50 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIURI.idl"
[scriptable, uuid(4bb38cb4-c3cb-4d17-9799-1b3132b39723)]
interface nsIContentURIGrouper : nsISupports
{
/**
* Determine the group to which the URI belongs.
*
* @param aURI the URI to group
*
* @returns the group to which the URI belongs
*/
AString group(in nsIURI aURI);
};

View File

@@ -0,0 +1,48 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Content Prefs (cpref).
#
# The Initial Developer of the Original Code is Mozilla.
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Myk Melez <myk@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = contentprefs
EXTRA_COMPONENTS = nsContentPrefService.js
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,965 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
function ContentPrefService() {}
ContentPrefService.prototype = {
//**************************************************************************//
// Convenience Getters
// Observer Service
__observerSvc: null,
get _observerSvc() {
if (!this.__observerSvc)
this.__observerSvc = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
return this.__observerSvc;
},
// Console Service
__consoleSvc: null,
get _consoleSvc() {
if (!this.__consoleSvc)
this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
return this.__consoleSvc;
},
//**************************************************************************//
// Initialization & Destruction
_init: function ContentPrefService__init() {
// If this throws an exception, it causes the getService call to fail,
// but the next time a consumer tries to retrieve the service, we'll try
// to initialize the database again, which might work if the failure
// was due to a temporary condition (like being out of disk space).
this._dbInit();
// Observe shutdown so we can shut down the database connection.
this._observerSvc.addObserver(this, "xpcom-shutdown", false);
},
_destroy: function ContentPrefService__destroy() {
this._observerSvc.removeObserver(this, "xpcom-shutdown");
// mozStorage shuts down the database for us automatically, but this
// helps us avoid the appearance of leaking the connection.
this._dbConnection = null;
},
//**************************************************************************//
// nsISupports
_interfaces: [Ci.nsIContentPrefService, Ci.nsISupports],
QueryInterface: function ContentPrefService_QueryInterface(aIID) {
if (!this._interfaces.some( function(v) { return aIID.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
//**************************************************************************//
// nsIObserver
observe: function ContentPrefService_observe(subject, topic, data) {
switch (topic) {
case "xpcom-shutdown":
this._destroy();
break;
default:
break;
}
},
//**************************************************************************//
// nsIContentPrefService
getPref: function ContentPrefService_getPref(aURI, aName) {
if (aURI) {
var group = this.grouper.group(aURI);
return this._selectPref(group, aName);
}
else
return this._selectGlobalPref(aName);
},
setPref: function ContentPrefService_setPref(aURI, aName, aValue) {
// If the pref is already set to the value, there's nothing more to do.
var currentValue = this.getPref(aURI, aName);
if (typeof currentValue != "undefined" && currentValue == aValue)
return;
var settingID = this._selectSettingID(aName) || this._insertSetting(aName);
var group, groupID, prefID;
if (aURI) {
group = this.grouper.group(aURI);
groupID = this._selectGroupID(group) || this._insertGroup(group);
prefID = this._selectPrefID(groupID, settingID);
}
else {
group = null;
groupID = null;
prefID = this._selectGlobalPrefID(settingID);
}
// Update the existing record, if any, or create a new one.
if (prefID)
this._updatePref(prefID, aValue);
else
this._insertPref(groupID, settingID, aValue);
for each (var observer in this._getObservers(aName)) {
try {
observer.onContentPrefSet(group, aName, aValue);
}
catch(ex) {
Cu.reportError(ex);
}
}
},
hasPref: function ContentPrefService_hasPref(aURI, aName) {
// XXX If consumers end up calling this method regularly, then we should
// optimize this to query the database directly.
return (typeof this.getPref(aURI, aName) != "undefined");
},
removePref: function ContentPrefService_removePref(aURI, aName) {
// If there's no old value, then there's nothing to remove.
if (!this.hasPref(aURI, aName))
return;
var settingID = this._selectSettingID(aName);
var group, groupID, prefID;
if (aURI) {
group = this.grouper.group(aURI);
groupID = this._selectGroupID(group);
prefID = this._selectPrefID(groupID, settingID);
}
else {
group = null;
groupID = null;
prefID = this._selectGlobalPrefID(settingID);
}
this._deletePref(prefID);
// Get rid of extraneous records that are no longer being used.
this._deleteSettingIfUnused(settingID);
if (groupID)
this._deleteGroupIfUnused(groupID);
for each (var observer in this._getObservers(aName)) {
try {
observer.onContentPrefRemoved(group, aName);
}
catch(ex) {
Cu.reportError(ex);
}
}
},
getPrefs: function ContentPrefService_getPrefs(aURI) {
if (aURI) {
var group = this.grouper.group(aURI);
return this._selectPrefs(group);
}
else
return this._selectGlobalPrefs();
},
// A hash of arrays of observers, indexed by setting name.
_observers: {},
// An array of generic observers, which observe all settings.
_genericObservers: [],
addObserver: function ContentPrefService_addObserver(aName, aObserver) {
var observers;
if (aName) {
if (!this._observers[aName])
this._observers[aName] = [];
observers = this._observers[aName];
}
else
observers = this._genericObservers;
if (observers.indexOf(aObserver) == -1)
observers.push(aObserver);
},
removeObserver: function ContentPrefService_removeObserver(aName, aObserver) {
var observers;
if (aName) {
if (!this._observers[aName])
return;
observers = this._observers[aName];
}
else
observers = this._genericObservers;
if (observers.indexOf(aObserver) != -1)
observers.splice(observers.indexOf(aObserver), 1);
},
/**
* Construct a list of observers to notify about a change to some setting,
* putting setting-specific observers before before generic ones, so observers
* that initialize individual settings (like the page style controller)
* execute before observers that display multiple settings and depend on them
* being initialized first (like the content prefs sidebar).
*/
_getObservers: function ContentPrefService__getObservers(aName) {
var observers = [];
if (aName && this._observers[aName])
observers = observers.concat(this._observers[aName]);
observers = observers.concat(this._genericObservers);
return observers;
},
_grouper: null,
get grouper() {
if (!this._grouper)
this._grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"].
getService(Ci.nsIContentURIGrouper);
return this._grouper;
},
//**************************************************************************//
// Data Retrieval & Modification
__stmtSelectPref: null,
get _stmtSelectPref() {
if (!this.__stmtSelectPref)
this.__stmtSelectPref = this._dbCreateStatement(
"SELECT prefs.value AS value " +
"FROM prefs " +
"JOIN groups ON prefs.groupID = groups.id " +
"JOIN settings ON prefs.settingID = settings.id " +
"WHERE groups.name = :group " +
"AND settings.name = :setting"
);
return this.__stmtSelectPref;
},
_selectPref: function ContentPrefService__selectPref(aGroup, aSetting) {
var value;
try {
this._stmtSelectPref.params.group = aGroup;
this._stmtSelectPref.params.setting = aSetting;
if (this._stmtSelectPref.step())
value = this._stmtSelectPref.row["value"];
}
finally {
this._stmtSelectPref.reset();
}
return value;
},
__stmtSelectGlobalPref: null,
get _stmtSelectGlobalPref() {
if (!this.__stmtSelectGlobalPref)
this.__stmtSelectGlobalPref = this._dbCreateStatement(
"SELECT prefs.value AS value " +
"FROM prefs " +
"JOIN settings ON prefs.settingID = settings.id " +
"WHERE prefs.groupID IS NULL " +
"AND settings.name = :name"
);
return this.__stmtSelectGlobalPref;
},
_selectGlobalPref: function ContentPrefService__selectGlobalPref(aName) {
var value;
try {
this._stmtSelectGlobalPref.params.name = aName;
if (this._stmtSelectGlobalPref.step())
value = this._stmtSelectGlobalPref.row["value"];
}
finally {
this._stmtSelectGlobalPref.reset();
}
return value;
},
__stmtSelectGroupID: null,
get _stmtSelectGroupID() {
if (!this.__stmtSelectGroupID)
this.__stmtSelectGroupID = this._dbCreateStatement(
"SELECT groups.id AS id " +
"FROM groups " +
"WHERE groups.name = :name "
);
return this.__stmtSelectGroupID;
},
_selectGroupID: function ContentPrefService__selectGroupID(aName) {
var id;
try {
this._stmtSelectGroupID.params.name = aName;
if (this._stmtSelectGroupID.step())
id = this._stmtSelectGroupID.row["id"];
}
finally {
this._stmtSelectGroupID.reset();
}
return id;
},
__stmtInsertGroup: null,
get _stmtInsertGroup() {
if (!this.__stmtInsertGroup)
this.__stmtInsertGroup = this._dbCreateStatement(
"INSERT INTO groups (name) VALUES (:name)"
);
return this.__stmtInsertGroup;
},
_insertGroup: function ContentPrefService__insertGroup(aName) {
this._stmtInsertGroup.params.name = aName;
this._stmtInsertGroup.execute();
return this._dbConnection.lastInsertRowID;
},
__stmtSelectSettingID: null,
get _stmtSelectSettingID() {
if (!this.__stmtSelectSettingID)
this.__stmtSelectSettingID = this._dbCreateStatement(
"SELECT id FROM settings WHERE name = :name"
);
return this.__stmtSelectSettingID;
},
_selectSettingID: function ContentPrefService__selectSettingID(aName) {
var id;
try {
this._stmtSelectSettingID.params.name = aName;
if (this._stmtSelectSettingID.step())
id = this._stmtSelectSettingID.row["id"];
}
finally {
this._stmtSelectSettingID.reset();
}
return id;
},
__stmtInsertSetting: null,
get _stmtInsertSetting() {
if (!this.__stmtInsertSetting)
this.__stmtInsertSetting = this._dbCreateStatement(
"INSERT INTO settings (name) VALUES (:name)"
);
return this.__stmtInsertSetting;
},
_insertSetting: function ContentPrefService__insertSetting(aName) {
this._stmtInsertSetting.params.name = aName;
this._stmtInsertSetting.execute();
return this._dbConnection.lastInsertRowID;
},
__stmtSelectPrefID: null,
get _stmtSelectPrefID() {
if (!this.__stmtSelectPrefID)
this.__stmtSelectPrefID = this._dbCreateStatement(
"SELECT id FROM prefs WHERE groupID = :groupID AND settingID = :settingID"
);
return this.__stmtSelectPrefID;
},
_selectPrefID: function ContentPrefService__selectPrefID(aGroupID, aSettingID) {
var id;
try {
this._stmtSelectPrefID.params.groupID = aGroupID;
this._stmtSelectPrefID.params.settingID = aSettingID;
if (this._stmtSelectPrefID.step())
id = this._stmtSelectPrefID.row["id"];
}
finally {
this._stmtSelectPrefID.reset();
}
return id;
},
__stmtSelectGlobalPrefID: null,
get _stmtSelectGlobalPrefID() {
if (!this.__stmtSelectGlobalPrefID)
this.__stmtSelectGlobalPrefID = this._dbCreateStatement(
"SELECT id FROM prefs WHERE groupID IS NULL AND settingID = :settingID"
);
return this.__stmtSelectGlobalPrefID;
},
_selectGlobalPrefID: function ContentPrefService__selectGlobalPrefID(aSettingID) {
var id;
try {
this._stmtSelectGlobalPrefID.params.settingID = aSettingID;
if (this._stmtSelectGlobalPrefID.step())
id = this._stmtSelectGlobalPrefID.row["id"];
}
finally {
this._stmtSelectGlobalPrefID.reset();
}
return id;
},
__stmtInsertPref: null,
get _stmtInsertPref() {
if (!this.__stmtInsertPref)
this.__stmtInsertPref = this._dbCreateStatement(
"INSERT INTO prefs (groupID, settingID, value) " +
"VALUES (:groupID, :settingID, :value)"
);
return this.__stmtInsertPref;
},
_insertPref: function ContentPrefService__insertPref(aGroupID, aSettingID, aValue) {
this._stmtInsertPref.params.groupID = aGroupID;
this._stmtInsertPref.params.settingID = aSettingID;
this._stmtInsertPref.params.value = aValue;
this._stmtInsertPref.execute();
return this._dbConnection.lastInsertRowID;
},
__stmtUpdatePref: null,
get _stmtUpdatePref() {
if (!this.__stmtUpdatePref)
this.__stmtUpdatePref = this._dbCreateStatement(
"UPDATE prefs SET value = :value WHERE id = :id"
);
return this.__stmtUpdatePref;
},
_updatePref: function ContentPrefService__updatePref(aPrefID, aValue) {
this._stmtUpdatePref.params.id = aPrefID;
this._stmtUpdatePref.params.value = aValue;
this._stmtUpdatePref.execute();
},
__stmtDeletePref: null,
get _stmtDeletePref() {
if (!this.__stmtDeletePref)
this.__stmtDeletePref = this._dbCreateStatement(
"DELETE FROM prefs WHERE id = :id"
);
return this.__stmtDeletePref;
},
_deletePref: function ContentPrefService__deletePref(aPrefID) {
this._stmtDeletePref.params.id = aPrefID;
this._stmtDeletePref.execute();
},
__stmtDeleteSettingIfUnused: null,
get _stmtDeleteSettingIfUnused() {
if (!this.__stmtDeleteSettingIfUnused)
this.__stmtDeleteSettingIfUnused = this._dbCreateStatement(
"DELETE FROM settings WHERE id = :id " +
"AND id NOT IN (SELECT DISTINCT settingID FROM prefs)"
);
return this.__stmtDeleteSettingIfUnused;
},
_deleteSettingIfUnused: function ContentPrefService__deleteSettingIfUnused(aSettingID) {
this._stmtDeleteSettingIfUnused.params.id = aSettingID;
this._stmtDeleteSettingIfUnused.execute();
},
__stmtDeleteGroupIfUnused: null,
get _stmtDeleteGroupIfUnused() {
if (!this.__stmtDeleteGroupIfUnused)
this.__stmtDeleteGroupIfUnused = this._dbCreateStatement(
"DELETE FROM groups WHERE id = :id " +
"AND id NOT IN (SELECT DISTINCT groupID FROM prefs)"
);
return this.__stmtDeleteGroupIfUnused;
},
_deleteGroupIfUnused: function ContentPrefService__deleteGroupIfUnused(aGroupID) {
this._stmtDeleteGroupIfUnused.params.id = aGroupID;
this._stmtDeleteGroupIfUnused.execute();
},
__stmtSelectPrefs: null,
get _stmtSelectPrefs() {
if (!this.__stmtSelectPrefs)
this.__stmtSelectPrefs = this._dbCreateStatement(
"SELECT settings.name AS name, prefs.value AS value " +
"FROM prefs " +
"JOIN groups ON prefs.groupID = groups.id " +
"JOIN settings ON prefs.settingID = settings.id " +
"WHERE groups.name = :group "
);
return this.__stmtSelectPrefs;
},
_selectPrefs: function ContentPrefService__selectPrefs(aGroup) {
var prefs = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
try {
this._stmtSelectPrefs.params.group = aGroup;
while (this._stmtSelectPrefs.step())
prefs.setProperty(this._stmtSelectPrefs.row["name"],
this._stmtSelectPrefs.row["value"]);
}
finally {
this._stmtSelectPrefs.reset();
}
return prefs;
},
__stmtSelectGlobalPrefs: null,
get _stmtSelectGlobalPrefs() {
if (!this.__stmtSelectGlobalPrefs)
this.__stmtSelectGlobalPrefs = this._dbCreateStatement(
"SELECT settings.name AS name, prefs.value AS value " +
"FROM prefs " +
"JOIN settings ON prefs.settingID = settings.id " +
"WHERE prefs.groupID IS NULL"
);
return this.__stmtSelectGlobalPrefs;
},
_selectGlobalPrefs: function ContentPrefService__selectGlobalPrefs() {
var prefs = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
try {
while (this._stmtSelectGlobalPrefs.step())
prefs.setProperty(this._stmtSelectGlobalPrefs.row["name"],
this._stmtSelectGlobalPrefs.row["value"]);
}
finally {
this._stmtSelectGlobalPrefs.reset();
}
return prefs;
},
//**************************************************************************//
// Database Creation & Access
_dbVersion: 2,
_dbSchema: {
groups: "id INTEGER PRIMARY KEY, \
name TEXT NOT NULL",
settings: "id INTEGER PRIMARY KEY, \
name TEXT NOT NULL",
prefs: "id INTEGER PRIMARY KEY, \
groupID INTEGER REFERENCES groups(id), \
settingID INTEGER NOT NULL REFERENCES settings(id), \
value BLOB"
},
_dbConnection: null,
_dbCreateStatement: function(aSQLString) {
try {
var statement = this._dbConnection.createStatement(aSQLString);
}
catch(ex) {
Cu.reportError("error creating statement " + aSQLString + ": " +
this._dbConnection.lastError + " - " +
this._dbConnection.lastErrorString);
throw ex;
}
var wrappedStatement = Cc["@mozilla.org/storage/statement-wrapper;1"].
createInstance(Ci.mozIStorageStatementWrapper);
wrappedStatement.initialize(statement);
return wrappedStatement;
},
// _dbInit and the methods it calls (_dbCreate, _dbMigrate, and version-
// specific migration methods) must be careful not to call any method
// of the service that assumes the database connection has already been
// initialized, since it won't be initialized until at the end of _dbInit.
_dbInit: function ContentPrefService__dbInit() {
this._log("initializing database");
var dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
var dbFile = dirService.get("ProfD", Ci.nsIFile);
dbFile.append("content-prefs.sqlite");
var dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
var dbConnection;
if (!dbFile.exists()) {
this._log("no database file; creating database");
dbConnection = this._dbCreate(dbService, dbFile);
}
else {
try {
dbConnection = dbService.openDatabase(dbFile);
// Get the version of the database in the file.
var statement, version;
try {
statement = dbConnection.createStatement("PRAGMA user_version");
statement.executeStep();
version = statement.getInt32(0);
}
finally {
statement.reset();
}
if (version != this._dbVersion) {
this._log("database: v" + version + ", application: v" +
this._dbVersion + "; migrating database");
this._dbMigrate(dbConnection, version, this._dbVersion);
}
}
catch (ex) {
// If the database file is corrupted, I'm not sure whether we should
// just delete the corrupted file or back it up. For now I'm just
// deleting it, but here's some code that backs it up (but doesn't limit
// the number of backups, which is probably necessary, thus I'm not
// using this code):
//var backup = this._dbFile.clone();
//backup.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
//backup.remove(false);
//this._dbFile.moveTo(null, backup.leafName);
if (ex.result == Cr.NS_ERROR_FILE_CORRUPTED) {
this._log("database file corrupted; recreating database");
// Remove the corrupted file, then recreate it.
dbFile.remove(false);
dbConnection = this._dbCreate(dbService, dbFile);
}
else
throw ex;
}
}
this._dbConnection = dbConnection;
},
_dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) {
var dbConnection = aDBService.openDatabase(aDBFile);
for (var table in this._dbSchema)
dbConnection.createTable(table, this._dbSchema[table]);
dbConnection.executeSimpleSQL("PRAGMA user_version = " + this._dbVersion);
return dbConnection;
},
_dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
this._log("migrating database from v" + aOldVersion + " to v" + aNewVersion);
aDBConnection.beginTransaction();
try {
this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
aDBConnection.commitTransaction();
}
catch(ex) {
this._log("migration failed: " + ex + "; DB error code " +
aDBConnection.lastError + ": " + aDBConnection.lastErrorString +
"; rolling back transaction");
aDBConnection.rollbackTransaction();
throw ex;
}
}
else
throw("can't migrate database from v" + aOldVersion +
" to v" + aNewVersion + ": no migrator function");
},
_dbMigrate0To2: function ContentPrefService___dbMigrate0To2(aDBConnection) {
this._log("migrating sites to the groups table");
aDBConnection.createTable("groups", this._dbSchema.groups);
aDBConnection.executeSimpleSQL(
"INSERT INTO groups (id, name) SELECT id, name FROM sites"
);
this._log("migrating keys to the settings table");
aDBConnection.createTable("settings", this._dbSchema.settings);
aDBConnection.executeSimpleSQL(
"INSERT INTO settings (id, name) SELECT id, name FROM keys"
);
this._log("migrating prefs from the old prefs table to the new one");
aDBConnection.executeSimpleSQL("ALTER TABLE prefs RENAME TO prefsOld");
aDBConnection.createTable("prefs", this._dbSchema.prefs);
aDBConnection.executeSimpleSQL(
"INSERT INTO prefs (id, groupID, settingID, value) " +
"SELECT id, site_id, key_id, value FROM prefsOld"
);
// Drop obsolete tables.
this._log("dropping obsolete tables");
aDBConnection.executeSimpleSQL("DROP TABLE prefsOld");
aDBConnection.executeSimpleSQL("DROP TABLE keys");
aDBConnection.executeSimpleSQL("DROP TABLE sites");
aDBConnection.executeSimpleSQL("PRAGMA user_version = " + this._dbVersion);
},
_dbMigrate1To2: function ContentPrefService___dbMigrate1To2(aDBConnection) {
this._log("migrating groups from the old groups table to the new one");
aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
aDBConnection.createTable("groups", this._dbSchema.groups);
aDBConnection.executeSimpleSQL(
"INSERT INTO groups (id, name) " +
"SELECT id, name FROM groupsOld"
);
this._log("dropping obsolete tables");
aDBConnection.executeSimpleSQL("DROP TABLE groupers");
aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
aDBConnection.executeSimpleSQL("PRAGMA user_version = " + this._dbVersion);
},
//**************************************************************************//
// Utilities
/**
* Get an app pref or a default value if the pref doesn't exist.
*
* @param aPrefName
* @param aDefaultValue
* @returns the pref's value or the default (if it is missing)
*/
_getAppPref: function ContentPrefService__getAppPref(aPrefName, aDefaultValue) {
try {
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
switch (prefBranch.getPrefType(aPrefName)) {
case prefBranch.PREF_STRING:
return prefBranch.getCharPref(aPrefName);
case prefBranch.PREF_INT:
return prefBranch.getIntPref(aPrefName);
case prefBranch.PREF_BOOL:
return prefBranch.getBoolPref(aPrefName);
}
}
catch (ex) { /* return the default value */ }
return aDefaultValue;
},
_log: function ContentPrefService__log(aMessage) {
if (!this._getAppPref("browser.preferences.content.log", false))
return;
aMessage = "*** ContentPrefService: " + aMessage;
dump(aMessage + "\n");
this._consoleSvc.logStringMessage(aMessage);
}
};
function HostnameGrouper() {}
HostnameGrouper.prototype = {
//**************************************************************************//
// nsISupports
_interfaces: [Ci.nsIContentURIGrouper, Ci.nsISupports],
QueryInterface: function HostnameGrouper_QueryInterface(aIID) {
if (!this._interfaces.some( function(v) { return aIID.equals(v) } ))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
//**************************************************************************//
// nsIContentURIGrouper
group: function HostnameGrouper_group(aURI) {
var group;
try {
// Accessing the host property of the URI will throw an exception
// if the URI is of a type that doesn't have a host property.
// Otherwise, we manually throw an exception if the host is empty,
// since the effect is the same (we can't derive a group from it).
group = aURI.host;
if (!group)
throw("can't derive group from host; no host in URI");
}
catch(ex) {
// If we don't have a host, then use the entire URI (minus the query,
// reference, and hash, if possible) as the group. This means that URIs
// like about:mozilla and about:blank will be considered separate groups,
// but at least they'll be grouped somehow.
// This also means that each individual file: URL will be considered
// its own group. This seems suboptimal, but so does treating the entire
// file: URL space as a single group (especially if folks start setting
// group-specific capabilities prefs).
// XXX Is there something better we can do here?
try {
var url = aURI.QueryInterface(Ci.nsIURL);
group = aURI.prePath + url.filePath;
}
catch(ex) {
group = aURI.spec;
}
}
return group;
}
};
var gModule = {
registerSelf: function(aComponentManager, aFileSpec, aLocation, aType) {
aComponentManager = aComponentManager.QueryInterface(Ci.nsIComponentRegistrar);
for (var key in this._objects) {
var obj = this._objects[key];
aComponentManager.registerFactoryLocation(obj.CID,
obj.className,
obj.contractID,
aFileSpec,
aLocation,
aType);
}
},
unregisterSelf: function(aComponentManager, aFileSpec, aLocation) {},
getClassObject: function(aComponentManager, aCID, aIID) {
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
for (var key in this._objects) {
if (aCID.equals(this._objects[key].CID))
return this._objects[key].factory;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
_objects: {
service: {
CID : Components.ID("{e6a3f533-4ffa-4615-8eb4-d4e72d883fa7}"),
contractID : "@mozilla.org/content-pref/service;1",
className : "Content Pref Service",
factory : ContentPrefServiceFactory = {
createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
var service = new ContentPrefService();
service._init();
return service.QueryInterface(aIID);
}
}
},
grouper: {
CID : Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"),
contractID : "@mozilla.org/content-pref/hostname-grouper;1",
className : "Hostname Grouper",
factory : HostnameGrouperFactory = {
createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
var grouper = new HostnameGrouper();
return grouper.QueryInterface(aIID);
}
}
}
},
canUnload: function(aComponentManager) {
return true;
}
};
function NSGetModule(aComponentManager, aFileSpec) {
return gModule;
}

View File

@@ -0,0 +1,52 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla.org.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Dietrich Ayala <dietrich@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = test_toolkit_contentprefs
ifdef MOZ_PHOENIX
XPCSHELL_TESTS = unit
endif
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,163 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Inspired by the Places infrastructure in head_bookmarks.js
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const CONTENT_PREFS_DB_FILENAME = "content-prefs.sqlite";
var ContentPrefTest = {
//**************************************************************************//
// Convenience Getters
__dirSvc: null,
get _dirSvc() {
if (!this.__dirSvc)
this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
return this.__dirSvc;
},
__consoleSvc: null,
get _consoleSvc() {
if (!this.__consoleSvc)
this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
return this.__consoleSvc;
},
__ioSvc: null,
get _ioSvc() {
if (!this.__ioSvc)
this.__ioSvc = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return this.__ioSvc;
},
//**************************************************************************//
// nsISupports
interfaces: [Ci.nsIDirectoryServiceProvider, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
//**************************************************************************//
// nsIDirectoryServiceProvider
getFile: function ContentPrefTest_getFile(property, persistent) {
persistent.value = true;
if (property == "ProfD")
return this._dirSvc.get("CurProcD", Ci.nsIFile);
// This causes extraneous errors to show up in the log when the directory
// service asks us first for CurProcD and MozBinD. I wish there was a way
// to suppress those errors.
throw Cr.NS_ERROR_FAILURE;
},
//**************************************************************************//
// Utilities
getURI: function ContentPrefTest_getURI(spec) {
return this._ioSvc.newURI(spec, null, null);
},
/**
* Get the profile directory, registering ourselves as a provider
* of that directory if necessary.
*/
getProfileDir: function ContentPrefTest_getProfileDir() {
var profileDir;
try {
profileDir = this._dirSvc.get("ProfD", Ci.nsIFile);
}
catch (e) {}
if (!profileDir) {
this._dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(this);
profileDir = this._dirSvc.get("ProfD", Ci.nsIFile);
}
return profileDir;
},
/**
* Delete the content pref service's persistent datastore. We do this before
* and after running tests to make sure we start from scratch each time.
*/
deleteDatabase: function ContentPrefTest_deleteDatabase() {
try {
var file = this.getProfileDir()
file.append(CONTENT_PREFS_DB_FILENAME);
if (file.exists())
file.remove(false);
}
catch(ex) {
Cu.reportError(ex);
}
},
/**
* Log a message to the console and the test log.
*/
log: function ContentPrefTest_log(message) {
message = "*** ContentPrefTest: " + message;
this._consoleSvc.logStringMessage(message);
print(message);
}
};
ContentPrefTest.deleteDatabase();
// Turn on logging for the content preferences service so we can troubleshoot
// problems with the tests.
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.preferences.content.log", true);

View File

@@ -0,0 +1,37 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
ContentPrefTest.deleteDatabase();

View File

@@ -0,0 +1,244 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function run_test() {
var cps = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService);
var uri = ContentPrefTest.getURI("http://www.example.com/");
//**************************************************************************//
// Nonexistent Pref
do_check_eq(cps.getPref(uri, "test.nonexistent.getPref"), undefined);
do_check_eq(cps.setPref(uri, "test.nonexistent.setPref", 5), undefined);
do_check_false(cps.hasPref(uri, "test.nonexistent.hasPref"));
do_check_eq(cps.removePref(uri, "test.nonexistent.removePref"), undefined);
//**************************************************************************//
// Existing Pref
cps.setPref(uri, "test.existing", 5);
// getPref should return the pref value
do_check_eq(cps.getPref(uri, "test.existing"), 5);
// setPref should return undefined and change the value of the pref
do_check_eq(cps.setPref(uri, "test.existing", 6), undefined);
do_check_eq(cps.getPref(uri, "test.existing"), 6);
// hasPref should return true
do_check_true(cps.hasPref(uri, "test.existing"));
// removePref should return undefined and remove the pref
do_check_eq(cps.removePref(uri, "test.existing"), undefined);
do_check_false(cps.hasPref(uri, "test.existing"));
//**************************************************************************//
// Round-Trip Data Integrity
// Make sure pref values remain the same from setPref to getPref.
cps.setPref(uri, "test.data-integrity.integer", 5);
do_check_eq(cps.getPref(uri, "test.data-integrity.integer"), 5);
cps.setPref(uri, "test.data-integrity.float", 5.5);
do_check_eq(cps.getPref(uri, "test.data-integrity.float"), 5.5);
cps.setPref(uri, "test.data-integrity.boolean", true);
do_check_eq(cps.getPref(uri, "test.data-integrity.boolean"), true);
cps.setPref(uri, "test.data-integrity.string", "test");
do_check_eq(cps.getPref(uri, "test.data-integrity.string"), "test");
cps.setPref(uri, "test.data-integrity.null", null);
do_check_eq(cps.getPref(uri, "test.data-integrity.null"), null);
// XXX Test arbitrary binary data.
// Make sure hasPref and removePref work on all data types.
do_check_true(cps.hasPref(uri, "test.data-integrity.integer"));
do_check_true(cps.hasPref(uri, "test.data-integrity.float"));
do_check_true(cps.hasPref(uri, "test.data-integrity.boolean"));
do_check_true(cps.hasPref(uri, "test.data-integrity.string"));
do_check_true(cps.hasPref(uri, "test.data-integrity.null"));
do_check_eq(cps.removePref(uri, "test.data-integrity.integer"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.float"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.boolean"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.string"), undefined);
do_check_eq(cps.removePref(uri, "test.data-integrity.null"), undefined);
do_check_false(cps.hasPref(uri, "test.data-integrity.integer"));
do_check_false(cps.hasPref(uri, "test.data-integrity.float"));
do_check_false(cps.hasPref(uri, "test.data-integrity.boolean"));
do_check_false(cps.hasPref(uri, "test.data-integrity.string"));
do_check_false(cps.hasPref(uri, "test.data-integrity.null"));
//**************************************************************************//
// getPrefs
cps.setPref(uri, "test.getPrefs.a", 1);
cps.setPref(uri, "test.getPrefs.b", 2);
cps.setPref(uri, "test.getPrefs.c", 3);
var prefs = cps.getPrefs(uri);
do_check_true(prefs.hasKey("test.getPrefs.a"));
do_check_eq(prefs.get("test.getPrefs.a"), 1);
do_check_true(prefs.hasKey("test.getPrefs.b"));
do_check_eq(prefs.get("test.getPrefs.b"), 2);
do_check_true(prefs.hasKey("test.getPrefs.c"));
do_check_eq(prefs.get("test.getPrefs.c"), 3);
//**************************************************************************//
// Site-Specificity
// These are all different sites, and setting a pref for one of them
// shouldn't set it for the others.
var uri1 = ContentPrefTest.getURI("http://www.domain1.com/");
var uri2 = ContentPrefTest.getURI("http://foo.domain1.com/");
var uri3 = ContentPrefTest.getURI("http://domain1.com/");
var uri4 = ContentPrefTest.getURI("http://www.domain2.com/");
cps.setPref(uri1, "test.site-specificity.uri1", 5);
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri1"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri1"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri1"));
cps.setPref(uri2, "test.site-specificity.uri2", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri2"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri2"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri2"));
cps.setPref(uri3, "test.site-specificity.uri3", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri3"));
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri3"));
do_check_false(cps.hasPref(uri4, "test.site-specificity.uri3"));
cps.setPref(uri4, "test.site-specificity.uri4", 5);
do_check_false(cps.hasPref(uri1, "test.site-specificity.uri4"));
do_check_false(cps.hasPref(uri2, "test.site-specificity.uri4"));
do_check_false(cps.hasPref(uri3, "test.site-specificity.uri4"));
//**************************************************************************//
// Observers
var specificObserver = {
interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
numTimesSetCalled: 0,
onContentPrefSet: function specificObserver_onContentPrefSet(group, name, value) {
++this.numTimesSetCalled;
do_check_eq(group, "www.example.com");
do_check_eq(name, "test.observer.1");
do_check_eq(value, "test value");
},
numTimesRemovedCalled: 0,
onContentPrefRemoved: function specificObserver_onContentPrefRemoved(group, name) {
++this.numTimesRemovedCalled;
do_check_eq(group, "www.example.com");
do_check_eq(name, "test.observer.1");
}
};
var genericObserver = {
interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports],
QueryInterface: function ContentPrefTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
numTimesSetCalled: 0,
onContentPrefSet: function genericObserver_onContentPrefSet(group, name, value) {
++this.numTimesSetCalled;
do_check_eq(group, "www.example.com");
if (name != "test.observer.1" && name != "test.observer.2")
do_throw("genericObserver.onContentPrefSet: " +
"name not in (test.observer.1, test.observer.2)");
do_check_eq(value, "test value");
},
numTimesRemovedCalled: 0,
onContentPrefRemoved: function genericObserver_onContentPrefRemoved(group, name) {
++this.numTimesRemovedCalled;
do_check_eq(group, "www.example.com");
if (name != "test.observer.1" && name != "test.observer.2")
do_throw("genericObserver.onContentPrefSet: " +
"name not in (test.observer.1, test.observer.2)");
}
};
// Make sure we can add observers, observers get notified about changes,
// specific observers only get notified about changes to the specific setting,
// and generic observers get notified about changes to all settings.
cps.addObserver("test.observer.1", specificObserver);
cps.addObserver(null, genericObserver);
cps.setPref(uri, "test.observer.1", "test value");
cps.setPref(uri, "test.observer.2", "test value");
cps.removePref(uri, "test.observer.1");
cps.removePref(uri, "test.observer.2");
do_check_eq(specificObserver.numTimesSetCalled, 1);
do_check_eq(genericObserver.numTimesSetCalled, 2);
do_check_eq(specificObserver.numTimesRemovedCalled, 1);
do_check_eq(genericObserver.numTimesRemovedCalled, 2);
// Make sure we can remove observers and they don't get notified
// about changes anymore.
cps.removeObserver("test.observer.1", specificObserver);
cps.removeObserver(null, genericObserver);
cps.setPref(uri, "test.observer.1", "test value");
cps.removePref(uri, "test.observer.1", "test value");
do_check_eq(specificObserver.numTimesSetCalled, 1);
do_check_eq(genericObserver.numTimesSetCalled, 2);
do_check_eq(specificObserver.numTimesRemovedCalled, 1);
do_check_eq(genericObserver.numTimesRemovedCalled, 2);
}