Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tools/xo_bundle/components/nsContentPrefService.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xo_bundle/components/nsContentPrefService.js')
-rwxr-xr-xtools/xo_bundle/components/nsContentPrefService.js944
1 files changed, 944 insertions, 0 deletions
diff --git a/tools/xo_bundle/components/nsContentPrefService.js b/tools/xo_bundle/components/nsContentPrefService.js
new file mode 100755
index 0000000..1a584bc
--- /dev/null
+++ b/tools/xo_bundle/components/nsContentPrefService.js
@@ -0,0 +1,944 @@
+/* ***** 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>
+ * Ehsan Akhgari <ehsan.akhgari@gmail.com>
+ *
+ * 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;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function ContentPrefService() {
+ // 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);
+}
+
+ContentPrefService.prototype = {
+ //**************************************************************************//
+ // XPCOM Plumbing
+
+ classDescription: "Content Pref Service",
+ classID: Components.ID("{e6a3f533-4ffa-4615-8eb4-d4e72d883fa7}"),
+ contractID: "@mozilla.org/content-pref/service;1",
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPrefService]),
+
+
+ //**************************************************************************//
+ // Convenience Getters
+
+ // Observer Service
+ __observerSvc: null,
+ get _observerSvc ContentPrefService_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 ContentPrefService_get__consoleSvc() {
+ if (!this.__consoleSvc)
+ this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+ return this.__consoleSvc;
+ },
+
+ // Preferences Service
+ __prefSvc: null,
+ get _prefSvc ContentPrefService_get__prefSvc() {
+ if (!this.__prefSvc)
+ this.__prefSvc = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch2);
+ return this.__prefSvc;
+ },
+
+
+ //**************************************************************************//
+ // Destruction
+
+ _destroy: function ContentPrefService__destroy() {
+ this._observerSvc.removeObserver(this, "xpcom-shutdown");
+
+ // Delete references to XPCOM components to make sure we don't leak them
+ // (although we haven't observed leakage in tests). Also delete references
+ // in _observers and _genericObservers to avoid cycles with those that
+ // refer to us and don't remove themselves from those observer pools.
+ for (var i in this) {
+ try { this[i] = null }
+ // Ignore "setting a property that has only a getter" exceptions.
+ catch(ex) {}
+ }
+ },
+
+
+ //**************************************************************************//
+ // nsIObserver
+
+ observe: function ContentPrefService_observe(subject, topic, data) {
+ switch (topic) {
+ case "xpcom-shutdown":
+ this._destroy();
+ break;
+ }
+ },
+
+
+ //**************************************************************************//
+ // nsIContentPrefService
+
+ getPref: function ContentPrefService_getPref(aURI, aName) {
+ if (aURI) {
+ var group = this.grouper.group(aURI);
+ return this._selectPref(group, aName);
+ }
+
+ 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") {
+ if (currentValue == aValue)
+ return;
+ }
+ else {
+ // If we are in private browsing mode, refuse to set new prefs
+ var inPrivateBrowsing = false;
+ try { // The Private Browsing service might not be available.
+ var pbs = Cc["@mozilla.org/privatebrowsing;1"].
+ getService(Ci.nsIPrivateBrowsingService);
+ inPrivateBrowsing = pbs.privateBrowsingEnabled;
+ } catch (e) {}
+ if (inPrivateBrowsing)
+ 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);
+ }
+ }
+ },
+
+ removeGroupedPrefs: function ContentPrefService_removeGroupedPrefs() {
+ this._dbConnection.beginTransaction();
+ try {
+ this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL");
+ this._dbConnection.executeSimpleSQL("DELETE FROM groups");
+ this._dbConnection.commitTransaction();
+ }
+ catch(ex) {
+ this._dbConnection.rollbackTransaction();
+ throw ex;
+ }
+ },
+
+ getPrefs: function ContentPrefService_getPrefs(aURI) {
+ if (aURI) {
+ var group = this.grouper.group(aURI);
+ return this._selectPrefs(group);
+ }
+
+ 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 ContentPrefService_get_grouper() {
+ if (!this._grouper)
+ this._grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"].
+ getService(Ci.nsIContentURIGrouper);
+ return this._grouper;
+ },
+
+ get DBConnection ContentPrefService_get_DBConnection() {
+ return this._dbConnection;
+ },
+
+
+ //**************************************************************************//
+ // Data Retrieval & Modification
+
+ __stmtSelectPref: null,
+ get _stmtSelectPref ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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 ContentPrefService_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: 3,
+
+ _dbSchema: {
+ tables: {
+ 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"
+ },
+ indices: {
+ groups_idx: {
+ table: "groups",
+ columns: ["name"]
+ },
+ settings_idx: {
+ table: "settings",
+ columns: ["name"]
+ },
+ prefs_idx: {
+ table: "prefs",
+ columns: ["groupID", "settingID"]
+ }
+ }
+ },
+
+ _dbConnection: null,
+
+ _dbCreateStatement: function ContentPrefService__dbCreateStatement(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() {
+ 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())
+ dbConnection = this._dbCreate(dbService, dbFile);
+ else {
+ try {
+ dbConnection = dbService.openDatabase(dbFile);
+ }
+ // If the connection isn't ready after we open the database, that means
+ // the database has been corrupted, so we back it up and then recreate it.
+ catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
+ dbConnection = this._dbBackUpAndRecreate(dbService, dbFile,
+ dbConnection);
+ }
+
+ // Get the version of the schema in the file.
+ var version = dbConnection.schemaVersion;
+
+ // Try to migrate the schema in the database to the current schema used by
+ // the service. If migration fails, back up the database and recreate it.
+ if (version != this._dbVersion) {
+ try {
+ this._dbMigrate(dbConnection, version, this._dbVersion);
+ }
+ catch(ex) {
+ Cu.reportError("error migrating DB: " + ex + "; backing up and recreating");
+ dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, dbConnection);
+ }
+ }
+ }
+
+ // Turn off disk synchronization checking to reduce disk churn and speed up
+ // operations when prefs are changed rapidly (such as when a user repeatedly
+ // changes the value of the browser zoom setting for a site).
+ //
+ // Note: this could cause database corruption if the OS crashes or machine
+ // loses power before the data gets written to disk, but this is considered
+ // a reasonable risk for the not-so-critical data stored in this database.
+ //
+ // If you really don't want to take this risk, however, just set the
+ // toolkit.storage.synchronous pref to 1 (NORMAL synchronization) or 2
+ // (FULL synchronization), in which case mozStorageConnection::Initialize
+ // will use that value, and we won't override it here.
+ if (!this._prefSvc.prefHasUserValue("toolkit.storage.synchronous"))
+ dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF");
+
+ this._dbConnection = dbConnection;
+ },
+
+ _dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) {
+ var dbConnection = aDBService.openDatabase(aDBFile);
+
+ try {
+ this._dbCreateSchema(dbConnection);
+ dbConnection.schemaVersion = this._dbVersion;
+ }
+ catch(ex) {
+ // If we failed to create the database (perhaps because the disk ran out
+ // of space), then remove the database file so we don't leave it in some
+ // half-created state from which we won't know how to recover.
+ dbConnection.close();
+ aDBFile.remove(false);
+ throw ex;
+ }
+
+ return dbConnection;
+ },
+
+ _dbCreateSchema: function ContentPrefService__dbCreateSchema(aDBConnection) {
+ this._dbCreateTables(aDBConnection);
+ this._dbCreateIndices(aDBConnection);
+ },
+
+ _dbCreateTables: function ContentPrefService__dbCreateTables(aDBConnection) {
+ for (let name in this._dbSchema.tables)
+ aDBConnection.createTable(name, this._dbSchema.tables[name]);
+ },
+
+ _dbCreateIndices: function ContentPrefService__dbCreateIndices(aDBConnection) {
+ for (let name in this._dbSchema.indices) {
+ let index = this._dbSchema.indices[name];
+ let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table +
+ "(" + index.columns.join(", ") + ")";
+ aDBConnection.executeSimpleSQL(statement);
+ }
+ },
+
+ _dbBackUpAndRecreate: function ContentPrefService__dbBackUpAndRecreate(aDBService,
+ aDBFile,
+ aDBConnection) {
+ aDBService.backupDatabaseFile(aDBFile, "content-prefs.sqlite.corrupt");
+
+ // Close the database, ignoring the "already closed" exception, if any.
+ // It'll be open if we're here because of a migration failure but closed
+ // if we're here because of database corruption.
+ try { aDBConnection.close() } catch(ex) {}
+
+ aDBFile.remove(false);
+
+ let dbConnection = this._dbCreate(aDBService, aDBFile);
+
+ return dbConnection;
+ },
+
+ _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
+ if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
+ aDBConnection.beginTransaction();
+ try {
+ this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
+ aDBConnection.schemaVersion = aNewVersion;
+ aDBConnection.commitTransaction();
+ }
+ catch(ex) {
+ aDBConnection.rollbackTransaction();
+ throw ex;
+ }
+ }
+ else
+ throw("no migrator function from version " + aOldVersion +
+ " to version " + aNewVersion);
+ },
+
+ /**
+ * If the schema version is 0, that means it was never set, which means
+ * the database was somehow created without the schema being applied, perhaps
+ * because the system ran out of disk space (although we check for this
+ * in _createDB) or because some other code created the database file without
+ * applying the schema. In any case, recover by simply reapplying the schema.
+ */
+ _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) {
+ this._dbCreateSchema(aDBConnection);
+ },
+
+ _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) {
+ aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
+ aDBConnection.createTable("groups", this._dbSchema.tables.groups);
+ aDBConnection.executeSimpleSQL(
+ "INSERT INTO groups (id, name) " +
+ "SELECT id, name FROM groupsOld"
+ );
+
+ aDBConnection.executeSimpleSQL("DROP TABLE groupers");
+ aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
+
+ this._dbCreateIndices(aDBConnection);
+ },
+
+ _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) {
+ this._dbCreateIndices(aDBConnection);
+ }
+
+};
+
+
+function HostnameGrouper() {}
+
+HostnameGrouper.prototype = {
+ //**************************************************************************//
+ // XPCOM Plumbing
+
+ classDescription: "Hostname Grouper",
+ classID: Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"),
+ contractID: "@mozilla.org/content-pref/hostname-grouper;1",
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]),
+
+
+ //**************************************************************************//
+ // 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;
+ }
+};
+
+
+//****************************************************************************//
+// XPCOM Plumbing
+
+var components = [ContentPrefService, HostnameGrouper];
+var NSGetModule = function ContentPrefService_NSGetModule(compMgr, fileSpec) {
+ return XPCOMUtils.generateModule(components);
+}