1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Content Preferences (cpref).
16 * The Initial Developer of the Original Code is Mozilla.
17 * Portions created by the Initial Developer are Copyright (C) 2006
18 * the Initial Developer. All Rights Reserved.
21 * Myk Melez <myk@mozilla.org>
22 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 const Ci = Components.interfaces;
39 const Cc = Components.classes;
40 const Cr = Components.results;
41 const Cu = Components.utils;
43 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
45 function ContentPrefService() {
46 // If this throws an exception, it causes the getService call to fail,
47 // but the next time a consumer tries to retrieve the service, we'll try
48 // to initialize the database again, which might work if the failure
49 // was due to a temporary condition (like being out of disk space).
52 // Observe shutdown so we can shut down the database connection.
53 this._observerSvc.addObserver(this, "xpcom-shutdown", false);
56 ContentPrefService.prototype = {
57 //**************************************************************************//
60 classDescription: "Content Pref Service",
61 classID: Components.ID("{e6a3f533-4ffa-4615-8eb4-d4e72d883fa7}"),
62 contractID: "@mozilla.org/content-pref/service;1",
63 QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPrefService]),
66 //**************************************************************************//
67 // Convenience Getters
71 get _observerSvc ContentPrefService_get__observerSvc() {
72 if (!this.__observerSvc)
73 this.__observerSvc = Cc["@mozilla.org/observer-service;1"].
74 getService(Ci.nsIObserverService);
75 return this.__observerSvc;
80 get _consoleSvc ContentPrefService_get__consoleSvc() {
81 if (!this.__consoleSvc)
82 this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
83 getService(Ci.nsIConsoleService);
84 return this.__consoleSvc;
87 // Preferences Service
89 get _prefSvc ContentPrefService_get__prefSvc() {
91 this.__prefSvc = Cc["@mozilla.org/preferences-service;1"].
92 getService(Ci.nsIPrefBranch2);
93 return this.__prefSvc;
97 //**************************************************************************//
100 _destroy: function ContentPrefService__destroy() {
101 this._observerSvc.removeObserver(this, "xpcom-shutdown");
103 // Delete references to XPCOM components to make sure we don't leak them
104 // (although we haven't observed leakage in tests). Also delete references
105 // in _observers and _genericObservers to avoid cycles with those that
106 // refer to us and don't remove themselves from those observer pools.
107 for (var i in this) {
108 try { this[i] = null }
109 // Ignore "setting a property that has only a getter" exceptions.
115 //**************************************************************************//
118 observe: function ContentPrefService_observe(subject, topic, data) {
120 case "xpcom-shutdown":
127 //**************************************************************************//
128 // nsIContentPrefService
130 getPref: function ContentPrefService_getPref(aURI, aName) {
132 var group = this.grouper.group(aURI);
133 return this._selectPref(group, aName);
136 return this._selectGlobalPref(aName);
139 setPref: function ContentPrefService_setPref(aURI, aName, aValue) {
140 // If the pref is already set to the value, there's nothing more to do.
141 var currentValue = this.getPref(aURI, aName);
142 if (typeof currentValue != "undefined") {
143 if (currentValue == aValue)
147 // If we are in private browsing mode, refuse to set new prefs
148 var inPrivateBrowsing = false;
149 try { // The Private Browsing service might not be available.
150 var pbs = Cc["@mozilla.org/privatebrowsing;1"].
151 getService(Ci.nsIPrivateBrowsingService);
152 inPrivateBrowsing = pbs.privateBrowsingEnabled;
154 if (inPrivateBrowsing)
158 var settingID = this._selectSettingID(aName) || this._insertSetting(aName);
159 var group, groupID, prefID;
161 group = this.grouper.group(aURI);
162 groupID = this._selectGroupID(group) || this._insertGroup(group);
163 prefID = this._selectPrefID(groupID, settingID);
168 prefID = this._selectGlobalPrefID(settingID);
171 // Update the existing record, if any, or create a new one.
173 this._updatePref(prefID, aValue);
175 this._insertPref(groupID, settingID, aValue);
177 for each (var observer in this._getObservers(aName)) {
179 observer.onContentPrefSet(group, aName, aValue);
187 hasPref: function ContentPrefService_hasPref(aURI, aName) {
188 // XXX If consumers end up calling this method regularly, then we should
189 // optimize this to query the database directly.
190 return (typeof this.getPref(aURI, aName) != "undefined");
193 removePref: function ContentPrefService_removePref(aURI, aName) {
194 // If there's no old value, then there's nothing to remove.
195 if (!this.hasPref(aURI, aName))
198 var settingID = this._selectSettingID(aName);
199 var group, groupID, prefID;
201 group = this.grouper.group(aURI);
202 groupID = this._selectGroupID(group);
203 prefID = this._selectPrefID(groupID, settingID);
208 prefID = this._selectGlobalPrefID(settingID);
211 this._deletePref(prefID);
213 // Get rid of extraneous records that are no longer being used.
214 this._deleteSettingIfUnused(settingID);
216 this._deleteGroupIfUnused(groupID);
218 for each (var observer in this._getObservers(aName)) {
220 observer.onContentPrefRemoved(group, aName);
228 getPrefs: function ContentPrefService_getPrefs(aURI) {
230 var group = this.grouper.group(aURI);
231 return this._selectPrefs(group);
234 return this._selectGlobalPrefs();
237 // A hash of arrays of observers, indexed by setting name.
240 // An array of generic observers, which observe all settings.
241 _genericObservers: [],
243 addObserver: function ContentPrefService_addObserver(aName, aObserver) {
246 if (!this._observers[aName])
247 this._observers[aName] = [];
248 observers = this._observers[aName];
251 observers = this._genericObservers;
253 if (observers.indexOf(aObserver) == -1)
254 observers.push(aObserver);
257 removeObserver: function ContentPrefService_removeObserver(aName, aObserver) {
260 if (!this._observers[aName])
262 observers = this._observers[aName];
265 observers = this._genericObservers;
267 if (observers.indexOf(aObserver) != -1)
268 observers.splice(observers.indexOf(aObserver), 1);
272 * Construct a list of observers to notify about a change to some setting,
273 * putting setting-specific observers before before generic ones, so observers
274 * that initialize individual settings (like the page style controller)
275 * execute before observers that display multiple settings and depend on them
276 * being initialized first (like the content prefs sidebar).
278 _getObservers: function ContentPrefService__getObservers(aName) {
281 if (aName && this._observers[aName])
282 observers = observers.concat(this._observers[aName]);
283 observers = observers.concat(this._genericObservers);
289 get grouper ContentPrefService_get_grouper() {
291 this._grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"].
292 getService(Ci.nsIContentURIGrouper);
293 return this._grouper;
296 get DBConnection ContentPrefService_get_DBConnection() {
297 return this._dbConnection;
301 //**************************************************************************//
302 // Data Retrieval & Modification
304 __stmtSelectPref: null,
305 get _stmtSelectPref ContentPrefService_get__stmtSelectPref() {
306 if (!this.__stmtSelectPref)
307 this.__stmtSelectPref = this._dbCreateStatement(
308 "SELECT prefs.value AS value " +
310 "JOIN groups ON prefs.groupID = groups.id " +
311 "JOIN settings ON prefs.settingID = settings.id " +
312 "WHERE groups.name = :group " +
313 "AND settings.name = :setting"
316 return this.__stmtSelectPref;
319 _selectPref: function ContentPrefService__selectPref(aGroup, aSetting) {
323 this._stmtSelectPref.params.group = aGroup;
324 this._stmtSelectPref.params.setting = aSetting;
326 if (this._stmtSelectPref.step())
327 value = this._stmtSelectPref.row["value"];
330 this._stmtSelectPref.reset();
336 __stmtSelectGlobalPref: null,
337 get _stmtSelectGlobalPref ContentPrefService_get__stmtSelectGlobalPref() {
338 if (!this.__stmtSelectGlobalPref)
339 this.__stmtSelectGlobalPref = this._dbCreateStatement(
340 "SELECT prefs.value AS value " +
342 "JOIN settings ON prefs.settingID = settings.id " +
343 "WHERE prefs.groupID IS NULL " +
344 "AND settings.name = :name"
347 return this.__stmtSelectGlobalPref;
350 _selectGlobalPref: function ContentPrefService__selectGlobalPref(aName) {
354 this._stmtSelectGlobalPref.params.name = aName;
356 if (this._stmtSelectGlobalPref.step())
357 value = this._stmtSelectGlobalPref.row["value"];
360 this._stmtSelectGlobalPref.reset();
366 __stmtSelectGroupID: null,
367 get _stmtSelectGroupID ContentPrefService_get__stmtSelectGroupID() {
368 if (!this.__stmtSelectGroupID)
369 this.__stmtSelectGroupID = this._dbCreateStatement(
370 "SELECT groups.id AS id " +
372 "WHERE groups.name = :name "
375 return this.__stmtSelectGroupID;
378 _selectGroupID: function ContentPrefService__selectGroupID(aName) {
382 this._stmtSelectGroupID.params.name = aName;
384 if (this._stmtSelectGroupID.step())
385 id = this._stmtSelectGroupID.row["id"];
388 this._stmtSelectGroupID.reset();
394 __stmtInsertGroup: null,
395 get _stmtInsertGroup ContentPrefService_get__stmtInsertGroup() {
396 if (!this.__stmtInsertGroup)
397 this.__stmtInsertGroup = this._dbCreateStatement(
398 "INSERT INTO groups (name) VALUES (:name)"
401 return this.__stmtInsertGroup;
404 _insertGroup: function ContentPrefService__insertGroup(aName) {
405 this._stmtInsertGroup.params.name = aName;
406 this._stmtInsertGroup.execute();
407 return this._dbConnection.lastInsertRowID;
410 __stmtSelectSettingID: null,
411 get _stmtSelectSettingID ContentPrefService_get__stmtSelectSettingID() {
412 if (!this.__stmtSelectSettingID)
413 this.__stmtSelectSettingID = this._dbCreateStatement(
414 "SELECT id FROM settings WHERE name = :name"
417 return this.__stmtSelectSettingID;
420 _selectSettingID: function ContentPrefService__selectSettingID(aName) {
424 this._stmtSelectSettingID.params.name = aName;
426 if (this._stmtSelectSettingID.step())
427 id = this._stmtSelectSettingID.row["id"];
430 this._stmtSelectSettingID.reset();
436 __stmtInsertSetting: null,
437 get _stmtInsertSetting ContentPrefService_get__stmtInsertSetting() {
438 if (!this.__stmtInsertSetting)
439 this.__stmtInsertSetting = this._dbCreateStatement(
440 "INSERT INTO settings (name) VALUES (:name)"
443 return this.__stmtInsertSetting;
446 _insertSetting: function ContentPrefService__insertSetting(aName) {
447 this._stmtInsertSetting.params.name = aName;
448 this._stmtInsertSetting.execute();
449 return this._dbConnection.lastInsertRowID;
452 __stmtSelectPrefID: null,
453 get _stmtSelectPrefID ContentPrefService_get__stmtSelectPrefID() {
454 if (!this.__stmtSelectPrefID)
455 this.__stmtSelectPrefID = this._dbCreateStatement(
456 "SELECT id FROM prefs WHERE groupID = :groupID AND settingID = :settingID"
459 return this.__stmtSelectPrefID;
462 _selectPrefID: function ContentPrefService__selectPrefID(aGroupID, aSettingID) {
466 this._stmtSelectPrefID.params.groupID = aGroupID;
467 this._stmtSelectPrefID.params.settingID = aSettingID;
469 if (this._stmtSelectPrefID.step())
470 id = this._stmtSelectPrefID.row["id"];
473 this._stmtSelectPrefID.reset();
479 __stmtSelectGlobalPrefID: null,
480 get _stmtSelectGlobalPrefID ContentPrefService_get__stmtSelectGlobalPrefID() {
481 if (!this.__stmtSelectGlobalPrefID)
482 this.__stmtSelectGlobalPrefID = this._dbCreateStatement(
483 "SELECT id FROM prefs WHERE groupID IS NULL AND settingID = :settingID"
486 return this.__stmtSelectGlobalPrefID;
489 _selectGlobalPrefID: function ContentPrefService__selectGlobalPrefID(aSettingID) {
493 this._stmtSelectGlobalPrefID.params.settingID = aSettingID;
495 if (this._stmtSelectGlobalPrefID.step())
496 id = this._stmtSelectGlobalPrefID.row["id"];
499 this._stmtSelectGlobalPrefID.reset();
505 __stmtInsertPref: null,
506 get _stmtInsertPref ContentPrefService_get__stmtInsertPref() {
507 if (!this.__stmtInsertPref)
508 this.__stmtInsertPref = this._dbCreateStatement(
509 "INSERT INTO prefs (groupID, settingID, value) " +
510 "VALUES (:groupID, :settingID, :value)"
513 return this.__stmtInsertPref;
516 _insertPref: function ContentPrefService__insertPref(aGroupID, aSettingID, aValue) {
517 this._stmtInsertPref.params.groupID = aGroupID;
518 this._stmtInsertPref.params.settingID = aSettingID;
519 this._stmtInsertPref.params.value = aValue;
520 this._stmtInsertPref.execute();
521 return this._dbConnection.lastInsertRowID;
524 __stmtUpdatePref: null,
525 get _stmtUpdatePref ContentPrefService_get__stmtUpdatePref() {
526 if (!this.__stmtUpdatePref)
527 this.__stmtUpdatePref = this._dbCreateStatement(
528 "UPDATE prefs SET value = :value WHERE id = :id"
531 return this.__stmtUpdatePref;
534 _updatePref: function ContentPrefService__updatePref(aPrefID, aValue) {
535 this._stmtUpdatePref.params.id = aPrefID;
536 this._stmtUpdatePref.params.value = aValue;
537 this._stmtUpdatePref.execute();
540 __stmtDeletePref: null,
541 get _stmtDeletePref ContentPrefService_get__stmtDeletePref() {
542 if (!this.__stmtDeletePref)
543 this.__stmtDeletePref = this._dbCreateStatement(
544 "DELETE FROM prefs WHERE id = :id"
547 return this.__stmtDeletePref;
550 _deletePref: function ContentPrefService__deletePref(aPrefID) {
551 this._stmtDeletePref.params.id = aPrefID;
552 this._stmtDeletePref.execute();
555 __stmtDeleteSettingIfUnused: null,
556 get _stmtDeleteSettingIfUnused ContentPrefService_get__stmtDeleteSettingIfUnused() {
557 if (!this.__stmtDeleteSettingIfUnused)
558 this.__stmtDeleteSettingIfUnused = this._dbCreateStatement(
559 "DELETE FROM settings WHERE id = :id " +
560 "AND id NOT IN (SELECT DISTINCT settingID FROM prefs)"
563 return this.__stmtDeleteSettingIfUnused;
566 _deleteSettingIfUnused: function ContentPrefService__deleteSettingIfUnused(aSettingID) {
567 this._stmtDeleteSettingIfUnused.params.id = aSettingID;
568 this._stmtDeleteSettingIfUnused.execute();
571 __stmtDeleteGroupIfUnused: null,
572 get _stmtDeleteGroupIfUnused ContentPrefService_get__stmtDeleteGroupIfUnused() {
573 if (!this.__stmtDeleteGroupIfUnused)
574 this.__stmtDeleteGroupIfUnused = this._dbCreateStatement(
575 "DELETE FROM groups WHERE id = :id " +
576 "AND id NOT IN (SELECT DISTINCT groupID FROM prefs)"
579 return this.__stmtDeleteGroupIfUnused;
582 _deleteGroupIfUnused: function ContentPrefService__deleteGroupIfUnused(aGroupID) {
583 this._stmtDeleteGroupIfUnused.params.id = aGroupID;
584 this._stmtDeleteGroupIfUnused.execute();
587 __stmtSelectPrefs: null,
588 get _stmtSelectPrefs ContentPrefService_get__stmtSelectPrefs() {
589 if (!this.__stmtSelectPrefs)
590 this.__stmtSelectPrefs = this._dbCreateStatement(
591 "SELECT settings.name AS name, prefs.value AS value " +
593 "JOIN groups ON prefs.groupID = groups.id " +
594 "JOIN settings ON prefs.settingID = settings.id " +
595 "WHERE groups.name = :group "
598 return this.__stmtSelectPrefs;
601 _selectPrefs: function ContentPrefService__selectPrefs(aGroup) {
602 var prefs = Cc["@mozilla.org/hash-property-bag;1"].
603 createInstance(Ci.nsIWritablePropertyBag);
606 this._stmtSelectPrefs.params.group = aGroup;
608 while (this._stmtSelectPrefs.step())
609 prefs.setProperty(this._stmtSelectPrefs.row["name"],
610 this._stmtSelectPrefs.row["value"]);
613 this._stmtSelectPrefs.reset();
619 __stmtSelectGlobalPrefs: null,
620 get _stmtSelectGlobalPrefs ContentPrefService_get__stmtSelectGlobalPrefs() {
621 if (!this.__stmtSelectGlobalPrefs)
622 this.__stmtSelectGlobalPrefs = this._dbCreateStatement(
623 "SELECT settings.name AS name, prefs.value AS value " +
625 "JOIN settings ON prefs.settingID = settings.id " +
626 "WHERE prefs.groupID IS NULL"
629 return this.__stmtSelectGlobalPrefs;
632 _selectGlobalPrefs: function ContentPrefService__selectGlobalPrefs() {
633 var prefs = Cc["@mozilla.org/hash-property-bag;1"].
634 createInstance(Ci.nsIWritablePropertyBag);
637 while (this._stmtSelectGlobalPrefs.step())
638 prefs.setProperty(this._stmtSelectGlobalPrefs.row["name"],
639 this._stmtSelectGlobalPrefs.row["value"]);
642 this._stmtSelectGlobalPrefs.reset();
649 //**************************************************************************//
650 // Database Creation & Access
656 groups: "id INTEGER PRIMARY KEY, \
659 settings: "id INTEGER PRIMARY KEY, \
662 prefs: "id INTEGER PRIMARY KEY, \
663 groupID INTEGER REFERENCES groups(id), \
664 settingID INTEGER NOT NULL REFERENCES settings(id), \
678 columns: ["groupID", "settingID"]
685 _dbCreateStatement: function ContentPrefService__dbCreateStatement(aSQLString) {
687 var statement = this._dbConnection.createStatement(aSQLString);
690 Cu.reportError("error creating statement " + aSQLString + ": " +
691 this._dbConnection.lastError + " - " +
692 this._dbConnection.lastErrorString);
696 var wrappedStatement = Cc["@mozilla.org/storage/statement-wrapper;1"].
697 createInstance(Ci.mozIStorageStatementWrapper);
698 wrappedStatement.initialize(statement);
699 return wrappedStatement;
702 // _dbInit and the methods it calls (_dbCreate, _dbMigrate, and version-
703 // specific migration methods) must be careful not to call any method
704 // of the service that assumes the database connection has already been
705 // initialized, since it won't be initialized until at the end of _dbInit.
707 _dbInit: function ContentPrefService__dbInit() {
708 var dirService = Cc["@mozilla.org/file/directory_service;1"].
709 getService(Ci.nsIProperties);
710 var dbFile = dirService.get("ProfD", Ci.nsIFile);
711 dbFile.append("content-prefs.sqlite");
713 var dbService = Cc["@mozilla.org/storage/service;1"].
714 getService(Ci.mozIStorageService);
718 if (!dbFile.exists())
719 dbConnection = this._dbCreate(dbService, dbFile);
722 dbConnection = dbService.openDatabase(dbFile);
724 // If the connection isn't ready after we open the database, that means
725 // the database has been corrupted, so we back it up and then recreate it.
726 catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
727 dbConnection = this._dbBackUpAndRecreate(dbService, dbFile,
731 // Get the version of the schema in the file.
732 var version = dbConnection.schemaVersion;
734 // Try to migrate the schema in the database to the current schema used by
735 // the service. If migration fails, back up the database and recreate it.
736 if (version != this._dbVersion) {
738 this._dbMigrate(dbConnection, version, this._dbVersion);
741 Cu.reportError("error migrating DB: " + ex + "; backing up and recreating");
742 dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, dbConnection);
747 // Turn off disk synchronization checking to reduce disk churn and speed up
748 // operations when prefs are changed rapidly (such as when a user repeatedly
749 // changes the value of the browser zoom setting for a site).
751 // Note: this could cause database corruption if the OS crashes or machine
752 // loses power before the data gets written to disk, but this is considered
753 // a reasonable risk for the not-so-critical data stored in this database.
755 // If you really don't want to take this risk, however, just set the
756 // toolkit.storage.synchronous pref to 1 (NORMAL synchronization) or 2
757 // (FULL synchronization), in which case mozStorageConnection::Initialize
758 // will use that value, and we won't override it here.
759 if (!this._prefSvc.prefHasUserValue("toolkit.storage.synchronous"))
760 dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF");
762 this._dbConnection = dbConnection;
765 _dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) {
766 var dbConnection = aDBService.openDatabase(aDBFile);
769 this._dbCreateSchema(dbConnection);
770 dbConnection.schemaVersion = this._dbVersion;
773 // If we failed to create the database (perhaps because the disk ran out
774 // of space), then remove the database file so we don't leave it in some
775 // half-created state from which we won't know how to recover.
776 dbConnection.close();
777 aDBFile.remove(false);
784 _dbCreateSchema: function ContentPrefService__dbCreateSchema(aDBConnection) {
785 this._dbCreateTables(aDBConnection);
786 this._dbCreateIndices(aDBConnection);
789 _dbCreateTables: function ContentPrefService__dbCreateTables(aDBConnection) {
790 for (let name in this._dbSchema.tables)
791 aDBConnection.createTable(name, this._dbSchema.tables[name]);
794 _dbCreateIndices: function ContentPrefService__dbCreateIndices(aDBConnection) {
795 for (let name in this._dbSchema.indices) {
796 let index = this._dbSchema.indices[name];
797 let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table +
798 "(" + index.columns.join(", ") + ")";
799 aDBConnection.executeSimpleSQL(statement);
803 _dbBackUpAndRecreate: function ContentPrefService__dbBackUpAndRecreate(aDBService,
806 aDBService.backupDatabaseFile(aDBFile, "content-prefs.sqlite.corrupt");
808 // Close the database, ignoring the "already closed" exception, if any.
809 // It'll be open if we're here because of a migration failure but closed
810 // if we're here because of database corruption.
811 try { aDBConnection.close() } catch(ex) {}
813 aDBFile.remove(false);
815 let dbConnection = this._dbCreate(aDBService, aDBFile);
820 _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
821 if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
822 aDBConnection.beginTransaction();
824 this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
825 aDBConnection.schemaVersion = aNewVersion;
826 aDBConnection.commitTransaction();
829 aDBConnection.rollbackTransaction();
834 throw("no migrator function from version " + aOldVersion +
835 " to version " + aNewVersion);
839 * If the schema version is 0, that means it was never set, which means
840 * the database was somehow created without the schema being applied, perhaps
841 * because the system ran out of disk space (although we check for this
842 * in _createDB) or because some other code created the database file without
843 * applying the schema. In any case, recover by simply reapplying the schema.
845 _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) {
846 this._dbCreateSchema(aDBConnection);
849 _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) {
850 aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
851 aDBConnection.createTable("groups", this._dbSchema.tables.groups);
852 aDBConnection.executeSimpleSQL(
853 "INSERT INTO groups (id, name) " +
854 "SELECT id, name FROM groupsOld"
857 aDBConnection.executeSimpleSQL("DROP TABLE groupers");
858 aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
860 this._dbCreateIndices(aDBConnection);
863 _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) {
864 this._dbCreateIndices(aDBConnection);
870 function HostnameGrouper() {}
872 HostnameGrouper.prototype = {
873 //**************************************************************************//
876 classDescription: "Hostname Grouper",
877 classID: Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"),
878 contractID: "@mozilla.org/content-pref/hostname-grouper;1",
879 QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]),
882 //**************************************************************************//
883 // nsIContentURIGrouper
885 group: function HostnameGrouper_group(aURI) {
889 // Accessing the host property of the URI will throw an exception
890 // if the URI is of a type that doesn't have a host property.
891 // Otherwise, we manually throw an exception if the host is empty,
892 // since the effect is the same (we can't derive a group from it).
896 throw("can't derive group from host; no host in URI");
899 // If we don't have a host, then use the entire URI (minus the query,
900 // reference, and hash, if possible) as the group. This means that URIs
901 // like about:mozilla and about:blank will be considered separate groups,
902 // but at least they'll be grouped somehow.
904 // This also means that each individual file: URL will be considered
905 // its own group. This seems suboptimal, but so does treating the entire
906 // file: URL space as a single group (especially if folks start setting
907 // group-specific capabilities prefs).
909 // XXX Is there something better we can do here?
912 var url = aURI.QueryInterface(Ci.nsIURL);
913 group = aURI.prePath + url.filePath;
925 //****************************************************************************//
928 var components = [ContentPrefService, HostnameGrouper];
929 var NSGetModule = function ContentPrefService_NSGetModule(compMgr, fileSpec) {
930 return XPCOMUtils.generateModule(components);