1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/contentprefs/nsContentPrefService.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1331 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const Ci = Components.interfaces; 1.9 +const Cc = Components.classes; 1.10 +const Cr = Components.results; 1.11 +const Cu = Components.utils; 1.12 + 1.13 +const CACHE_MAX_GROUP_ENTRIES = 100; 1.14 + 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 + 1.17 +/** 1.18 + * Remotes the service. All the remoting/electrolysis code is in here, 1.19 + * so the regular service code below remains uncluttered and maintainable. 1.20 + */ 1.21 +function electrolify(service) { 1.22 + // FIXME: For now, use the wrappedJSObject hack, until bug 1.23 + // 593407 which will clean that up. 1.24 + // Note that we also use this in the xpcshell tests, separately. 1.25 + service.wrappedJSObject = service; 1.26 + 1.27 + var appInfo = Cc["@mozilla.org/xre/app-info;1"]; 1.28 + if (appInfo && appInfo.getService(Ci.nsIXULRuntime).processType != 1.29 + Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) 1.30 + { 1.31 + // Child process 1.32 + service._dbInit = function(){}; // No local DB 1.33 + } 1.34 +} 1.35 + 1.36 +function ContentPrefService() { 1.37 + electrolify(this); 1.38 + 1.39 + // If this throws an exception, it causes the getService call to fail, 1.40 + // but the next time a consumer tries to retrieve the service, we'll try 1.41 + // to initialize the database again, which might work if the failure 1.42 + // was due to a temporary condition (like being out of disk space). 1.43 + this._dbInit(); 1.44 + 1.45 + this._observerSvc.addObserver(this, "last-pb-context-exited", false); 1.46 + 1.47 + // Observe shutdown so we can shut down the database connection. 1.48 + this._observerSvc.addObserver(this, "xpcom-shutdown", false); 1.49 +} 1.50 + 1.51 +Cu.import("resource://gre/modules/ContentPrefStore.jsm"); 1.52 +const cache = new ContentPrefStore(); 1.53 +cache.set = function CPS_cache_set(group, name, val) { 1.54 + Object.getPrototypeOf(this).set.apply(this, arguments); 1.55 + let groupCount = Object.keys(this._groups).length; 1.56 + if (groupCount >= CACHE_MAX_GROUP_ENTRIES) { 1.57 + // Clean half of the entries 1.58 + for (let [group, name, ] in this) { 1.59 + this.remove(group, name); 1.60 + groupCount--; 1.61 + if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2) 1.62 + break; 1.63 + } 1.64 + } 1.65 +}; 1.66 + 1.67 +const privModeStorage = new ContentPrefStore(); 1.68 + 1.69 +ContentPrefService.prototype = { 1.70 + //**************************************************************************// 1.71 + // XPCOM Plumbing 1.72 + 1.73 + classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"), 1.74 + 1.75 + QueryInterface: function CPS_QueryInterface(iid) { 1.76 + let supportedIIDs = [ 1.77 + Ci.nsIContentPrefService, 1.78 + Ci.nsIFrameMessageListener, 1.79 + Ci.nsISupports, 1.80 + ]; 1.81 + if (supportedIIDs.some(function (i) iid.equals(i))) 1.82 + return this; 1.83 + if (iid.equals(Ci.nsIContentPrefService2)) { 1.84 + if (!this._contentPrefService2) { 1.85 + let s = {}; 1.86 + Cu.import("resource://gre/modules/ContentPrefService2.jsm", s); 1.87 + this._contentPrefService2 = new s.ContentPrefService2(this); 1.88 + } 1.89 + return this._contentPrefService2; 1.90 + } 1.91 + throw Cr.NS_ERROR_NO_INTERFACE; 1.92 + }, 1.93 + 1.94 + //**************************************************************************// 1.95 + // Convenience Getters 1.96 + 1.97 + // Observer Service 1.98 + __observerSvc: null, 1.99 + get _observerSvc() { 1.100 + if (!this.__observerSvc) 1.101 + this.__observerSvc = Cc["@mozilla.org/observer-service;1"]. 1.102 + getService(Ci.nsIObserverService); 1.103 + return this.__observerSvc; 1.104 + }, 1.105 + 1.106 + // Console Service 1.107 + __consoleSvc: null, 1.108 + get _consoleSvc() { 1.109 + if (!this.__consoleSvc) 1.110 + this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"]. 1.111 + getService(Ci.nsIConsoleService); 1.112 + return this.__consoleSvc; 1.113 + }, 1.114 + 1.115 + // Preferences Service 1.116 + __prefSvc: null, 1.117 + get _prefSvc() { 1.118 + if (!this.__prefSvc) 1.119 + this.__prefSvc = Cc["@mozilla.org/preferences-service;1"]. 1.120 + getService(Ci.nsIPrefBranch); 1.121 + return this.__prefSvc; 1.122 + }, 1.123 + 1.124 + 1.125 + //**************************************************************************// 1.126 + // Destruction 1.127 + 1.128 + _destroy: function ContentPrefService__destroy() { 1.129 + this._observerSvc.removeObserver(this, "xpcom-shutdown"); 1.130 + this._observerSvc.removeObserver(this, "last-pb-context-exited"); 1.131 + 1.132 + // Finalize statements which may have been used asynchronously. 1.133 + // FIXME(696499): put them in an object cache like other components. 1.134 + if (this.__stmtSelectPrefID) { 1.135 + this.__stmtSelectPrefID.finalize(); 1.136 + this.__stmtSelectPrefID = null; 1.137 + } 1.138 + if (this.__stmtSelectGlobalPrefID) { 1.139 + this.__stmtSelectGlobalPrefID.finalize(); 1.140 + this.__stmtSelectGlobalPrefID = null; 1.141 + } 1.142 + if (this.__stmtInsertPref) { 1.143 + this.__stmtInsertPref.finalize(); 1.144 + this.__stmtInsertPref = null; 1.145 + } 1.146 + if (this.__stmtInsertGroup) { 1.147 + this.__stmtInsertGroup.finalize(); 1.148 + this.__stmtInsertGroup = null; 1.149 + } 1.150 + if (this.__stmtInsertSetting) { 1.151 + this.__stmtInsertSetting.finalize(); 1.152 + this.__stmtInsertSetting = null; 1.153 + } 1.154 + if (this.__stmtSelectGroupID) { 1.155 + this.__stmtSelectGroupID.finalize(); 1.156 + this.__stmtSelectGroupID = null; 1.157 + } 1.158 + if (this.__stmtSelectSettingID) { 1.159 + this.__stmtSelectSettingID.finalize(); 1.160 + this.__stmtSelectSettingID = null; 1.161 + } 1.162 + if (this.__stmtSelectPref) { 1.163 + this.__stmtSelectPref.finalize(); 1.164 + this.__stmtSelectPref = null; 1.165 + } 1.166 + if (this.__stmtSelectGlobalPref) { 1.167 + this.__stmtSelectGlobalPref.finalize(); 1.168 + this.__stmtSelectGlobalPref = null; 1.169 + } 1.170 + if (this.__stmtSelectPrefsByName) { 1.171 + this.__stmtSelectPrefsByName.finalize(); 1.172 + this.__stmtSelectPrefsByName = null; 1.173 + } 1.174 + if (this.__stmtDeleteSettingIfUnused) { 1.175 + this.__stmtDeleteSettingIfUnused.finalize(); 1.176 + this.__stmtDeleteSettingIfUnused = null; 1.177 + } 1.178 + if(this.__stmtSelectPrefs) { 1.179 + this.__stmtSelectPrefs.finalize(); 1.180 + this.__stmtSelectPrefs = null; 1.181 + } 1.182 + if(this.__stmtDeleteGroupIfUnused) { 1.183 + this.__stmtDeleteGroupIfUnused.finalize(); 1.184 + this.__stmtDeleteGroupIfUnused = null; 1.185 + } 1.186 + if (this.__stmtDeletePref) { 1.187 + this.__stmtDeletePref.finalize(); 1.188 + this.__stmtDeletePref = null; 1.189 + } 1.190 + if (this.__stmtUpdatePref) { 1.191 + this.__stmtUpdatePref.finalize(); 1.192 + this.__stmtUpdatePref = null; 1.193 + } 1.194 + 1.195 + if (this._contentPrefService2) 1.196 + this._contentPrefService2.destroy(); 1.197 + 1.198 + this._dbConnection.asyncClose(); 1.199 + 1.200 + // Delete references to XPCOM components to make sure we don't leak them 1.201 + // (although we haven't observed leakage in tests). Also delete references 1.202 + // in _observers and _genericObservers to avoid cycles with those that 1.203 + // refer to us and don't remove themselves from those observer pools. 1.204 + for (var i in this) { 1.205 + try { this[i] = null } 1.206 + // Ignore "setting a property that has only a getter" exceptions. 1.207 + catch(ex) {} 1.208 + } 1.209 + }, 1.210 + 1.211 + 1.212 + //**************************************************************************// 1.213 + // nsIObserver 1.214 + 1.215 + observe: function ContentPrefService_observe(subject, topic, data) { 1.216 + switch (topic) { 1.217 + case "xpcom-shutdown": 1.218 + this._destroy(); 1.219 + break; 1.220 + case "last-pb-context-exited": 1.221 + this._privModeStorage.removeAll(); 1.222 + break; 1.223 + } 1.224 + }, 1.225 + 1.226 + 1.227 + //**************************************************************************// 1.228 + // in-memory cache and private-browsing stores 1.229 + 1.230 + _cache: cache, 1.231 + _privModeStorage: privModeStorage, 1.232 + 1.233 + //**************************************************************************// 1.234 + // nsIContentPrefService 1.235 + 1.236 + getPref: function ContentPrefService_getPref(aGroup, aName, aContext, aCallback) { 1.237 + warnDeprecated(); 1.238 + 1.239 + if (!aName) 1.240 + throw Components.Exception("aName cannot be null or an empty string", 1.241 + Cr.NS_ERROR_ILLEGAL_VALUE); 1.242 + 1.243 + var group = this._parseGroupParam(aGroup); 1.244 + 1.245 + if (aContext && aContext.usePrivateBrowsing) { 1.246 + if (this._privModeStorage.has(group, aName)) { 1.247 + let value = this._privModeStorage.get(group, aName); 1.248 + if (aCallback) { 1.249 + this._scheduleCallback(function(){aCallback.onResult(value);}); 1.250 + return; 1.251 + } 1.252 + return value; 1.253 + } 1.254 + // if we don't have a pref specific to this private mode browsing 1.255 + // session, to try to get one from normal mode 1.256 + } 1.257 + 1.258 + if (group == null) 1.259 + return this._selectGlobalPref(aName, aCallback); 1.260 + return this._selectPref(group, aName, aCallback); 1.261 + }, 1.262 + 1.263 + setPref: function ContentPrefService_setPref(aGroup, aName, aValue, aContext) { 1.264 + warnDeprecated(); 1.265 + 1.266 + // If the pref is already set to the value, there's nothing more to do. 1.267 + var currentValue = this.getPref(aGroup, aName, aContext); 1.268 + if (typeof currentValue != "undefined") { 1.269 + if (currentValue == aValue) 1.270 + return; 1.271 + } 1.272 + 1.273 + var group = this._parseGroupParam(aGroup); 1.274 + 1.275 + if (aContext && aContext.usePrivateBrowsing) { 1.276 + this._privModeStorage.setWithCast(group, aName, aValue); 1.277 + this._notifyPrefSet(group, aName, aValue); 1.278 + return; 1.279 + } 1.280 + 1.281 + var settingID = this._selectSettingID(aName) || this._insertSetting(aName); 1.282 + var groupID, prefID; 1.283 + if (group == null) { 1.284 + groupID = null; 1.285 + prefID = this._selectGlobalPrefID(settingID); 1.286 + } 1.287 + else { 1.288 + groupID = this._selectGroupID(group) || this._insertGroup(group); 1.289 + prefID = this._selectPrefID(groupID, settingID); 1.290 + } 1.291 + 1.292 + // Update the existing record, if any, or create a new one. 1.293 + if (prefID) 1.294 + this._updatePref(prefID, aValue); 1.295 + else 1.296 + this._insertPref(groupID, settingID, aValue); 1.297 + 1.298 + this._cache.setWithCast(group, aName, aValue); 1.299 + this._notifyPrefSet(group, aName, aValue); 1.300 + }, 1.301 + 1.302 + hasPref: function ContentPrefService_hasPref(aGroup, aName, aContext) { 1.303 + warnDeprecated(); 1.304 + 1.305 + // XXX If consumers end up calling this method regularly, then we should 1.306 + // optimize this to query the database directly. 1.307 + return (typeof this.getPref(aGroup, aName, aContext) != "undefined"); 1.308 + }, 1.309 + 1.310 + hasCachedPref: function ContentPrefService_hasCachedPref(aGroup, aName, aContext) { 1.311 + warnDeprecated(); 1.312 + 1.313 + if (!aName) 1.314 + throw Components.Exception("aName cannot be null or an empty string", 1.315 + Cr.NS_ERROR_ILLEGAL_VALUE); 1.316 + 1.317 + let group = this._parseGroupParam(aGroup); 1.318 + let storage = aContext && aContext.usePrivateBrowsing ? this._privModeStorage: this._cache; 1.319 + return storage.has(group, aName); 1.320 + }, 1.321 + 1.322 + removePref: function ContentPrefService_removePref(aGroup, aName, aContext) { 1.323 + warnDeprecated(); 1.324 + 1.325 + // If there's no old value, then there's nothing to remove. 1.326 + if (!this.hasPref(aGroup, aName, aContext)) 1.327 + return; 1.328 + 1.329 + var group = this._parseGroupParam(aGroup); 1.330 + 1.331 + if (aContext && aContext.usePrivateBrowsing) { 1.332 + this._privModeStorage.remove(group, aName); 1.333 + this._notifyPrefRemoved(group, aName); 1.334 + return; 1.335 + } 1.336 + 1.337 + var settingID = this._selectSettingID(aName); 1.338 + var groupID, prefID; 1.339 + if (group == null) { 1.340 + groupID = null; 1.341 + prefID = this._selectGlobalPrefID(settingID); 1.342 + } 1.343 + else { 1.344 + groupID = this._selectGroupID(group); 1.345 + prefID = this._selectPrefID(groupID, settingID); 1.346 + } 1.347 + 1.348 + this._deletePref(prefID); 1.349 + 1.350 + // Get rid of extraneous records that are no longer being used. 1.351 + this._deleteSettingIfUnused(settingID); 1.352 + if (groupID) 1.353 + this._deleteGroupIfUnused(groupID); 1.354 + 1.355 + this._cache.remove(group, aName); 1.356 + this._notifyPrefRemoved(group, aName); 1.357 + }, 1.358 + 1.359 + removeGroupedPrefs: function ContentPrefService_removeGroupedPrefs(aContext) { 1.360 + warnDeprecated(); 1.361 + 1.362 + // will not delete global preferences 1.363 + if (aContext && aContext.usePrivateBrowsing) { 1.364 + // keep only global prefs 1.365 + this._privModeStorage.removeAllGroups(); 1.366 + } 1.367 + this._cache.removeAllGroups(); 1.368 + this._dbConnection.beginTransaction(); 1.369 + try { 1.370 + this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL"); 1.371 + this._dbConnection.executeSimpleSQL("DELETE FROM groups"); 1.372 + this._dbConnection.executeSimpleSQL( 1.373 + "DELETE FROM settings " + 1.374 + "WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)" 1.375 + ); 1.376 + this._dbConnection.commitTransaction(); 1.377 + } 1.378 + catch(ex) { 1.379 + this._dbConnection.rollbackTransaction(); 1.380 + throw ex; 1.381 + } 1.382 + }, 1.383 + 1.384 + removePrefsByName: function ContentPrefService_removePrefsByName(aName, aContext) { 1.385 + warnDeprecated(); 1.386 + 1.387 + if (!aName) 1.388 + throw Components.Exception("aName cannot be null or an empty string", 1.389 + Cr.NS_ERROR_ILLEGAL_VALUE); 1.390 + 1.391 + if (aContext && aContext.usePrivateBrowsing) { 1.392 + for (let [group, name, ] in this._privModeStorage) { 1.393 + if (name === aName) { 1.394 + this._privModeStorage.remove(group, aName); 1.395 + this._notifyPrefRemoved(group, aName); 1.396 + } 1.397 + } 1.398 + } 1.399 + 1.400 + var settingID = this._selectSettingID(aName); 1.401 + if (!settingID) 1.402 + return; 1.403 + 1.404 + var selectGroupsStmt = this._dbCreateStatement( 1.405 + "SELECT groups.id AS groupID, groups.name AS groupName " + 1.406 + "FROM prefs " + 1.407 + "JOIN groups ON prefs.groupID = groups.id " + 1.408 + "WHERE prefs.settingID = :setting " 1.409 + ); 1.410 + 1.411 + var groupNames = []; 1.412 + var groupIDs = []; 1.413 + try { 1.414 + selectGroupsStmt.params.setting = settingID; 1.415 + 1.416 + while (selectGroupsStmt.executeStep()) { 1.417 + groupIDs.push(selectGroupsStmt.row["groupID"]); 1.418 + groupNames.push(selectGroupsStmt.row["groupName"]); 1.419 + } 1.420 + } 1.421 + finally { 1.422 + selectGroupsStmt.reset(); 1.423 + } 1.424 + 1.425 + if (this.hasPref(null, aName)) { 1.426 + groupNames.push(null); 1.427 + } 1.428 + 1.429 + this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE settingID = " + settingID); 1.430 + this._dbConnection.executeSimpleSQL("DELETE FROM settings WHERE id = " + settingID); 1.431 + 1.432 + for (var i = 0; i < groupNames.length; i++) { 1.433 + this._cache.remove(groupNames[i], aName); 1.434 + if (groupNames[i]) // ie. not null, which will be last (and i == groupIDs.length) 1.435 + this._deleteGroupIfUnused(groupIDs[i]); 1.436 + if (!aContext || !aContext.usePrivateBrowsing) { 1.437 + this._notifyPrefRemoved(groupNames[i], aName); 1.438 + } 1.439 + } 1.440 + }, 1.441 + 1.442 + getPrefs: function ContentPrefService_getPrefs(aGroup, aContext) { 1.443 + warnDeprecated(); 1.444 + 1.445 + var group = this._parseGroupParam(aGroup); 1.446 + if (aContext && aContext.usePrivateBrowsing) { 1.447 + let prefs = Cc["@mozilla.org/hash-property-bag;1"]. 1.448 + createInstance(Ci.nsIWritablePropertyBag); 1.449 + for (let [sgroup, sname, sval] in this._privModeStorage) { 1.450 + if (sgroup === group) 1.451 + prefs.setProperty(sname, sval); 1.452 + } 1.453 + return prefs; 1.454 + } 1.455 + 1.456 + if (group == null) 1.457 + return this._selectGlobalPrefs(); 1.458 + return this._selectPrefs(group); 1.459 + }, 1.460 + 1.461 + getPrefsByName: function ContentPrefService_getPrefsByName(aName, aContext) { 1.462 + warnDeprecated(); 1.463 + 1.464 + if (!aName) 1.465 + throw Components.Exception("aName cannot be null or an empty string", 1.466 + Cr.NS_ERROR_ILLEGAL_VALUE); 1.467 + 1.468 + if (aContext && aContext.usePrivateBrowsing) { 1.469 + let prefs = Cc["@mozilla.org/hash-property-bag;1"]. 1.470 + createInstance(Ci.nsIWritablePropertyBag); 1.471 + for (let [sgroup, sname, sval] in this._privModeStorage) { 1.472 + if (sname === aName) 1.473 + prefs.setProperty(sgroup, sval); 1.474 + } 1.475 + return prefs; 1.476 + } 1.477 + 1.478 + return this._selectPrefsByName(aName); 1.479 + }, 1.480 + 1.481 + // A hash of arrays of observers, indexed by setting name. 1.482 + _observers: {}, 1.483 + 1.484 + // An array of generic observers, which observe all settings. 1.485 + _genericObservers: [], 1.486 + 1.487 + addObserver: function ContentPrefService_addObserver(aName, aObserver) { 1.488 + warnDeprecated(); 1.489 + this._addObserver.apply(this, arguments); 1.490 + }, 1.491 + 1.492 + _addObserver: function ContentPrefService__addObserver(aName, aObserver) { 1.493 + var observers; 1.494 + if (aName) { 1.495 + if (!this._observers[aName]) 1.496 + this._observers[aName] = []; 1.497 + observers = this._observers[aName]; 1.498 + } 1.499 + else 1.500 + observers = this._genericObservers; 1.501 + 1.502 + if (observers.indexOf(aObserver) == -1) 1.503 + observers.push(aObserver); 1.504 + }, 1.505 + 1.506 + removeObserver: function ContentPrefService_removeObserver(aName, aObserver) { 1.507 + warnDeprecated(); 1.508 + this._removeObserver.apply(this, arguments); 1.509 + }, 1.510 + 1.511 + _removeObserver: function ContentPrefService__removeObserver(aName, aObserver) { 1.512 + var observers; 1.513 + if (aName) { 1.514 + if (!this._observers[aName]) 1.515 + return; 1.516 + observers = this._observers[aName]; 1.517 + } 1.518 + else 1.519 + observers = this._genericObservers; 1.520 + 1.521 + if (observers.indexOf(aObserver) != -1) 1.522 + observers.splice(observers.indexOf(aObserver), 1); 1.523 + }, 1.524 + 1.525 + /** 1.526 + * Construct a list of observers to notify about a change to some setting, 1.527 + * putting setting-specific observers before before generic ones, so observers 1.528 + * that initialize individual settings (like the page style controller) 1.529 + * execute before observers that display multiple settings and depend on them 1.530 + * being initialized first (like the content prefs sidebar). 1.531 + */ 1.532 + _getObservers: function ContentPrefService__getObservers(aName) { 1.533 + var observers = []; 1.534 + 1.535 + if (aName && this._observers[aName]) 1.536 + observers = observers.concat(this._observers[aName]); 1.537 + observers = observers.concat(this._genericObservers); 1.538 + 1.539 + return observers; 1.540 + }, 1.541 + 1.542 + /** 1.543 + * Notify all observers about the removal of a preference. 1.544 + */ 1.545 + _notifyPrefRemoved: function ContentPrefService__notifyPrefRemoved(aGroup, aName) { 1.546 + for each (var observer in this._getObservers(aName)) { 1.547 + try { 1.548 + observer.onContentPrefRemoved(aGroup, aName); 1.549 + } 1.550 + catch(ex) { 1.551 + Cu.reportError(ex); 1.552 + } 1.553 + } 1.554 + }, 1.555 + 1.556 + /** 1.557 + * Notify all observers about a preference change. 1.558 + */ 1.559 + _notifyPrefSet: function ContentPrefService__notifyPrefSet(aGroup, aName, aValue) { 1.560 + for each (var observer in this._getObservers(aName)) { 1.561 + try { 1.562 + observer.onContentPrefSet(aGroup, aName, aValue); 1.563 + } 1.564 + catch(ex) { 1.565 + Cu.reportError(ex); 1.566 + } 1.567 + } 1.568 + }, 1.569 + 1.570 + get grouper() { 1.571 + warnDeprecated(); 1.572 + return this._grouper; 1.573 + }, 1.574 + __grouper: null, 1.575 + get _grouper() { 1.576 + if (!this.__grouper) 1.577 + this.__grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"]. 1.578 + getService(Ci.nsIContentURIGrouper); 1.579 + return this.__grouper; 1.580 + }, 1.581 + 1.582 + get DBConnection() { 1.583 + warnDeprecated(); 1.584 + return this._dbConnection; 1.585 + }, 1.586 + 1.587 + 1.588 + //**************************************************************************// 1.589 + // Data Retrieval & Modification 1.590 + 1.591 + __stmtSelectPref: null, 1.592 + get _stmtSelectPref() { 1.593 + if (!this.__stmtSelectPref) 1.594 + this.__stmtSelectPref = this._dbCreateStatement( 1.595 + "SELECT prefs.value AS value " + 1.596 + "FROM prefs " + 1.597 + "JOIN groups ON prefs.groupID = groups.id " + 1.598 + "JOIN settings ON prefs.settingID = settings.id " + 1.599 + "WHERE groups.name = :group " + 1.600 + "AND settings.name = :setting" 1.601 + ); 1.602 + 1.603 + return this.__stmtSelectPref; 1.604 + }, 1.605 + 1.606 + _scheduleCallback: function(func) { 1.607 + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); 1.608 + tm.mainThread.dispatch(func, Ci.nsIThread.DISPATCH_NORMAL); 1.609 + }, 1.610 + 1.611 + _selectPref: function ContentPrefService__selectPref(aGroup, aSetting, aCallback) { 1.612 + let value = undefined; 1.613 + if (this._cache.has(aGroup, aSetting)) { 1.614 + value = this._cache.get(aGroup, aSetting); 1.615 + if (aCallback) { 1.616 + this._scheduleCallback(function(){aCallback.onResult(value);}); 1.617 + return; 1.618 + } 1.619 + return value; 1.620 + } 1.621 + 1.622 + try { 1.623 + this._stmtSelectPref.params.group = aGroup; 1.624 + this._stmtSelectPref.params.setting = aSetting; 1.625 + 1.626 + if (aCallback) { 1.627 + let cache = this._cache; 1.628 + new AsyncStatement(this._stmtSelectPref).execute({onResult: function(aResult) { 1.629 + cache.set(aGroup, aSetting, aResult); 1.630 + aCallback.onResult(aResult); 1.631 + }}); 1.632 + } 1.633 + else { 1.634 + if (this._stmtSelectPref.executeStep()) { 1.635 + value = this._stmtSelectPref.row["value"]; 1.636 + } 1.637 + this._cache.set(aGroup, aSetting, value); 1.638 + } 1.639 + } 1.640 + finally { 1.641 + this._stmtSelectPref.reset(); 1.642 + } 1.643 + 1.644 + return value; 1.645 + }, 1.646 + 1.647 + __stmtSelectGlobalPref: null, 1.648 + get _stmtSelectGlobalPref() { 1.649 + if (!this.__stmtSelectGlobalPref) 1.650 + this.__stmtSelectGlobalPref = this._dbCreateStatement( 1.651 + "SELECT prefs.value AS value " + 1.652 + "FROM prefs " + 1.653 + "JOIN settings ON prefs.settingID = settings.id " + 1.654 + "WHERE prefs.groupID IS NULL " + 1.655 + "AND settings.name = :name" 1.656 + ); 1.657 + 1.658 + return this.__stmtSelectGlobalPref; 1.659 + }, 1.660 + 1.661 + _selectGlobalPref: function ContentPrefService__selectGlobalPref(aName, aCallback) { 1.662 + let value = undefined; 1.663 + if (this._cache.has(null, aName)) { 1.664 + value = this._cache.get(null, aName); 1.665 + if (aCallback) { 1.666 + this._scheduleCallback(function(){aCallback.onResult(value);}); 1.667 + return; 1.668 + } 1.669 + return value; 1.670 + } 1.671 + 1.672 + try { 1.673 + this._stmtSelectGlobalPref.params.name = aName; 1.674 + 1.675 + if (aCallback) { 1.676 + let cache = this._cache; 1.677 + new AsyncStatement(this._stmtSelectGlobalPref).execute({onResult: function(aResult) { 1.678 + cache.set(null, aName, aResult); 1.679 + aCallback.onResult(aResult); 1.680 + }}); 1.681 + } 1.682 + else { 1.683 + if (this._stmtSelectGlobalPref.executeStep()) { 1.684 + value = this._stmtSelectGlobalPref.row["value"]; 1.685 + } 1.686 + this._cache.set(null, aName, value); 1.687 + } 1.688 + } 1.689 + finally { 1.690 + this._stmtSelectGlobalPref.reset(); 1.691 + } 1.692 + 1.693 + return value; 1.694 + }, 1.695 + 1.696 + __stmtSelectGroupID: null, 1.697 + get _stmtSelectGroupID() { 1.698 + if (!this.__stmtSelectGroupID) 1.699 + this.__stmtSelectGroupID = this._dbCreateStatement( 1.700 + "SELECT groups.id AS id " + 1.701 + "FROM groups " + 1.702 + "WHERE groups.name = :name " 1.703 + ); 1.704 + 1.705 + return this.__stmtSelectGroupID; 1.706 + }, 1.707 + 1.708 + _selectGroupID: function ContentPrefService__selectGroupID(aName) { 1.709 + var id; 1.710 + 1.711 + try { 1.712 + this._stmtSelectGroupID.params.name = aName; 1.713 + 1.714 + if (this._stmtSelectGroupID.executeStep()) 1.715 + id = this._stmtSelectGroupID.row["id"]; 1.716 + } 1.717 + finally { 1.718 + this._stmtSelectGroupID.reset(); 1.719 + } 1.720 + 1.721 + return id; 1.722 + }, 1.723 + 1.724 + __stmtInsertGroup: null, 1.725 + get _stmtInsertGroup() { 1.726 + if (!this.__stmtInsertGroup) 1.727 + this.__stmtInsertGroup = this._dbCreateStatement( 1.728 + "INSERT INTO groups (name) VALUES (:name)" 1.729 + ); 1.730 + 1.731 + return this.__stmtInsertGroup; 1.732 + }, 1.733 + 1.734 + _insertGroup: function ContentPrefService__insertGroup(aName) { 1.735 + this._stmtInsertGroup.params.name = aName; 1.736 + this._stmtInsertGroup.execute(); 1.737 + return this._dbConnection.lastInsertRowID; 1.738 + }, 1.739 + 1.740 + __stmtSelectSettingID: null, 1.741 + get _stmtSelectSettingID() { 1.742 + if (!this.__stmtSelectSettingID) 1.743 + this.__stmtSelectSettingID = this._dbCreateStatement( 1.744 + "SELECT id FROM settings WHERE name = :name" 1.745 + ); 1.746 + 1.747 + return this.__stmtSelectSettingID; 1.748 + }, 1.749 + 1.750 + _selectSettingID: function ContentPrefService__selectSettingID(aName) { 1.751 + var id; 1.752 + 1.753 + try { 1.754 + this._stmtSelectSettingID.params.name = aName; 1.755 + 1.756 + if (this._stmtSelectSettingID.executeStep()) 1.757 + id = this._stmtSelectSettingID.row["id"]; 1.758 + } 1.759 + finally { 1.760 + this._stmtSelectSettingID.reset(); 1.761 + } 1.762 + 1.763 + return id; 1.764 + }, 1.765 + 1.766 + __stmtInsertSetting: null, 1.767 + get _stmtInsertSetting() { 1.768 + if (!this.__stmtInsertSetting) 1.769 + this.__stmtInsertSetting = this._dbCreateStatement( 1.770 + "INSERT INTO settings (name) VALUES (:name)" 1.771 + ); 1.772 + 1.773 + return this.__stmtInsertSetting; 1.774 + }, 1.775 + 1.776 + _insertSetting: function ContentPrefService__insertSetting(aName) { 1.777 + this._stmtInsertSetting.params.name = aName; 1.778 + this._stmtInsertSetting.execute(); 1.779 + return this._dbConnection.lastInsertRowID; 1.780 + }, 1.781 + 1.782 + __stmtSelectPrefID: null, 1.783 + get _stmtSelectPrefID() { 1.784 + if (!this.__stmtSelectPrefID) 1.785 + this.__stmtSelectPrefID = this._dbCreateStatement( 1.786 + "SELECT id FROM prefs WHERE groupID = :groupID AND settingID = :settingID" 1.787 + ); 1.788 + 1.789 + return this.__stmtSelectPrefID; 1.790 + }, 1.791 + 1.792 + _selectPrefID: function ContentPrefService__selectPrefID(aGroupID, aSettingID) { 1.793 + var id; 1.794 + 1.795 + try { 1.796 + this._stmtSelectPrefID.params.groupID = aGroupID; 1.797 + this._stmtSelectPrefID.params.settingID = aSettingID; 1.798 + 1.799 + if (this._stmtSelectPrefID.executeStep()) 1.800 + id = this._stmtSelectPrefID.row["id"]; 1.801 + } 1.802 + finally { 1.803 + this._stmtSelectPrefID.reset(); 1.804 + } 1.805 + 1.806 + return id; 1.807 + }, 1.808 + 1.809 + __stmtSelectGlobalPrefID: null, 1.810 + get _stmtSelectGlobalPrefID() { 1.811 + if (!this.__stmtSelectGlobalPrefID) 1.812 + this.__stmtSelectGlobalPrefID = this._dbCreateStatement( 1.813 + "SELECT id FROM prefs WHERE groupID IS NULL AND settingID = :settingID" 1.814 + ); 1.815 + 1.816 + return this.__stmtSelectGlobalPrefID; 1.817 + }, 1.818 + 1.819 + _selectGlobalPrefID: function ContentPrefService__selectGlobalPrefID(aSettingID) { 1.820 + var id; 1.821 + 1.822 + try { 1.823 + this._stmtSelectGlobalPrefID.params.settingID = aSettingID; 1.824 + 1.825 + if (this._stmtSelectGlobalPrefID.executeStep()) 1.826 + id = this._stmtSelectGlobalPrefID.row["id"]; 1.827 + } 1.828 + finally { 1.829 + this._stmtSelectGlobalPrefID.reset(); 1.830 + } 1.831 + 1.832 + return id; 1.833 + }, 1.834 + 1.835 + __stmtInsertPref: null, 1.836 + get _stmtInsertPref() { 1.837 + if (!this.__stmtInsertPref) 1.838 + this.__stmtInsertPref = this._dbCreateStatement( 1.839 + "INSERT INTO prefs (groupID, settingID, value) " + 1.840 + "VALUES (:groupID, :settingID, :value)" 1.841 + ); 1.842 + 1.843 + return this.__stmtInsertPref; 1.844 + }, 1.845 + 1.846 + _insertPref: function ContentPrefService__insertPref(aGroupID, aSettingID, aValue) { 1.847 + this._stmtInsertPref.params.groupID = aGroupID; 1.848 + this._stmtInsertPref.params.settingID = aSettingID; 1.849 + this._stmtInsertPref.params.value = aValue; 1.850 + this._stmtInsertPref.execute(); 1.851 + return this._dbConnection.lastInsertRowID; 1.852 + }, 1.853 + 1.854 + __stmtUpdatePref: null, 1.855 + get _stmtUpdatePref() { 1.856 + if (!this.__stmtUpdatePref) 1.857 + this.__stmtUpdatePref = this._dbCreateStatement( 1.858 + "UPDATE prefs SET value = :value WHERE id = :id" 1.859 + ); 1.860 + 1.861 + return this.__stmtUpdatePref; 1.862 + }, 1.863 + 1.864 + _updatePref: function ContentPrefService__updatePref(aPrefID, aValue) { 1.865 + this._stmtUpdatePref.params.id = aPrefID; 1.866 + this._stmtUpdatePref.params.value = aValue; 1.867 + this._stmtUpdatePref.execute(); 1.868 + }, 1.869 + 1.870 + __stmtDeletePref: null, 1.871 + get _stmtDeletePref() { 1.872 + if (!this.__stmtDeletePref) 1.873 + this.__stmtDeletePref = this._dbCreateStatement( 1.874 + "DELETE FROM prefs WHERE id = :id" 1.875 + ); 1.876 + 1.877 + return this.__stmtDeletePref; 1.878 + }, 1.879 + 1.880 + _deletePref: function ContentPrefService__deletePref(aPrefID) { 1.881 + this._stmtDeletePref.params.id = aPrefID; 1.882 + this._stmtDeletePref.execute(); 1.883 + }, 1.884 + 1.885 + __stmtDeleteSettingIfUnused: null, 1.886 + get _stmtDeleteSettingIfUnused() { 1.887 + if (!this.__stmtDeleteSettingIfUnused) 1.888 + this.__stmtDeleteSettingIfUnused = this._dbCreateStatement( 1.889 + "DELETE FROM settings WHERE id = :id " + 1.890 + "AND id NOT IN (SELECT DISTINCT settingID FROM prefs)" 1.891 + ); 1.892 + 1.893 + return this.__stmtDeleteSettingIfUnused; 1.894 + }, 1.895 + 1.896 + _deleteSettingIfUnused: function ContentPrefService__deleteSettingIfUnused(aSettingID) { 1.897 + this._stmtDeleteSettingIfUnused.params.id = aSettingID; 1.898 + this._stmtDeleteSettingIfUnused.execute(); 1.899 + }, 1.900 + 1.901 + __stmtDeleteGroupIfUnused: null, 1.902 + get _stmtDeleteGroupIfUnused() { 1.903 + if (!this.__stmtDeleteGroupIfUnused) 1.904 + this.__stmtDeleteGroupIfUnused = this._dbCreateStatement( 1.905 + "DELETE FROM groups WHERE id = :id " + 1.906 + "AND id NOT IN (SELECT DISTINCT groupID FROM prefs)" 1.907 + ); 1.908 + 1.909 + return this.__stmtDeleteGroupIfUnused; 1.910 + }, 1.911 + 1.912 + _deleteGroupIfUnused: function ContentPrefService__deleteGroupIfUnused(aGroupID) { 1.913 + this._stmtDeleteGroupIfUnused.params.id = aGroupID; 1.914 + this._stmtDeleteGroupIfUnused.execute(); 1.915 + }, 1.916 + 1.917 + __stmtSelectPrefs: null, 1.918 + get _stmtSelectPrefs() { 1.919 + if (!this.__stmtSelectPrefs) 1.920 + this.__stmtSelectPrefs = this._dbCreateStatement( 1.921 + "SELECT settings.name AS name, prefs.value AS value " + 1.922 + "FROM prefs " + 1.923 + "JOIN groups ON prefs.groupID = groups.id " + 1.924 + "JOIN settings ON prefs.settingID = settings.id " + 1.925 + "WHERE groups.name = :group " 1.926 + ); 1.927 + 1.928 + return this.__stmtSelectPrefs; 1.929 + }, 1.930 + 1.931 + _selectPrefs: function ContentPrefService__selectPrefs(aGroup) { 1.932 + var prefs = Cc["@mozilla.org/hash-property-bag;1"]. 1.933 + createInstance(Ci.nsIWritablePropertyBag); 1.934 + 1.935 + try { 1.936 + this._stmtSelectPrefs.params.group = aGroup; 1.937 + 1.938 + while (this._stmtSelectPrefs.executeStep()) 1.939 + prefs.setProperty(this._stmtSelectPrefs.row["name"], 1.940 + this._stmtSelectPrefs.row["value"]); 1.941 + } 1.942 + finally { 1.943 + this._stmtSelectPrefs.reset(); 1.944 + } 1.945 + 1.946 + return prefs; 1.947 + }, 1.948 + 1.949 + __stmtSelectGlobalPrefs: null, 1.950 + get _stmtSelectGlobalPrefs() { 1.951 + if (!this.__stmtSelectGlobalPrefs) 1.952 + this.__stmtSelectGlobalPrefs = this._dbCreateStatement( 1.953 + "SELECT settings.name AS name, prefs.value AS value " + 1.954 + "FROM prefs " + 1.955 + "JOIN settings ON prefs.settingID = settings.id " + 1.956 + "WHERE prefs.groupID IS NULL" 1.957 + ); 1.958 + 1.959 + return this.__stmtSelectGlobalPrefs; 1.960 + }, 1.961 + 1.962 + _selectGlobalPrefs: function ContentPrefService__selectGlobalPrefs() { 1.963 + var prefs = Cc["@mozilla.org/hash-property-bag;1"]. 1.964 + createInstance(Ci.nsIWritablePropertyBag); 1.965 + 1.966 + try { 1.967 + while (this._stmtSelectGlobalPrefs.executeStep()) 1.968 + prefs.setProperty(this._stmtSelectGlobalPrefs.row["name"], 1.969 + this._stmtSelectGlobalPrefs.row["value"]); 1.970 + } 1.971 + finally { 1.972 + this._stmtSelectGlobalPrefs.reset(); 1.973 + } 1.974 + 1.975 + return prefs; 1.976 + }, 1.977 + 1.978 + __stmtSelectPrefsByName: null, 1.979 + get _stmtSelectPrefsByName() { 1.980 + if (!this.__stmtSelectPrefsByName) 1.981 + this.__stmtSelectPrefsByName = this._dbCreateStatement( 1.982 + "SELECT groups.name AS groupName, prefs.value AS value " + 1.983 + "FROM prefs " + 1.984 + "JOIN groups ON prefs.groupID = groups.id " + 1.985 + "JOIN settings ON prefs.settingID = settings.id " + 1.986 + "WHERE settings.name = :setting " 1.987 + ); 1.988 + 1.989 + return this.__stmtSelectPrefsByName; 1.990 + }, 1.991 + 1.992 + _selectPrefsByName: function ContentPrefService__selectPrefsByName(aName) { 1.993 + var prefs = Cc["@mozilla.org/hash-property-bag;1"]. 1.994 + createInstance(Ci.nsIWritablePropertyBag); 1.995 + 1.996 + try { 1.997 + this._stmtSelectPrefsByName.params.setting = aName; 1.998 + 1.999 + while (this._stmtSelectPrefsByName.executeStep()) 1.1000 + prefs.setProperty(this._stmtSelectPrefsByName.row["groupName"], 1.1001 + this._stmtSelectPrefsByName.row["value"]); 1.1002 + } 1.1003 + finally { 1.1004 + this._stmtSelectPrefsByName.reset(); 1.1005 + } 1.1006 + 1.1007 + var global = this._selectGlobalPref(aName); 1.1008 + if (typeof global != "undefined") { 1.1009 + prefs.setProperty(null, global); 1.1010 + } 1.1011 + 1.1012 + return prefs; 1.1013 + }, 1.1014 + 1.1015 + 1.1016 + //**************************************************************************// 1.1017 + // Database Creation & Access 1.1018 + 1.1019 + _dbVersion: 3, 1.1020 + 1.1021 + _dbSchema: { 1.1022 + tables: { 1.1023 + groups: "id INTEGER PRIMARY KEY, \ 1.1024 + name TEXT NOT NULL", 1.1025 + 1.1026 + settings: "id INTEGER PRIMARY KEY, \ 1.1027 + name TEXT NOT NULL", 1.1028 + 1.1029 + prefs: "id INTEGER PRIMARY KEY, \ 1.1030 + groupID INTEGER REFERENCES groups(id), \ 1.1031 + settingID INTEGER NOT NULL REFERENCES settings(id), \ 1.1032 + value BLOB" 1.1033 + }, 1.1034 + indices: { 1.1035 + groups_idx: { 1.1036 + table: "groups", 1.1037 + columns: ["name"] 1.1038 + }, 1.1039 + settings_idx: { 1.1040 + table: "settings", 1.1041 + columns: ["name"] 1.1042 + }, 1.1043 + prefs_idx: { 1.1044 + table: "prefs", 1.1045 + columns: ["groupID", "settingID"] 1.1046 + } 1.1047 + } 1.1048 + }, 1.1049 + 1.1050 + _dbConnection: null, 1.1051 + 1.1052 + _dbCreateStatement: function ContentPrefService__dbCreateStatement(aSQLString) { 1.1053 + try { 1.1054 + var statement = this._dbConnection.createStatement(aSQLString); 1.1055 + } 1.1056 + catch(ex) { 1.1057 + Cu.reportError("error creating statement " + aSQLString + ": " + 1.1058 + this._dbConnection.lastError + " - " + 1.1059 + this._dbConnection.lastErrorString); 1.1060 + throw ex; 1.1061 + } 1.1062 + 1.1063 + return statement; 1.1064 + }, 1.1065 + 1.1066 + // _dbInit and the methods it calls (_dbCreate, _dbMigrate, and version- 1.1067 + // specific migration methods) must be careful not to call any method 1.1068 + // of the service that assumes the database connection has already been 1.1069 + // initialized, since it won't be initialized until at the end of _dbInit. 1.1070 + 1.1071 + _dbInit: function ContentPrefService__dbInit() { 1.1072 + var dirService = Cc["@mozilla.org/file/directory_service;1"]. 1.1073 + getService(Ci.nsIProperties); 1.1074 + var dbFile = dirService.get("ProfD", Ci.nsIFile); 1.1075 + dbFile.append("content-prefs.sqlite"); 1.1076 + 1.1077 + var dbService = Cc["@mozilla.org/storage/service;1"]. 1.1078 + getService(Ci.mozIStorageService); 1.1079 + 1.1080 + var dbConnection; 1.1081 + 1.1082 + if (true || !dbFile.exists()) 1.1083 + dbConnection = this._dbCreate(dbService, dbFile); 1.1084 + else { 1.1085 + try { 1.1086 + dbConnection = dbService.openDatabase(dbFile); 1.1087 + } 1.1088 + // If the connection isn't ready after we open the database, that means 1.1089 + // the database has been corrupted, so we back it up and then recreate it. 1.1090 + catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) { 1.1091 + dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, 1.1092 + dbConnection); 1.1093 + } 1.1094 + 1.1095 + // Get the version of the schema in the file. 1.1096 + var version = dbConnection.schemaVersion; 1.1097 + 1.1098 + // Try to migrate the schema in the database to the current schema used by 1.1099 + // the service. If migration fails, back up the database and recreate it. 1.1100 + if (version != this._dbVersion) { 1.1101 + try { 1.1102 + this._dbMigrate(dbConnection, version, this._dbVersion); 1.1103 + } 1.1104 + catch(ex) { 1.1105 + Cu.reportError("error migrating DB: " + ex + "; backing up and recreating"); 1.1106 + dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, dbConnection); 1.1107 + } 1.1108 + } 1.1109 + } 1.1110 + 1.1111 + // Turn off disk synchronization checking to reduce disk churn and speed up 1.1112 + // operations when prefs are changed rapidly (such as when a user repeatedly 1.1113 + // changes the value of the browser zoom setting for a site). 1.1114 + // 1.1115 + // Note: this could cause database corruption if the OS crashes or machine 1.1116 + // loses power before the data gets written to disk, but this is considered 1.1117 + // a reasonable risk for the not-so-critical data stored in this database. 1.1118 + // 1.1119 + // If you really don't want to take this risk, however, just set the 1.1120 + // toolkit.storage.synchronous pref to 1 (NORMAL synchronization) or 2 1.1121 + // (FULL synchronization), in which case mozStorageConnection::Initialize 1.1122 + // will use that value, and we won't override it here. 1.1123 + if (!this._prefSvc.prefHasUserValue("toolkit.storage.synchronous")) 1.1124 + dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF"); 1.1125 + 1.1126 + this._dbConnection = dbConnection; 1.1127 + }, 1.1128 + 1.1129 + _dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) { 1.1130 + var dbConnection = aDBService.openSpecialDatabase("memory"); 1.1131 + 1.1132 + try { 1.1133 + this._dbCreateSchema(dbConnection); 1.1134 + dbConnection.schemaVersion = this._dbVersion; 1.1135 + } 1.1136 + catch(ex) { 1.1137 + // If we failed to create the database (perhaps because the disk ran out 1.1138 + // of space), then remove the database file so we don't leave it in some 1.1139 + // half-created state from which we won't know how to recover. 1.1140 + dbConnection.close(); 1.1141 + aDBFile.remove(false); 1.1142 + throw ex; 1.1143 + } 1.1144 + 1.1145 + return dbConnection; 1.1146 + }, 1.1147 + 1.1148 + _dbCreateSchema: function ContentPrefService__dbCreateSchema(aDBConnection) { 1.1149 + this._dbCreateTables(aDBConnection); 1.1150 + this._dbCreateIndices(aDBConnection); 1.1151 + }, 1.1152 + 1.1153 + _dbCreateTables: function ContentPrefService__dbCreateTables(aDBConnection) { 1.1154 + for (let name in this._dbSchema.tables) 1.1155 + aDBConnection.createTable(name, this._dbSchema.tables[name]); 1.1156 + }, 1.1157 + 1.1158 + _dbCreateIndices: function ContentPrefService__dbCreateIndices(aDBConnection) { 1.1159 + for (let name in this._dbSchema.indices) { 1.1160 + let index = this._dbSchema.indices[name]; 1.1161 + let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table + 1.1162 + "(" + index.columns.join(", ") + ")"; 1.1163 + aDBConnection.executeSimpleSQL(statement); 1.1164 + } 1.1165 + }, 1.1166 + 1.1167 + _dbBackUpAndRecreate: function ContentPrefService__dbBackUpAndRecreate(aDBService, 1.1168 + aDBFile, 1.1169 + aDBConnection) { 1.1170 + aDBService.backupDatabaseFile(aDBFile, "content-prefs.sqlite.corrupt"); 1.1171 + 1.1172 + // Close the database, ignoring the "already closed" exception, if any. 1.1173 + // It'll be open if we're here because of a migration failure but closed 1.1174 + // if we're here because of database corruption. 1.1175 + try { aDBConnection.close() } catch(ex) {} 1.1176 + 1.1177 + aDBFile.remove(false); 1.1178 + 1.1179 + let dbConnection = this._dbCreate(aDBService, aDBFile); 1.1180 + 1.1181 + return dbConnection; 1.1182 + }, 1.1183 + 1.1184 + _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) { 1.1185 + if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) { 1.1186 + aDBConnection.beginTransaction(); 1.1187 + try { 1.1188 + this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection); 1.1189 + aDBConnection.schemaVersion = aNewVersion; 1.1190 + aDBConnection.commitTransaction(); 1.1191 + } 1.1192 + catch(ex) { 1.1193 + aDBConnection.rollbackTransaction(); 1.1194 + throw ex; 1.1195 + } 1.1196 + } 1.1197 + else 1.1198 + throw("no migrator function from version " + aOldVersion + 1.1199 + " to version " + aNewVersion); 1.1200 + }, 1.1201 + 1.1202 + /** 1.1203 + * If the schema version is 0, that means it was never set, which means 1.1204 + * the database was somehow created without the schema being applied, perhaps 1.1205 + * because the system ran out of disk space (although we check for this 1.1206 + * in _createDB) or because some other code created the database file without 1.1207 + * applying the schema. In any case, recover by simply reapplying the schema. 1.1208 + */ 1.1209 + _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) { 1.1210 + this._dbCreateSchema(aDBConnection); 1.1211 + }, 1.1212 + 1.1213 + _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) { 1.1214 + aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld"); 1.1215 + aDBConnection.createTable("groups", this._dbSchema.tables.groups); 1.1216 + aDBConnection.executeSimpleSQL( 1.1217 + "INSERT INTO groups (id, name) " + 1.1218 + "SELECT id, name FROM groupsOld" 1.1219 + ); 1.1220 + 1.1221 + aDBConnection.executeSimpleSQL("DROP TABLE groupers"); 1.1222 + aDBConnection.executeSimpleSQL("DROP TABLE groupsOld"); 1.1223 + 1.1224 + this._dbCreateIndices(aDBConnection); 1.1225 + }, 1.1226 + 1.1227 + _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) { 1.1228 + this._dbCreateIndices(aDBConnection); 1.1229 + }, 1.1230 + 1.1231 + _parseGroupParam: function ContentPrefService__parseGroupParam(aGroup) { 1.1232 + if (aGroup == null) 1.1233 + return null; 1.1234 + if (aGroup.constructor.name == "String") 1.1235 + return aGroup.toString(); 1.1236 + if (aGroup instanceof Ci.nsIURI) 1.1237 + return this.grouper.group(aGroup); 1.1238 + 1.1239 + throw Components.Exception("aGroup is not a string, nsIURI or null", 1.1240 + Cr.NS_ERROR_ILLEGAL_VALUE); 1.1241 + }, 1.1242 +}; 1.1243 + 1.1244 +function warnDeprecated() { 1.1245 + let Deprecated = Cu.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; 1.1246 + Deprecated.warning("nsIContentPrefService is deprecated. Please use nsIContentPrefService2 instead.", 1.1247 + "https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIContentPrefService2", 1.1248 + Components.stack.caller); 1.1249 +} 1.1250 + 1.1251 + 1.1252 +function HostnameGrouper() {} 1.1253 + 1.1254 +HostnameGrouper.prototype = { 1.1255 + //**************************************************************************// 1.1256 + // XPCOM Plumbing 1.1257 + 1.1258 + classID: Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"), 1.1259 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]), 1.1260 + 1.1261 + //**************************************************************************// 1.1262 + // nsIContentURIGrouper 1.1263 + 1.1264 + group: function HostnameGrouper_group(aURI) { 1.1265 + var group; 1.1266 + 1.1267 + try { 1.1268 + // Accessing the host property of the URI will throw an exception 1.1269 + // if the URI is of a type that doesn't have a host property. 1.1270 + // Otherwise, we manually throw an exception if the host is empty, 1.1271 + // since the effect is the same (we can't derive a group from it). 1.1272 + 1.1273 + group = aURI.host; 1.1274 + if (!group) 1.1275 + throw("can't derive group from host; no host in URI"); 1.1276 + } 1.1277 + catch(ex) { 1.1278 + // If we don't have a host, then use the entire URI (minus the query, 1.1279 + // reference, and hash, if possible) as the group. This means that URIs 1.1280 + // like about:mozilla and about:blank will be considered separate groups, 1.1281 + // but at least they'll be grouped somehow. 1.1282 + 1.1283 + // This also means that each individual file: URL will be considered 1.1284 + // its own group. This seems suboptimal, but so does treating the entire 1.1285 + // file: URL space as a single group (especially if folks start setting 1.1286 + // group-specific capabilities prefs). 1.1287 + 1.1288 + // XXX Is there something better we can do here? 1.1289 + 1.1290 + try { 1.1291 + var url = aURI.QueryInterface(Ci.nsIURL); 1.1292 + group = aURI.prePath + url.filePath; 1.1293 + } 1.1294 + catch(ex) { 1.1295 + group = aURI.spec; 1.1296 + } 1.1297 + } 1.1298 + 1.1299 + return group; 1.1300 + } 1.1301 +}; 1.1302 + 1.1303 +function AsyncStatement(aStatement) { 1.1304 + this.stmt = aStatement; 1.1305 +} 1.1306 + 1.1307 +AsyncStatement.prototype = { 1.1308 + execute: function AsyncStmt_execute(aCallback) { 1.1309 + let stmt = this.stmt; 1.1310 + stmt.executeAsync({ 1.1311 + _callback: aCallback, 1.1312 + _hadResult: false, 1.1313 + handleResult: function(aResult) { 1.1314 + this._hadResult = true; 1.1315 + if (this._callback) { 1.1316 + let row = aResult.getNextRow(); 1.1317 + this._callback.onResult(row.getResultByName("value")); 1.1318 + } 1.1319 + }, 1.1320 + handleCompletion: function(aReason) { 1.1321 + if (!this._hadResult && this._callback && 1.1322 + aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) 1.1323 + this._callback.onResult(undefined); 1.1324 + }, 1.1325 + handleError: function(aError) {} 1.1326 + }); 1.1327 + } 1.1328 +}; 1.1329 + 1.1330 +//****************************************************************************// 1.1331 +// XPCOM Plumbing 1.1332 + 1.1333 +var components = [ContentPrefService, HostnameGrouper]; 1.1334 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);