Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | const Ci = Components.interfaces; |
michael@0 | 6 | const Cc = Components.classes; |
michael@0 | 7 | const Cr = Components.results; |
michael@0 | 8 | const Cu = Components.utils; |
michael@0 | 9 | |
michael@0 | 10 | const CACHE_MAX_GROUP_ENTRIES = 100; |
michael@0 | 11 | |
michael@0 | 12 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 13 | |
michael@0 | 14 | /** |
michael@0 | 15 | * Remotes the service. All the remoting/electrolysis code is in here, |
michael@0 | 16 | * so the regular service code below remains uncluttered and maintainable. |
michael@0 | 17 | */ |
michael@0 | 18 | function electrolify(service) { |
michael@0 | 19 | // FIXME: For now, use the wrappedJSObject hack, until bug |
michael@0 | 20 | // 593407 which will clean that up. |
michael@0 | 21 | // Note that we also use this in the xpcshell tests, separately. |
michael@0 | 22 | service.wrappedJSObject = service; |
michael@0 | 23 | |
michael@0 | 24 | var appInfo = Cc["@mozilla.org/xre/app-info;1"]; |
michael@0 | 25 | if (appInfo && appInfo.getService(Ci.nsIXULRuntime).processType != |
michael@0 | 26 | Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) |
michael@0 | 27 | { |
michael@0 | 28 | // Child process |
michael@0 | 29 | service._dbInit = function(){}; // No local DB |
michael@0 | 30 | } |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | function ContentPrefService() { |
michael@0 | 34 | electrolify(this); |
michael@0 | 35 | |
michael@0 | 36 | // If this throws an exception, it causes the getService call to fail, |
michael@0 | 37 | // but the next time a consumer tries to retrieve the service, we'll try |
michael@0 | 38 | // to initialize the database again, which might work if the failure |
michael@0 | 39 | // was due to a temporary condition (like being out of disk space). |
michael@0 | 40 | this._dbInit(); |
michael@0 | 41 | |
michael@0 | 42 | this._observerSvc.addObserver(this, "last-pb-context-exited", false); |
michael@0 | 43 | |
michael@0 | 44 | // Observe shutdown so we can shut down the database connection. |
michael@0 | 45 | this._observerSvc.addObserver(this, "xpcom-shutdown", false); |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | Cu.import("resource://gre/modules/ContentPrefStore.jsm"); |
michael@0 | 49 | const cache = new ContentPrefStore(); |
michael@0 | 50 | cache.set = function CPS_cache_set(group, name, val) { |
michael@0 | 51 | Object.getPrototypeOf(this).set.apply(this, arguments); |
michael@0 | 52 | let groupCount = Object.keys(this._groups).length; |
michael@0 | 53 | if (groupCount >= CACHE_MAX_GROUP_ENTRIES) { |
michael@0 | 54 | // Clean half of the entries |
michael@0 | 55 | for (let [group, name, ] in this) { |
michael@0 | 56 | this.remove(group, name); |
michael@0 | 57 | groupCount--; |
michael@0 | 58 | if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2) |
michael@0 | 59 | break; |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | }; |
michael@0 | 63 | |
michael@0 | 64 | const privModeStorage = new ContentPrefStore(); |
michael@0 | 65 | |
michael@0 | 66 | ContentPrefService.prototype = { |
michael@0 | 67 | //**************************************************************************// |
michael@0 | 68 | // XPCOM Plumbing |
michael@0 | 69 | |
michael@0 | 70 | classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"), |
michael@0 | 71 | |
michael@0 | 72 | QueryInterface: function CPS_QueryInterface(iid) { |
michael@0 | 73 | let supportedIIDs = [ |
michael@0 | 74 | Ci.nsIContentPrefService, |
michael@0 | 75 | Ci.nsIFrameMessageListener, |
michael@0 | 76 | Ci.nsISupports, |
michael@0 | 77 | ]; |
michael@0 | 78 | if (supportedIIDs.some(function (i) iid.equals(i))) |
michael@0 | 79 | return this; |
michael@0 | 80 | if (iid.equals(Ci.nsIContentPrefService2)) { |
michael@0 | 81 | if (!this._contentPrefService2) { |
michael@0 | 82 | let s = {}; |
michael@0 | 83 | Cu.import("resource://gre/modules/ContentPrefService2.jsm", s); |
michael@0 | 84 | this._contentPrefService2 = new s.ContentPrefService2(this); |
michael@0 | 85 | } |
michael@0 | 86 | return this._contentPrefService2; |
michael@0 | 87 | } |
michael@0 | 88 | throw Cr.NS_ERROR_NO_INTERFACE; |
michael@0 | 89 | }, |
michael@0 | 90 | |
michael@0 | 91 | //**************************************************************************// |
michael@0 | 92 | // Convenience Getters |
michael@0 | 93 | |
michael@0 | 94 | // Observer Service |
michael@0 | 95 | __observerSvc: null, |
michael@0 | 96 | get _observerSvc() { |
michael@0 | 97 | if (!this.__observerSvc) |
michael@0 | 98 | this.__observerSvc = Cc["@mozilla.org/observer-service;1"]. |
michael@0 | 99 | getService(Ci.nsIObserverService); |
michael@0 | 100 | return this.__observerSvc; |
michael@0 | 101 | }, |
michael@0 | 102 | |
michael@0 | 103 | // Console Service |
michael@0 | 104 | __consoleSvc: null, |
michael@0 | 105 | get _consoleSvc() { |
michael@0 | 106 | if (!this.__consoleSvc) |
michael@0 | 107 | this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"]. |
michael@0 | 108 | getService(Ci.nsIConsoleService); |
michael@0 | 109 | return this.__consoleSvc; |
michael@0 | 110 | }, |
michael@0 | 111 | |
michael@0 | 112 | // Preferences Service |
michael@0 | 113 | __prefSvc: null, |
michael@0 | 114 | get _prefSvc() { |
michael@0 | 115 | if (!this.__prefSvc) |
michael@0 | 116 | this.__prefSvc = Cc["@mozilla.org/preferences-service;1"]. |
michael@0 | 117 | getService(Ci.nsIPrefBranch); |
michael@0 | 118 | return this.__prefSvc; |
michael@0 | 119 | }, |
michael@0 | 120 | |
michael@0 | 121 | |
michael@0 | 122 | //**************************************************************************// |
michael@0 | 123 | // Destruction |
michael@0 | 124 | |
michael@0 | 125 | _destroy: function ContentPrefService__destroy() { |
michael@0 | 126 | this._observerSvc.removeObserver(this, "xpcom-shutdown"); |
michael@0 | 127 | this._observerSvc.removeObserver(this, "last-pb-context-exited"); |
michael@0 | 128 | |
michael@0 | 129 | // Finalize statements which may have been used asynchronously. |
michael@0 | 130 | // FIXME(696499): put them in an object cache like other components. |
michael@0 | 131 | if (this.__stmtSelectPrefID) { |
michael@0 | 132 | this.__stmtSelectPrefID.finalize(); |
michael@0 | 133 | this.__stmtSelectPrefID = null; |
michael@0 | 134 | } |
michael@0 | 135 | if (this.__stmtSelectGlobalPrefID) { |
michael@0 | 136 | this.__stmtSelectGlobalPrefID.finalize(); |
michael@0 | 137 | this.__stmtSelectGlobalPrefID = null; |
michael@0 | 138 | } |
michael@0 | 139 | if (this.__stmtInsertPref) { |
michael@0 | 140 | this.__stmtInsertPref.finalize(); |
michael@0 | 141 | this.__stmtInsertPref = null; |
michael@0 | 142 | } |
michael@0 | 143 | if (this.__stmtInsertGroup) { |
michael@0 | 144 | this.__stmtInsertGroup.finalize(); |
michael@0 | 145 | this.__stmtInsertGroup = null; |
michael@0 | 146 | } |
michael@0 | 147 | if (this.__stmtInsertSetting) { |
michael@0 | 148 | this.__stmtInsertSetting.finalize(); |
michael@0 | 149 | this.__stmtInsertSetting = null; |
michael@0 | 150 | } |
michael@0 | 151 | if (this.__stmtSelectGroupID) { |
michael@0 | 152 | this.__stmtSelectGroupID.finalize(); |
michael@0 | 153 | this.__stmtSelectGroupID = null; |
michael@0 | 154 | } |
michael@0 | 155 | if (this.__stmtSelectSettingID) { |
michael@0 | 156 | this.__stmtSelectSettingID.finalize(); |
michael@0 | 157 | this.__stmtSelectSettingID = null; |
michael@0 | 158 | } |
michael@0 | 159 | if (this.__stmtSelectPref) { |
michael@0 | 160 | this.__stmtSelectPref.finalize(); |
michael@0 | 161 | this.__stmtSelectPref = null; |
michael@0 | 162 | } |
michael@0 | 163 | if (this.__stmtSelectGlobalPref) { |
michael@0 | 164 | this.__stmtSelectGlobalPref.finalize(); |
michael@0 | 165 | this.__stmtSelectGlobalPref = null; |
michael@0 | 166 | } |
michael@0 | 167 | if (this.__stmtSelectPrefsByName) { |
michael@0 | 168 | this.__stmtSelectPrefsByName.finalize(); |
michael@0 | 169 | this.__stmtSelectPrefsByName = null; |
michael@0 | 170 | } |
michael@0 | 171 | if (this.__stmtDeleteSettingIfUnused) { |
michael@0 | 172 | this.__stmtDeleteSettingIfUnused.finalize(); |
michael@0 | 173 | this.__stmtDeleteSettingIfUnused = null; |
michael@0 | 174 | } |
michael@0 | 175 | if(this.__stmtSelectPrefs) { |
michael@0 | 176 | this.__stmtSelectPrefs.finalize(); |
michael@0 | 177 | this.__stmtSelectPrefs = null; |
michael@0 | 178 | } |
michael@0 | 179 | if(this.__stmtDeleteGroupIfUnused) { |
michael@0 | 180 | this.__stmtDeleteGroupIfUnused.finalize(); |
michael@0 | 181 | this.__stmtDeleteGroupIfUnused = null; |
michael@0 | 182 | } |
michael@0 | 183 | if (this.__stmtDeletePref) { |
michael@0 | 184 | this.__stmtDeletePref.finalize(); |
michael@0 | 185 | this.__stmtDeletePref = null; |
michael@0 | 186 | } |
michael@0 | 187 | if (this.__stmtUpdatePref) { |
michael@0 | 188 | this.__stmtUpdatePref.finalize(); |
michael@0 | 189 | this.__stmtUpdatePref = null; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | if (this._contentPrefService2) |
michael@0 | 193 | this._contentPrefService2.destroy(); |
michael@0 | 194 | |
michael@0 | 195 | this._dbConnection.asyncClose(); |
michael@0 | 196 | |
michael@0 | 197 | // Delete references to XPCOM components to make sure we don't leak them |
michael@0 | 198 | // (although we haven't observed leakage in tests). Also delete references |
michael@0 | 199 | // in _observers and _genericObservers to avoid cycles with those that |
michael@0 | 200 | // refer to us and don't remove themselves from those observer pools. |
michael@0 | 201 | for (var i in this) { |
michael@0 | 202 | try { this[i] = null } |
michael@0 | 203 | // Ignore "setting a property that has only a getter" exceptions. |
michael@0 | 204 | catch(ex) {} |
michael@0 | 205 | } |
michael@0 | 206 | }, |
michael@0 | 207 | |
michael@0 | 208 | |
michael@0 | 209 | //**************************************************************************// |
michael@0 | 210 | // nsIObserver |
michael@0 | 211 | |
michael@0 | 212 | observe: function ContentPrefService_observe(subject, topic, data) { |
michael@0 | 213 | switch (topic) { |
michael@0 | 214 | case "xpcom-shutdown": |
michael@0 | 215 | this._destroy(); |
michael@0 | 216 | break; |
michael@0 | 217 | case "last-pb-context-exited": |
michael@0 | 218 | this._privModeStorage.removeAll(); |
michael@0 | 219 | break; |
michael@0 | 220 | } |
michael@0 | 221 | }, |
michael@0 | 222 | |
michael@0 | 223 | |
michael@0 | 224 | //**************************************************************************// |
michael@0 | 225 | // in-memory cache and private-browsing stores |
michael@0 | 226 | |
michael@0 | 227 | _cache: cache, |
michael@0 | 228 | _privModeStorage: privModeStorage, |
michael@0 | 229 | |
michael@0 | 230 | //**************************************************************************// |
michael@0 | 231 | // nsIContentPrefService |
michael@0 | 232 | |
michael@0 | 233 | getPref: function ContentPrefService_getPref(aGroup, aName, aContext, aCallback) { |
michael@0 | 234 | warnDeprecated(); |
michael@0 | 235 | |
michael@0 | 236 | if (!aName) |
michael@0 | 237 | throw Components.Exception("aName cannot be null or an empty string", |
michael@0 | 238 | Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 239 | |
michael@0 | 240 | var group = this._parseGroupParam(aGroup); |
michael@0 | 241 | |
michael@0 | 242 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 243 | if (this._privModeStorage.has(group, aName)) { |
michael@0 | 244 | let value = this._privModeStorage.get(group, aName); |
michael@0 | 245 | if (aCallback) { |
michael@0 | 246 | this._scheduleCallback(function(){aCallback.onResult(value);}); |
michael@0 | 247 | return; |
michael@0 | 248 | } |
michael@0 | 249 | return value; |
michael@0 | 250 | } |
michael@0 | 251 | // if we don't have a pref specific to this private mode browsing |
michael@0 | 252 | // session, to try to get one from normal mode |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | if (group == null) |
michael@0 | 256 | return this._selectGlobalPref(aName, aCallback); |
michael@0 | 257 | return this._selectPref(group, aName, aCallback); |
michael@0 | 258 | }, |
michael@0 | 259 | |
michael@0 | 260 | setPref: function ContentPrefService_setPref(aGroup, aName, aValue, aContext) { |
michael@0 | 261 | warnDeprecated(); |
michael@0 | 262 | |
michael@0 | 263 | // If the pref is already set to the value, there's nothing more to do. |
michael@0 | 264 | var currentValue = this.getPref(aGroup, aName, aContext); |
michael@0 | 265 | if (typeof currentValue != "undefined") { |
michael@0 | 266 | if (currentValue == aValue) |
michael@0 | 267 | return; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | var group = this._parseGroupParam(aGroup); |
michael@0 | 271 | |
michael@0 | 272 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 273 | this._privModeStorage.setWithCast(group, aName, aValue); |
michael@0 | 274 | this._notifyPrefSet(group, aName, aValue); |
michael@0 | 275 | return; |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | var settingID = this._selectSettingID(aName) || this._insertSetting(aName); |
michael@0 | 279 | var groupID, prefID; |
michael@0 | 280 | if (group == null) { |
michael@0 | 281 | groupID = null; |
michael@0 | 282 | prefID = this._selectGlobalPrefID(settingID); |
michael@0 | 283 | } |
michael@0 | 284 | else { |
michael@0 | 285 | groupID = this._selectGroupID(group) || this._insertGroup(group); |
michael@0 | 286 | prefID = this._selectPrefID(groupID, settingID); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | // Update the existing record, if any, or create a new one. |
michael@0 | 290 | if (prefID) |
michael@0 | 291 | this._updatePref(prefID, aValue); |
michael@0 | 292 | else |
michael@0 | 293 | this._insertPref(groupID, settingID, aValue); |
michael@0 | 294 | |
michael@0 | 295 | this._cache.setWithCast(group, aName, aValue); |
michael@0 | 296 | this._notifyPrefSet(group, aName, aValue); |
michael@0 | 297 | }, |
michael@0 | 298 | |
michael@0 | 299 | hasPref: function ContentPrefService_hasPref(aGroup, aName, aContext) { |
michael@0 | 300 | warnDeprecated(); |
michael@0 | 301 | |
michael@0 | 302 | // XXX If consumers end up calling this method regularly, then we should |
michael@0 | 303 | // optimize this to query the database directly. |
michael@0 | 304 | return (typeof this.getPref(aGroup, aName, aContext) != "undefined"); |
michael@0 | 305 | }, |
michael@0 | 306 | |
michael@0 | 307 | hasCachedPref: function ContentPrefService_hasCachedPref(aGroup, aName, aContext) { |
michael@0 | 308 | warnDeprecated(); |
michael@0 | 309 | |
michael@0 | 310 | if (!aName) |
michael@0 | 311 | throw Components.Exception("aName cannot be null or an empty string", |
michael@0 | 312 | Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 313 | |
michael@0 | 314 | let group = this._parseGroupParam(aGroup); |
michael@0 | 315 | let storage = aContext && aContext.usePrivateBrowsing ? this._privModeStorage: this._cache; |
michael@0 | 316 | return storage.has(group, aName); |
michael@0 | 317 | }, |
michael@0 | 318 | |
michael@0 | 319 | removePref: function ContentPrefService_removePref(aGroup, aName, aContext) { |
michael@0 | 320 | warnDeprecated(); |
michael@0 | 321 | |
michael@0 | 322 | // If there's no old value, then there's nothing to remove. |
michael@0 | 323 | if (!this.hasPref(aGroup, aName, aContext)) |
michael@0 | 324 | return; |
michael@0 | 325 | |
michael@0 | 326 | var group = this._parseGroupParam(aGroup); |
michael@0 | 327 | |
michael@0 | 328 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 329 | this._privModeStorage.remove(group, aName); |
michael@0 | 330 | this._notifyPrefRemoved(group, aName); |
michael@0 | 331 | return; |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | var settingID = this._selectSettingID(aName); |
michael@0 | 335 | var groupID, prefID; |
michael@0 | 336 | if (group == null) { |
michael@0 | 337 | groupID = null; |
michael@0 | 338 | prefID = this._selectGlobalPrefID(settingID); |
michael@0 | 339 | } |
michael@0 | 340 | else { |
michael@0 | 341 | groupID = this._selectGroupID(group); |
michael@0 | 342 | prefID = this._selectPrefID(groupID, settingID); |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | this._deletePref(prefID); |
michael@0 | 346 | |
michael@0 | 347 | // Get rid of extraneous records that are no longer being used. |
michael@0 | 348 | this._deleteSettingIfUnused(settingID); |
michael@0 | 349 | if (groupID) |
michael@0 | 350 | this._deleteGroupIfUnused(groupID); |
michael@0 | 351 | |
michael@0 | 352 | this._cache.remove(group, aName); |
michael@0 | 353 | this._notifyPrefRemoved(group, aName); |
michael@0 | 354 | }, |
michael@0 | 355 | |
michael@0 | 356 | removeGroupedPrefs: function ContentPrefService_removeGroupedPrefs(aContext) { |
michael@0 | 357 | warnDeprecated(); |
michael@0 | 358 | |
michael@0 | 359 | // will not delete global preferences |
michael@0 | 360 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 361 | // keep only global prefs |
michael@0 | 362 | this._privModeStorage.removeAllGroups(); |
michael@0 | 363 | } |
michael@0 | 364 | this._cache.removeAllGroups(); |
michael@0 | 365 | this._dbConnection.beginTransaction(); |
michael@0 | 366 | try { |
michael@0 | 367 | this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL"); |
michael@0 | 368 | this._dbConnection.executeSimpleSQL("DELETE FROM groups"); |
michael@0 | 369 | this._dbConnection.executeSimpleSQL( |
michael@0 | 370 | "DELETE FROM settings " + |
michael@0 | 371 | "WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)" |
michael@0 | 372 | ); |
michael@0 | 373 | this._dbConnection.commitTransaction(); |
michael@0 | 374 | } |
michael@0 | 375 | catch(ex) { |
michael@0 | 376 | this._dbConnection.rollbackTransaction(); |
michael@0 | 377 | throw ex; |
michael@0 | 378 | } |
michael@0 | 379 | }, |
michael@0 | 380 | |
michael@0 | 381 | removePrefsByName: function ContentPrefService_removePrefsByName(aName, aContext) { |
michael@0 | 382 | warnDeprecated(); |
michael@0 | 383 | |
michael@0 | 384 | if (!aName) |
michael@0 | 385 | throw Components.Exception("aName cannot be null or an empty string", |
michael@0 | 386 | Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 387 | |
michael@0 | 388 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 389 | for (let [group, name, ] in this._privModeStorage) { |
michael@0 | 390 | if (name === aName) { |
michael@0 | 391 | this._privModeStorage.remove(group, aName); |
michael@0 | 392 | this._notifyPrefRemoved(group, aName); |
michael@0 | 393 | } |
michael@0 | 394 | } |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | var settingID = this._selectSettingID(aName); |
michael@0 | 398 | if (!settingID) |
michael@0 | 399 | return; |
michael@0 | 400 | |
michael@0 | 401 | var selectGroupsStmt = this._dbCreateStatement( |
michael@0 | 402 | "SELECT groups.id AS groupID, groups.name AS groupName " + |
michael@0 | 403 | "FROM prefs " + |
michael@0 | 404 | "JOIN groups ON prefs.groupID = groups.id " + |
michael@0 | 405 | "WHERE prefs.settingID = :setting " |
michael@0 | 406 | ); |
michael@0 | 407 | |
michael@0 | 408 | var groupNames = []; |
michael@0 | 409 | var groupIDs = []; |
michael@0 | 410 | try { |
michael@0 | 411 | selectGroupsStmt.params.setting = settingID; |
michael@0 | 412 | |
michael@0 | 413 | while (selectGroupsStmt.executeStep()) { |
michael@0 | 414 | groupIDs.push(selectGroupsStmt.row["groupID"]); |
michael@0 | 415 | groupNames.push(selectGroupsStmt.row["groupName"]); |
michael@0 | 416 | } |
michael@0 | 417 | } |
michael@0 | 418 | finally { |
michael@0 | 419 | selectGroupsStmt.reset(); |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | if (this.hasPref(null, aName)) { |
michael@0 | 423 | groupNames.push(null); |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE settingID = " + settingID); |
michael@0 | 427 | this._dbConnection.executeSimpleSQL("DELETE FROM settings WHERE id = " + settingID); |
michael@0 | 428 | |
michael@0 | 429 | for (var i = 0; i < groupNames.length; i++) { |
michael@0 | 430 | this._cache.remove(groupNames[i], aName); |
michael@0 | 431 | if (groupNames[i]) // ie. not null, which will be last (and i == groupIDs.length) |
michael@0 | 432 | this._deleteGroupIfUnused(groupIDs[i]); |
michael@0 | 433 | if (!aContext || !aContext.usePrivateBrowsing) { |
michael@0 | 434 | this._notifyPrefRemoved(groupNames[i], aName); |
michael@0 | 435 | } |
michael@0 | 436 | } |
michael@0 | 437 | }, |
michael@0 | 438 | |
michael@0 | 439 | getPrefs: function ContentPrefService_getPrefs(aGroup, aContext) { |
michael@0 | 440 | warnDeprecated(); |
michael@0 | 441 | |
michael@0 | 442 | var group = this._parseGroupParam(aGroup); |
michael@0 | 443 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 444 | let prefs = Cc["@mozilla.org/hash-property-bag;1"]. |
michael@0 | 445 | createInstance(Ci.nsIWritablePropertyBag); |
michael@0 | 446 | for (let [sgroup, sname, sval] in this._privModeStorage) { |
michael@0 | 447 | if (sgroup === group) |
michael@0 | 448 | prefs.setProperty(sname, sval); |
michael@0 | 449 | } |
michael@0 | 450 | return prefs; |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | if (group == null) |
michael@0 | 454 | return this._selectGlobalPrefs(); |
michael@0 | 455 | return this._selectPrefs(group); |
michael@0 | 456 | }, |
michael@0 | 457 | |
michael@0 | 458 | getPrefsByName: function ContentPrefService_getPrefsByName(aName, aContext) { |
michael@0 | 459 | warnDeprecated(); |
michael@0 | 460 | |
michael@0 | 461 | if (!aName) |
michael@0 | 462 | throw Components.Exception("aName cannot be null or an empty string", |
michael@0 | 463 | Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 464 | |
michael@0 | 465 | if (aContext && aContext.usePrivateBrowsing) { |
michael@0 | 466 | let prefs = Cc["@mozilla.org/hash-property-bag;1"]. |
michael@0 | 467 | createInstance(Ci.nsIWritablePropertyBag); |
michael@0 | 468 | for (let [sgroup, sname, sval] in this._privModeStorage) { |
michael@0 | 469 | if (sname === aName) |
michael@0 | 470 | prefs.setProperty(sgroup, sval); |
michael@0 | 471 | } |
michael@0 | 472 | return prefs; |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | return this._selectPrefsByName(aName); |
michael@0 | 476 | }, |
michael@0 | 477 | |
michael@0 | 478 | // A hash of arrays of observers, indexed by setting name. |
michael@0 | 479 | _observers: {}, |
michael@0 | 480 | |
michael@0 | 481 | // An array of generic observers, which observe all settings. |
michael@0 | 482 | _genericObservers: [], |
michael@0 | 483 | |
michael@0 | 484 | addObserver: function ContentPrefService_addObserver(aName, aObserver) { |
michael@0 | 485 | warnDeprecated(); |
michael@0 | 486 | this._addObserver.apply(this, arguments); |
michael@0 | 487 | }, |
michael@0 | 488 | |
michael@0 | 489 | _addObserver: function ContentPrefService__addObserver(aName, aObserver) { |
michael@0 | 490 | var observers; |
michael@0 | 491 | if (aName) { |
michael@0 | 492 | if (!this._observers[aName]) |
michael@0 | 493 | this._observers[aName] = []; |
michael@0 | 494 | observers = this._observers[aName]; |
michael@0 | 495 | } |
michael@0 | 496 | else |
michael@0 | 497 | observers = this._genericObservers; |
michael@0 | 498 | |
michael@0 | 499 | if (observers.indexOf(aObserver) == -1) |
michael@0 | 500 | observers.push(aObserver); |
michael@0 | 501 | }, |
michael@0 | 502 | |
michael@0 | 503 | removeObserver: function ContentPrefService_removeObserver(aName, aObserver) { |
michael@0 | 504 | warnDeprecated(); |
michael@0 | 505 | this._removeObserver.apply(this, arguments); |
michael@0 | 506 | }, |
michael@0 | 507 | |
michael@0 | 508 | _removeObserver: function ContentPrefService__removeObserver(aName, aObserver) { |
michael@0 | 509 | var observers; |
michael@0 | 510 | if (aName) { |
michael@0 | 511 | if (!this._observers[aName]) |
michael@0 | 512 | return; |
michael@0 | 513 | observers = this._observers[aName]; |
michael@0 | 514 | } |
michael@0 | 515 | else |
michael@0 | 516 | observers = this._genericObservers; |
michael@0 | 517 | |
michael@0 | 518 | if (observers.indexOf(aObserver) != -1) |
michael@0 | 519 | observers.splice(observers.indexOf(aObserver), 1); |
michael@0 | 520 | }, |
michael@0 | 521 | |
michael@0 | 522 | /** |
michael@0 | 523 | * Construct a list of observers to notify about a change to some setting, |
michael@0 | 524 | * putting setting-specific observers before before generic ones, so observers |
michael@0 | 525 | * that initialize individual settings (like the page style controller) |
michael@0 | 526 | * execute before observers that display multiple settings and depend on them |
michael@0 | 527 | * being initialized first (like the content prefs sidebar). |
michael@0 | 528 | */ |
michael@0 | 529 | _getObservers: function ContentPrefService__getObservers(aName) { |
michael@0 | 530 | var observers = []; |
michael@0 | 531 | |
michael@0 | 532 | if (aName && this._observers[aName]) |
michael@0 | 533 | observers = observers.concat(this._observers[aName]); |
michael@0 | 534 | observers = observers.concat(this._genericObservers); |
michael@0 | 535 | |
michael@0 | 536 | return observers; |
michael@0 | 537 | }, |
michael@0 | 538 | |
michael@0 | 539 | /** |
michael@0 | 540 | * Notify all observers about the removal of a preference. |
michael@0 | 541 | */ |
michael@0 | 542 | _notifyPrefRemoved: function ContentPrefService__notifyPrefRemoved(aGroup, aName) { |
michael@0 | 543 | for each (var observer in this._getObservers(aName)) { |
michael@0 | 544 | try { |
michael@0 | 545 | observer.onContentPrefRemoved(aGroup, aName); |
michael@0 | 546 | } |
michael@0 | 547 | catch(ex) { |
michael@0 | 548 | Cu.reportError(ex); |
michael@0 | 549 | } |
michael@0 | 550 | } |
michael@0 | 551 | }, |
michael@0 | 552 | |
michael@0 | 553 | /** |
michael@0 | 554 | * Notify all observers about a preference change. |
michael@0 | 555 | */ |
michael@0 | 556 | _notifyPrefSet: function ContentPrefService__notifyPrefSet(aGroup, aName, aValue) { |
michael@0 | 557 | for each (var observer in this._getObservers(aName)) { |
michael@0 | 558 | try { |
michael@0 | 559 | observer.onContentPrefSet(aGroup, aName, aValue); |
michael@0 | 560 | } |
michael@0 | 561 | catch(ex) { |
michael@0 | 562 | Cu.reportError(ex); |
michael@0 | 563 | } |
michael@0 | 564 | } |
michael@0 | 565 | }, |
michael@0 | 566 | |
michael@0 | 567 | get grouper() { |
michael@0 | 568 | warnDeprecated(); |
michael@0 | 569 | return this._grouper; |
michael@0 | 570 | }, |
michael@0 | 571 | __grouper: null, |
michael@0 | 572 | get _grouper() { |
michael@0 | 573 | if (!this.__grouper) |
michael@0 | 574 | this.__grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"]. |
michael@0 | 575 | getService(Ci.nsIContentURIGrouper); |
michael@0 | 576 | return this.__grouper; |
michael@0 | 577 | }, |
michael@0 | 578 | |
michael@0 | 579 | get DBConnection() { |
michael@0 | 580 | warnDeprecated(); |
michael@0 | 581 | return this._dbConnection; |
michael@0 | 582 | }, |
michael@0 | 583 | |
michael@0 | 584 | |
michael@0 | 585 | //**************************************************************************// |
michael@0 | 586 | // Data Retrieval & Modification |
michael@0 | 587 | |
michael@0 | 588 | __stmtSelectPref: null, |
michael@0 | 589 | get _stmtSelectPref() { |
michael@0 | 590 | if (!this.__stmtSelectPref) |
michael@0 | 591 | this.__stmtSelectPref = this._dbCreateStatement( |
michael@0 | 592 | "SELECT prefs.value AS value " + |
michael@0 | 593 | "FROM prefs " + |
michael@0 | 594 | "JOIN groups ON prefs.groupID = groups.id " + |
michael@0 | 595 | "JOIN settings ON prefs.settingID = settings.id " + |
michael@0 | 596 | "WHERE groups.name = :group " + |
michael@0 | 597 | "AND settings.name = :setting" |
michael@0 | 598 | ); |
michael@0 | 599 | |
michael@0 | 600 | return this.__stmtSelectPref; |
michael@0 | 601 | }, |
michael@0 | 602 | |
michael@0 | 603 | _scheduleCallback: function(func) { |
michael@0 | 604 | let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); |
michael@0 | 605 | tm.mainThread.dispatch(func, Ci.nsIThread.DISPATCH_NORMAL); |
michael@0 | 606 | }, |
michael@0 | 607 | |
michael@0 | 608 | _selectPref: function ContentPrefService__selectPref(aGroup, aSetting, aCallback) { |
michael@0 | 609 | let value = undefined; |
michael@0 | 610 | if (this._cache.has(aGroup, aSetting)) { |
michael@0 | 611 | value = this._cache.get(aGroup, aSetting); |
michael@0 | 612 | if (aCallback) { |
michael@0 | 613 | this._scheduleCallback(function(){aCallback.onResult(value);}); |
michael@0 | 614 | return; |
michael@0 | 615 | } |
michael@0 | 616 | return value; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | try { |
michael@0 | 620 | this._stmtSelectPref.params.group = aGroup; |
michael@0 | 621 | this._stmtSelectPref.params.setting = aSetting; |
michael@0 | 622 | |
michael@0 | 623 | if (aCallback) { |
michael@0 | 624 | let cache = this._cache; |
michael@0 | 625 | new AsyncStatement(this._stmtSelectPref).execute({onResult: function(aResult) { |
michael@0 | 626 | cache.set(aGroup, aSetting, aResult); |
michael@0 | 627 | aCallback.onResult(aResult); |
michael@0 | 628 | }}); |
michael@0 | 629 | } |
michael@0 | 630 | else { |
michael@0 | 631 | if (this._stmtSelectPref.executeStep()) { |
michael@0 | 632 | value = this._stmtSelectPref.row["value"]; |
michael@0 | 633 | } |
michael@0 | 634 | this._cache.set(aGroup, aSetting, value); |
michael@0 | 635 | } |
michael@0 | 636 | } |
michael@0 | 637 | finally { |
michael@0 | 638 | this._stmtSelectPref.reset(); |
michael@0 | 639 | } |
michael@0 | 640 | |
michael@0 | 641 | return value; |
michael@0 | 642 | }, |
michael@0 | 643 | |
michael@0 | 644 | __stmtSelectGlobalPref: null, |
michael@0 | 645 | get _stmtSelectGlobalPref() { |
michael@0 | 646 | if (!this.__stmtSelectGlobalPref) |
michael@0 | 647 | this.__stmtSelectGlobalPref = this._dbCreateStatement( |
michael@0 | 648 | "SELECT prefs.value AS value " + |
michael@0 | 649 | "FROM prefs " + |
michael@0 | 650 | "JOIN settings ON prefs.settingID = settings.id " + |
michael@0 | 651 | "WHERE prefs.groupID IS NULL " + |
michael@0 | 652 | "AND settings.name = :name" |
michael@0 | 653 | ); |
michael@0 | 654 | |
michael@0 | 655 | return this.__stmtSelectGlobalPref; |
michael@0 | 656 | }, |
michael@0 | 657 | |
michael@0 | 658 | _selectGlobalPref: function ContentPrefService__selectGlobalPref(aName, aCallback) { |
michael@0 | 659 | let value = undefined; |
michael@0 | 660 | if (this._cache.has(null, aName)) { |
michael@0 | 661 | value = this._cache.get(null, aName); |
michael@0 | 662 | if (aCallback) { |
michael@0 | 663 | this._scheduleCallback(function(){aCallback.onResult(value);}); |
michael@0 | 664 | return; |
michael@0 | 665 | } |
michael@0 | 666 | return value; |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | try { |
michael@0 | 670 | this._stmtSelectGlobalPref.params.name = aName; |
michael@0 | 671 | |
michael@0 | 672 | if (aCallback) { |
michael@0 | 673 | let cache = this._cache; |
michael@0 | 674 | new AsyncStatement(this._stmtSelectGlobalPref).execute({onResult: function(aResult) { |
michael@0 | 675 | cache.set(null, aName, aResult); |
michael@0 | 676 | aCallback.onResult(aResult); |
michael@0 | 677 | }}); |
michael@0 | 678 | } |
michael@0 | 679 | else { |
michael@0 | 680 | if (this._stmtSelectGlobalPref.executeStep()) { |
michael@0 | 681 | value = this._stmtSelectGlobalPref.row["value"]; |
michael@0 | 682 | } |
michael@0 | 683 | this._cache.set(null, aName, value); |
michael@0 | 684 | } |
michael@0 | 685 | } |
michael@0 | 686 | finally { |
michael@0 | 687 | this._stmtSelectGlobalPref.reset(); |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | return value; |
michael@0 | 691 | }, |
michael@0 | 692 | |
michael@0 | 693 | __stmtSelectGroupID: null, |
michael@0 | 694 | get _stmtSelectGroupID() { |
michael@0 | 695 | if (!this.__stmtSelectGroupID) |
michael@0 | 696 | this.__stmtSelectGroupID = this._dbCreateStatement( |
michael@0 | 697 | "SELECT groups.id AS id " + |
michael@0 | 698 | "FROM groups " + |
michael@0 | 699 | "WHERE groups.name = :name " |
michael@0 | 700 | ); |
michael@0 | 701 | |
michael@0 | 702 | return this.__stmtSelectGroupID; |
michael@0 | 703 | }, |
michael@0 | 704 | |
michael@0 | 705 | _selectGroupID: function ContentPrefService__selectGroupID(aName) { |
michael@0 | 706 | var id; |
michael@0 | 707 | |
michael@0 | 708 | try { |
michael@0 | 709 | this._stmtSelectGroupID.params.name = aName; |
michael@0 | 710 | |
michael@0 | 711 | if (this._stmtSelectGroupID.executeStep()) |
michael@0 | 712 | id = this._stmtSelectGroupID.row["id"]; |
michael@0 | 713 | } |
michael@0 | 714 | finally { |
michael@0 | 715 | this._stmtSelectGroupID.reset(); |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | return id; |
michael@0 | 719 | }, |
michael@0 | 720 | |
michael@0 | 721 | __stmtInsertGroup: null, |
michael@0 | 722 | get _stmtInsertGroup() { |
michael@0 | 723 | if (!this.__stmtInsertGroup) |
michael@0 | 724 | this.__stmtInsertGroup = this._dbCreateStatement( |
michael@0 | 725 | "INSERT INTO groups (name) VALUES (:name)" |
michael@0 | 726 | ); |
michael@0 | 727 | |
michael@0 | 728 | return this.__stmtInsertGroup; |
michael@0 | 729 | }, |
michael@0 | 730 | |
michael@0 | 731 | _insertGroup: function ContentPrefService__insertGroup(aName) { |
michael@0 | 732 | this._stmtInsertGroup.params.name = aName; |
michael@0 | 733 | this._stmtInsertGroup.execute(); |
michael@0 | 734 | return this._dbConnection.lastInsertRowID; |
michael@0 | 735 | }, |
michael@0 | 736 | |
michael@0 | 737 | __stmtSelectSettingID: null, |
michael@0 | 738 | get _stmtSelectSettingID() { |
michael@0 | 739 | if (!this.__stmtSelectSettingID) |
michael@0 | 740 | this.__stmtSelectSettingID = this._dbCreateStatement( |
michael@0 | 741 | "SELECT id FROM settings WHERE name = :name" |
michael@0 | 742 | ); |
michael@0 | 743 | |
michael@0 | 744 | return this.__stmtSelectSettingID; |
michael@0 | 745 | }, |
michael@0 | 746 | |
michael@0 | 747 | _selectSettingID: function ContentPrefService__selectSettingID(aName) { |
michael@0 | 748 | var id; |
michael@0 | 749 | |
michael@0 | 750 | try { |
michael@0 | 751 | this._stmtSelectSettingID.params.name = aName; |
michael@0 | 752 | |
michael@0 | 753 | if (this._stmtSelectSettingID.executeStep()) |
michael@0 | 754 | id = this._stmtSelectSettingID.row["id"]; |
michael@0 | 755 | } |
michael@0 | 756 | finally { |
michael@0 | 757 | this._stmtSelectSettingID.reset(); |
michael@0 | 758 | } |
michael@0 | 759 | |
michael@0 | 760 | return id; |
michael@0 | 761 | }, |
michael@0 | 762 | |
michael@0 | 763 | __stmtInsertSetting: null, |
michael@0 | 764 | get _stmtInsertSetting() { |
michael@0 | 765 | if (!this.__stmtInsertSetting) |
michael@0 | 766 | this.__stmtInsertSetting = this._dbCreateStatement( |
michael@0 | 767 | "INSERT INTO settings (name) VALUES (:name)" |
michael@0 | 768 | ); |
michael@0 | 769 | |
michael@0 | 770 | return this.__stmtInsertSetting; |
michael@0 | 771 | }, |
michael@0 | 772 | |
michael@0 | 773 | _insertSetting: function ContentPrefService__insertSetting(aName) { |
michael@0 | 774 | this._stmtInsertSetting.params.name = aName; |
michael@0 | 775 | this._stmtInsertSetting.execute(); |
michael@0 | 776 | return this._dbConnection.lastInsertRowID; |
michael@0 | 777 | }, |
michael@0 | 778 | |
michael@0 | 779 | __stmtSelectPrefID: null, |
michael@0 | 780 | get _stmtSelectPrefID() { |
michael@0 | 781 | if (!this.__stmtSelectPrefID) |
michael@0 | 782 | this.__stmtSelectPrefID = this._dbCreateStatement( |
michael@0 | 783 | "SELECT id FROM prefs WHERE groupID = :groupID AND settingID = :settingID" |
michael@0 | 784 | ); |
michael@0 | 785 | |
michael@0 | 786 | return this.__stmtSelectPrefID; |
michael@0 | 787 | }, |
michael@0 | 788 | |
michael@0 | 789 | _selectPrefID: function ContentPrefService__selectPrefID(aGroupID, aSettingID) { |
michael@0 | 790 | var id; |
michael@0 | 791 | |
michael@0 | 792 | try { |
michael@0 | 793 | this._stmtSelectPrefID.params.groupID = aGroupID; |
michael@0 | 794 | this._stmtSelectPrefID.params.settingID = aSettingID; |
michael@0 | 795 | |
michael@0 | 796 | if (this._stmtSelectPrefID.executeStep()) |
michael@0 | 797 | id = this._stmtSelectPrefID.row["id"]; |
michael@0 | 798 | } |
michael@0 | 799 | finally { |
michael@0 | 800 | this._stmtSelectPrefID.reset(); |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | return id; |
michael@0 | 804 | }, |
michael@0 | 805 | |
michael@0 | 806 | __stmtSelectGlobalPrefID: null, |
michael@0 | 807 | get _stmtSelectGlobalPrefID() { |
michael@0 | 808 | if (!this.__stmtSelectGlobalPrefID) |
michael@0 | 809 | this.__stmtSelectGlobalPrefID = this._dbCreateStatement( |
michael@0 | 810 | "SELECT id FROM prefs WHERE groupID IS NULL AND settingID = :settingID" |
michael@0 | 811 | ); |
michael@0 | 812 | |
michael@0 | 813 | return this.__stmtSelectGlobalPrefID; |
michael@0 | 814 | }, |
michael@0 | 815 | |
michael@0 | 816 | _selectGlobalPrefID: function ContentPrefService__selectGlobalPrefID(aSettingID) { |
michael@0 | 817 | var id; |
michael@0 | 818 | |
michael@0 | 819 | try { |
michael@0 | 820 | this._stmtSelectGlobalPrefID.params.settingID = aSettingID; |
michael@0 | 821 | |
michael@0 | 822 | if (this._stmtSelectGlobalPrefID.executeStep()) |
michael@0 | 823 | id = this._stmtSelectGlobalPrefID.row["id"]; |
michael@0 | 824 | } |
michael@0 | 825 | finally { |
michael@0 | 826 | this._stmtSelectGlobalPrefID.reset(); |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | return id; |
michael@0 | 830 | }, |
michael@0 | 831 | |
michael@0 | 832 | __stmtInsertPref: null, |
michael@0 | 833 | get _stmtInsertPref() { |
michael@0 | 834 | if (!this.__stmtInsertPref) |
michael@0 | 835 | this.__stmtInsertPref = this._dbCreateStatement( |
michael@0 | 836 | "INSERT INTO prefs (groupID, settingID, value) " + |
michael@0 | 837 | "VALUES (:groupID, :settingID, :value)" |
michael@0 | 838 | ); |
michael@0 | 839 | |
michael@0 | 840 | return this.__stmtInsertPref; |
michael@0 | 841 | }, |
michael@0 | 842 | |
michael@0 | 843 | _insertPref: function ContentPrefService__insertPref(aGroupID, aSettingID, aValue) { |
michael@0 | 844 | this._stmtInsertPref.params.groupID = aGroupID; |
michael@0 | 845 | this._stmtInsertPref.params.settingID = aSettingID; |
michael@0 | 846 | this._stmtInsertPref.params.value = aValue; |
michael@0 | 847 | this._stmtInsertPref.execute(); |
michael@0 | 848 | return this._dbConnection.lastInsertRowID; |
michael@0 | 849 | }, |
michael@0 | 850 | |
michael@0 | 851 | __stmtUpdatePref: null, |
michael@0 | 852 | get _stmtUpdatePref() { |
michael@0 | 853 | if (!this.__stmtUpdatePref) |
michael@0 | 854 | this.__stmtUpdatePref = this._dbCreateStatement( |
michael@0 | 855 | "UPDATE prefs SET value = :value WHERE id = :id" |
michael@0 | 856 | ); |
michael@0 | 857 | |
michael@0 | 858 | return this.__stmtUpdatePref; |
michael@0 | 859 | }, |
michael@0 | 860 | |
michael@0 | 861 | _updatePref: function ContentPrefService__updatePref(aPrefID, aValue) { |
michael@0 | 862 | this._stmtUpdatePref.params.id = aPrefID; |
michael@0 | 863 | this._stmtUpdatePref.params.value = aValue; |
michael@0 | 864 | this._stmtUpdatePref.execute(); |
michael@0 | 865 | }, |
michael@0 | 866 | |
michael@0 | 867 | __stmtDeletePref: null, |
michael@0 | 868 | get _stmtDeletePref() { |
michael@0 | 869 | if (!this.__stmtDeletePref) |
michael@0 | 870 | this.__stmtDeletePref = this._dbCreateStatement( |
michael@0 | 871 | "DELETE FROM prefs WHERE id = :id" |
michael@0 | 872 | ); |
michael@0 | 873 | |
michael@0 | 874 | return this.__stmtDeletePref; |
michael@0 | 875 | }, |
michael@0 | 876 | |
michael@0 | 877 | _deletePref: function ContentPrefService__deletePref(aPrefID) { |
michael@0 | 878 | this._stmtDeletePref.params.id = aPrefID; |
michael@0 | 879 | this._stmtDeletePref.execute(); |
michael@0 | 880 | }, |
michael@0 | 881 | |
michael@0 | 882 | __stmtDeleteSettingIfUnused: null, |
michael@0 | 883 | get _stmtDeleteSettingIfUnused() { |
michael@0 | 884 | if (!this.__stmtDeleteSettingIfUnused) |
michael@0 | 885 | this.__stmtDeleteSettingIfUnused = this._dbCreateStatement( |
michael@0 | 886 | "DELETE FROM settings WHERE id = :id " + |
michael@0 | 887 | "AND id NOT IN (SELECT DISTINCT settingID FROM prefs)" |
michael@0 | 888 | ); |
michael@0 | 889 | |
michael@0 | 890 | return this.__stmtDeleteSettingIfUnused; |
michael@0 | 891 | }, |
michael@0 | 892 | |
michael@0 | 893 | _deleteSettingIfUnused: function ContentPrefService__deleteSettingIfUnused(aSettingID) { |
michael@0 | 894 | this._stmtDeleteSettingIfUnused.params.id = aSettingID; |
michael@0 | 895 | this._stmtDeleteSettingIfUnused.execute(); |
michael@0 | 896 | }, |
michael@0 | 897 | |
michael@0 | 898 | __stmtDeleteGroupIfUnused: null, |
michael@0 | 899 | get _stmtDeleteGroupIfUnused() { |
michael@0 | 900 | if (!this.__stmtDeleteGroupIfUnused) |
michael@0 | 901 | this.__stmtDeleteGroupIfUnused = this._dbCreateStatement( |
michael@0 | 902 | "DELETE FROM groups WHERE id = :id " + |
michael@0 | 903 | "AND id NOT IN (SELECT DISTINCT groupID FROM prefs)" |
michael@0 | 904 | ); |
michael@0 | 905 | |
michael@0 | 906 | return this.__stmtDeleteGroupIfUnused; |
michael@0 | 907 | }, |
michael@0 | 908 | |
michael@0 | 909 | _deleteGroupIfUnused: function ContentPrefService__deleteGroupIfUnused(aGroupID) { |
michael@0 | 910 | this._stmtDeleteGroupIfUnused.params.id = aGroupID; |
michael@0 | 911 | this._stmtDeleteGroupIfUnused.execute(); |
michael@0 | 912 | }, |
michael@0 | 913 | |
michael@0 | 914 | __stmtSelectPrefs: null, |
michael@0 | 915 | get _stmtSelectPrefs() { |
michael@0 | 916 | if (!this.__stmtSelectPrefs) |
michael@0 | 917 | this.__stmtSelectPrefs = this._dbCreateStatement( |
michael@0 | 918 | "SELECT settings.name AS name, prefs.value AS value " + |
michael@0 | 919 | "FROM prefs " + |
michael@0 | 920 | "JOIN groups ON prefs.groupID = groups.id " + |
michael@0 | 921 | "JOIN settings ON prefs.settingID = settings.id " + |
michael@0 | 922 | "WHERE groups.name = :group " |
michael@0 | 923 | ); |
michael@0 | 924 | |
michael@0 | 925 | return this.__stmtSelectPrefs; |
michael@0 | 926 | }, |
michael@0 | 927 | |
michael@0 | 928 | _selectPrefs: function ContentPrefService__selectPrefs(aGroup) { |
michael@0 | 929 | var prefs = Cc["@mozilla.org/hash-property-bag;1"]. |
michael@0 | 930 | createInstance(Ci.nsIWritablePropertyBag); |
michael@0 | 931 | |
michael@0 | 932 | try { |
michael@0 | 933 | this._stmtSelectPrefs.params.group = aGroup; |
michael@0 | 934 | |
michael@0 | 935 | while (this._stmtSelectPrefs.executeStep()) |
michael@0 | 936 | prefs.setProperty(this._stmtSelectPrefs.row["name"], |
michael@0 | 937 | this._stmtSelectPrefs.row["value"]); |
michael@0 | 938 | } |
michael@0 | 939 | finally { |
michael@0 | 940 | this._stmtSelectPrefs.reset(); |
michael@0 | 941 | } |
michael@0 | 942 | |
michael@0 | 943 | return prefs; |
michael@0 | 944 | }, |
michael@0 | 945 | |
michael@0 | 946 | __stmtSelectGlobalPrefs: null, |
michael@0 | 947 | get _stmtSelectGlobalPrefs() { |
michael@0 | 948 | if (!this.__stmtSelectGlobalPrefs) |
michael@0 | 949 | this.__stmtSelectGlobalPrefs = this._dbCreateStatement( |
michael@0 | 950 | "SELECT settings.name AS name, prefs.value AS value " + |
michael@0 | 951 | "FROM prefs " + |
michael@0 | 952 | "JOIN settings ON prefs.settingID = settings.id " + |
michael@0 | 953 | "WHERE prefs.groupID IS NULL" |
michael@0 | 954 | ); |
michael@0 | 955 | |
michael@0 | 956 | return this.__stmtSelectGlobalPrefs; |
michael@0 | 957 | }, |
michael@0 | 958 | |
michael@0 | 959 | _selectGlobalPrefs: function ContentPrefService__selectGlobalPrefs() { |
michael@0 | 960 | var prefs = Cc["@mozilla.org/hash-property-bag;1"]. |
michael@0 | 961 | createInstance(Ci.nsIWritablePropertyBag); |
michael@0 | 962 | |
michael@0 | 963 | try { |
michael@0 | 964 | while (this._stmtSelectGlobalPrefs.executeStep()) |
michael@0 | 965 | prefs.setProperty(this._stmtSelectGlobalPrefs.row["name"], |
michael@0 | 966 | this._stmtSelectGlobalPrefs.row["value"]); |
michael@0 | 967 | } |
michael@0 | 968 | finally { |
michael@0 | 969 | this._stmtSelectGlobalPrefs.reset(); |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | return prefs; |
michael@0 | 973 | }, |
michael@0 | 974 | |
michael@0 | 975 | __stmtSelectPrefsByName: null, |
michael@0 | 976 | get _stmtSelectPrefsByName() { |
michael@0 | 977 | if (!this.__stmtSelectPrefsByName) |
michael@0 | 978 | this.__stmtSelectPrefsByName = this._dbCreateStatement( |
michael@0 | 979 | "SELECT groups.name AS groupName, prefs.value AS value " + |
michael@0 | 980 | "FROM prefs " + |
michael@0 | 981 | "JOIN groups ON prefs.groupID = groups.id " + |
michael@0 | 982 | "JOIN settings ON prefs.settingID = settings.id " + |
michael@0 | 983 | "WHERE settings.name = :setting " |
michael@0 | 984 | ); |
michael@0 | 985 | |
michael@0 | 986 | return this.__stmtSelectPrefsByName; |
michael@0 | 987 | }, |
michael@0 | 988 | |
michael@0 | 989 | _selectPrefsByName: function ContentPrefService__selectPrefsByName(aName) { |
michael@0 | 990 | var prefs = Cc["@mozilla.org/hash-property-bag;1"]. |
michael@0 | 991 | createInstance(Ci.nsIWritablePropertyBag); |
michael@0 | 992 | |
michael@0 | 993 | try { |
michael@0 | 994 | this._stmtSelectPrefsByName.params.setting = aName; |
michael@0 | 995 | |
michael@0 | 996 | while (this._stmtSelectPrefsByName.executeStep()) |
michael@0 | 997 | prefs.setProperty(this._stmtSelectPrefsByName.row["groupName"], |
michael@0 | 998 | this._stmtSelectPrefsByName.row["value"]); |
michael@0 | 999 | } |
michael@0 | 1000 | finally { |
michael@0 | 1001 | this._stmtSelectPrefsByName.reset(); |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | var global = this._selectGlobalPref(aName); |
michael@0 | 1005 | if (typeof global != "undefined") { |
michael@0 | 1006 | prefs.setProperty(null, global); |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | return prefs; |
michael@0 | 1010 | }, |
michael@0 | 1011 | |
michael@0 | 1012 | |
michael@0 | 1013 | //**************************************************************************// |
michael@0 | 1014 | // Database Creation & Access |
michael@0 | 1015 | |
michael@0 | 1016 | _dbVersion: 3, |
michael@0 | 1017 | |
michael@0 | 1018 | _dbSchema: { |
michael@0 | 1019 | tables: { |
michael@0 | 1020 | groups: "id INTEGER PRIMARY KEY, \ |
michael@0 | 1021 | name TEXT NOT NULL", |
michael@0 | 1022 | |
michael@0 | 1023 | settings: "id INTEGER PRIMARY KEY, \ |
michael@0 | 1024 | name TEXT NOT NULL", |
michael@0 | 1025 | |
michael@0 | 1026 | prefs: "id INTEGER PRIMARY KEY, \ |
michael@0 | 1027 | groupID INTEGER REFERENCES groups(id), \ |
michael@0 | 1028 | settingID INTEGER NOT NULL REFERENCES settings(id), \ |
michael@0 | 1029 | value BLOB" |
michael@0 | 1030 | }, |
michael@0 | 1031 | indices: { |
michael@0 | 1032 | groups_idx: { |
michael@0 | 1033 | table: "groups", |
michael@0 | 1034 | columns: ["name"] |
michael@0 | 1035 | }, |
michael@0 | 1036 | settings_idx: { |
michael@0 | 1037 | table: "settings", |
michael@0 | 1038 | columns: ["name"] |
michael@0 | 1039 | }, |
michael@0 | 1040 | prefs_idx: { |
michael@0 | 1041 | table: "prefs", |
michael@0 | 1042 | columns: ["groupID", "settingID"] |
michael@0 | 1043 | } |
michael@0 | 1044 | } |
michael@0 | 1045 | }, |
michael@0 | 1046 | |
michael@0 | 1047 | _dbConnection: null, |
michael@0 | 1048 | |
michael@0 | 1049 | _dbCreateStatement: function ContentPrefService__dbCreateStatement(aSQLString) { |
michael@0 | 1050 | try { |
michael@0 | 1051 | var statement = this._dbConnection.createStatement(aSQLString); |
michael@0 | 1052 | } |
michael@0 | 1053 | catch(ex) { |
michael@0 | 1054 | Cu.reportError("error creating statement " + aSQLString + ": " + |
michael@0 | 1055 | this._dbConnection.lastError + " - " + |
michael@0 | 1056 | this._dbConnection.lastErrorString); |
michael@0 | 1057 | throw ex; |
michael@0 | 1058 | } |
michael@0 | 1059 | |
michael@0 | 1060 | return statement; |
michael@0 | 1061 | }, |
michael@0 | 1062 | |
michael@0 | 1063 | // _dbInit and the methods it calls (_dbCreate, _dbMigrate, and version- |
michael@0 | 1064 | // specific migration methods) must be careful not to call any method |
michael@0 | 1065 | // of the service that assumes the database connection has already been |
michael@0 | 1066 | // initialized, since it won't be initialized until at the end of _dbInit. |
michael@0 | 1067 | |
michael@0 | 1068 | _dbInit: function ContentPrefService__dbInit() { |
michael@0 | 1069 | var dirService = Cc["@mozilla.org/file/directory_service;1"]. |
michael@0 | 1070 | getService(Ci.nsIProperties); |
michael@0 | 1071 | var dbFile = dirService.get("ProfD", Ci.nsIFile); |
michael@0 | 1072 | dbFile.append("content-prefs.sqlite"); |
michael@0 | 1073 | |
michael@0 | 1074 | var dbService = Cc["@mozilla.org/storage/service;1"]. |
michael@0 | 1075 | getService(Ci.mozIStorageService); |
michael@0 | 1076 | |
michael@0 | 1077 | var dbConnection; |
michael@0 | 1078 | |
michael@0 | 1079 | if (true || !dbFile.exists()) |
michael@0 | 1080 | dbConnection = this._dbCreate(dbService, dbFile); |
michael@0 | 1081 | else { |
michael@0 | 1082 | try { |
michael@0 | 1083 | dbConnection = dbService.openDatabase(dbFile); |
michael@0 | 1084 | } |
michael@0 | 1085 | // If the connection isn't ready after we open the database, that means |
michael@0 | 1086 | // the database has been corrupted, so we back it up and then recreate it. |
michael@0 | 1087 | catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) { |
michael@0 | 1088 | dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, |
michael@0 | 1089 | dbConnection); |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | // Get the version of the schema in the file. |
michael@0 | 1093 | var version = dbConnection.schemaVersion; |
michael@0 | 1094 | |
michael@0 | 1095 | // Try to migrate the schema in the database to the current schema used by |
michael@0 | 1096 | // the service. If migration fails, back up the database and recreate it. |
michael@0 | 1097 | if (version != this._dbVersion) { |
michael@0 | 1098 | try { |
michael@0 | 1099 | this._dbMigrate(dbConnection, version, this._dbVersion); |
michael@0 | 1100 | } |
michael@0 | 1101 | catch(ex) { |
michael@0 | 1102 | Cu.reportError("error migrating DB: " + ex + "; backing up and recreating"); |
michael@0 | 1103 | dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, dbConnection); |
michael@0 | 1104 | } |
michael@0 | 1105 | } |
michael@0 | 1106 | } |
michael@0 | 1107 | |
michael@0 | 1108 | // Turn off disk synchronization checking to reduce disk churn and speed up |
michael@0 | 1109 | // operations when prefs are changed rapidly (such as when a user repeatedly |
michael@0 | 1110 | // changes the value of the browser zoom setting for a site). |
michael@0 | 1111 | // |
michael@0 | 1112 | // Note: this could cause database corruption if the OS crashes or machine |
michael@0 | 1113 | // loses power before the data gets written to disk, but this is considered |
michael@0 | 1114 | // a reasonable risk for the not-so-critical data stored in this database. |
michael@0 | 1115 | // |
michael@0 | 1116 | // If you really don't want to take this risk, however, just set the |
michael@0 | 1117 | // toolkit.storage.synchronous pref to 1 (NORMAL synchronization) or 2 |
michael@0 | 1118 | // (FULL synchronization), in which case mozStorageConnection::Initialize |
michael@0 | 1119 | // will use that value, and we won't override it here. |
michael@0 | 1120 | if (!this._prefSvc.prefHasUserValue("toolkit.storage.synchronous")) |
michael@0 | 1121 | dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF"); |
michael@0 | 1122 | |
michael@0 | 1123 | this._dbConnection = dbConnection; |
michael@0 | 1124 | }, |
michael@0 | 1125 | |
michael@0 | 1126 | _dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) { |
michael@0 | 1127 | var dbConnection = aDBService.openSpecialDatabase("memory"); |
michael@0 | 1128 | |
michael@0 | 1129 | try { |
michael@0 | 1130 | this._dbCreateSchema(dbConnection); |
michael@0 | 1131 | dbConnection.schemaVersion = this._dbVersion; |
michael@0 | 1132 | } |
michael@0 | 1133 | catch(ex) { |
michael@0 | 1134 | // If we failed to create the database (perhaps because the disk ran out |
michael@0 | 1135 | // of space), then remove the database file so we don't leave it in some |
michael@0 | 1136 | // half-created state from which we won't know how to recover. |
michael@0 | 1137 | dbConnection.close(); |
michael@0 | 1138 | aDBFile.remove(false); |
michael@0 | 1139 | throw ex; |
michael@0 | 1140 | } |
michael@0 | 1141 | |
michael@0 | 1142 | return dbConnection; |
michael@0 | 1143 | }, |
michael@0 | 1144 | |
michael@0 | 1145 | _dbCreateSchema: function ContentPrefService__dbCreateSchema(aDBConnection) { |
michael@0 | 1146 | this._dbCreateTables(aDBConnection); |
michael@0 | 1147 | this._dbCreateIndices(aDBConnection); |
michael@0 | 1148 | }, |
michael@0 | 1149 | |
michael@0 | 1150 | _dbCreateTables: function ContentPrefService__dbCreateTables(aDBConnection) { |
michael@0 | 1151 | for (let name in this._dbSchema.tables) |
michael@0 | 1152 | aDBConnection.createTable(name, this._dbSchema.tables[name]); |
michael@0 | 1153 | }, |
michael@0 | 1154 | |
michael@0 | 1155 | _dbCreateIndices: function ContentPrefService__dbCreateIndices(aDBConnection) { |
michael@0 | 1156 | for (let name in this._dbSchema.indices) { |
michael@0 | 1157 | let index = this._dbSchema.indices[name]; |
michael@0 | 1158 | let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table + |
michael@0 | 1159 | "(" + index.columns.join(", ") + ")"; |
michael@0 | 1160 | aDBConnection.executeSimpleSQL(statement); |
michael@0 | 1161 | } |
michael@0 | 1162 | }, |
michael@0 | 1163 | |
michael@0 | 1164 | _dbBackUpAndRecreate: function ContentPrefService__dbBackUpAndRecreate(aDBService, |
michael@0 | 1165 | aDBFile, |
michael@0 | 1166 | aDBConnection) { |
michael@0 | 1167 | aDBService.backupDatabaseFile(aDBFile, "content-prefs.sqlite.corrupt"); |
michael@0 | 1168 | |
michael@0 | 1169 | // Close the database, ignoring the "already closed" exception, if any. |
michael@0 | 1170 | // It'll be open if we're here because of a migration failure but closed |
michael@0 | 1171 | // if we're here because of database corruption. |
michael@0 | 1172 | try { aDBConnection.close() } catch(ex) {} |
michael@0 | 1173 | |
michael@0 | 1174 | aDBFile.remove(false); |
michael@0 | 1175 | |
michael@0 | 1176 | let dbConnection = this._dbCreate(aDBService, aDBFile); |
michael@0 | 1177 | |
michael@0 | 1178 | return dbConnection; |
michael@0 | 1179 | }, |
michael@0 | 1180 | |
michael@0 | 1181 | _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) { |
michael@0 | 1182 | if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) { |
michael@0 | 1183 | aDBConnection.beginTransaction(); |
michael@0 | 1184 | try { |
michael@0 | 1185 | this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection); |
michael@0 | 1186 | aDBConnection.schemaVersion = aNewVersion; |
michael@0 | 1187 | aDBConnection.commitTransaction(); |
michael@0 | 1188 | } |
michael@0 | 1189 | catch(ex) { |
michael@0 | 1190 | aDBConnection.rollbackTransaction(); |
michael@0 | 1191 | throw ex; |
michael@0 | 1192 | } |
michael@0 | 1193 | } |
michael@0 | 1194 | else |
michael@0 | 1195 | throw("no migrator function from version " + aOldVersion + |
michael@0 | 1196 | " to version " + aNewVersion); |
michael@0 | 1197 | }, |
michael@0 | 1198 | |
michael@0 | 1199 | /** |
michael@0 | 1200 | * If the schema version is 0, that means it was never set, which means |
michael@0 | 1201 | * the database was somehow created without the schema being applied, perhaps |
michael@0 | 1202 | * because the system ran out of disk space (although we check for this |
michael@0 | 1203 | * in _createDB) or because some other code created the database file without |
michael@0 | 1204 | * applying the schema. In any case, recover by simply reapplying the schema. |
michael@0 | 1205 | */ |
michael@0 | 1206 | _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) { |
michael@0 | 1207 | this._dbCreateSchema(aDBConnection); |
michael@0 | 1208 | }, |
michael@0 | 1209 | |
michael@0 | 1210 | _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) { |
michael@0 | 1211 | aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld"); |
michael@0 | 1212 | aDBConnection.createTable("groups", this._dbSchema.tables.groups); |
michael@0 | 1213 | aDBConnection.executeSimpleSQL( |
michael@0 | 1214 | "INSERT INTO groups (id, name) " + |
michael@0 | 1215 | "SELECT id, name FROM groupsOld" |
michael@0 | 1216 | ); |
michael@0 | 1217 | |
michael@0 | 1218 | aDBConnection.executeSimpleSQL("DROP TABLE groupers"); |
michael@0 | 1219 | aDBConnection.executeSimpleSQL("DROP TABLE groupsOld"); |
michael@0 | 1220 | |
michael@0 | 1221 | this._dbCreateIndices(aDBConnection); |
michael@0 | 1222 | }, |
michael@0 | 1223 | |
michael@0 | 1224 | _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) { |
michael@0 | 1225 | this._dbCreateIndices(aDBConnection); |
michael@0 | 1226 | }, |
michael@0 | 1227 | |
michael@0 | 1228 | _parseGroupParam: function ContentPrefService__parseGroupParam(aGroup) { |
michael@0 | 1229 | if (aGroup == null) |
michael@0 | 1230 | return null; |
michael@0 | 1231 | if (aGroup.constructor.name == "String") |
michael@0 | 1232 | return aGroup.toString(); |
michael@0 | 1233 | if (aGroup instanceof Ci.nsIURI) |
michael@0 | 1234 | return this.grouper.group(aGroup); |
michael@0 | 1235 | |
michael@0 | 1236 | throw Components.Exception("aGroup is not a string, nsIURI or null", |
michael@0 | 1237 | Cr.NS_ERROR_ILLEGAL_VALUE); |
michael@0 | 1238 | }, |
michael@0 | 1239 | }; |
michael@0 | 1240 | |
michael@0 | 1241 | function warnDeprecated() { |
michael@0 | 1242 | let Deprecated = Cu.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; |
michael@0 | 1243 | Deprecated.warning("nsIContentPrefService is deprecated. Please use nsIContentPrefService2 instead.", |
michael@0 | 1244 | "https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIContentPrefService2", |
michael@0 | 1245 | Components.stack.caller); |
michael@0 | 1246 | } |
michael@0 | 1247 | |
michael@0 | 1248 | |
michael@0 | 1249 | function HostnameGrouper() {} |
michael@0 | 1250 | |
michael@0 | 1251 | HostnameGrouper.prototype = { |
michael@0 | 1252 | //**************************************************************************// |
michael@0 | 1253 | // XPCOM Plumbing |
michael@0 | 1254 | |
michael@0 | 1255 | classID: Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"), |
michael@0 | 1256 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]), |
michael@0 | 1257 | |
michael@0 | 1258 | //**************************************************************************// |
michael@0 | 1259 | // nsIContentURIGrouper |
michael@0 | 1260 | |
michael@0 | 1261 | group: function HostnameGrouper_group(aURI) { |
michael@0 | 1262 | var group; |
michael@0 | 1263 | |
michael@0 | 1264 | try { |
michael@0 | 1265 | // Accessing the host property of the URI will throw an exception |
michael@0 | 1266 | // if the URI is of a type that doesn't have a host property. |
michael@0 | 1267 | // Otherwise, we manually throw an exception if the host is empty, |
michael@0 | 1268 | // since the effect is the same (we can't derive a group from it). |
michael@0 | 1269 | |
michael@0 | 1270 | group = aURI.host; |
michael@0 | 1271 | if (!group) |
michael@0 | 1272 | throw("can't derive group from host; no host in URI"); |
michael@0 | 1273 | } |
michael@0 | 1274 | catch(ex) { |
michael@0 | 1275 | // If we don't have a host, then use the entire URI (minus the query, |
michael@0 | 1276 | // reference, and hash, if possible) as the group. This means that URIs |
michael@0 | 1277 | // like about:mozilla and about:blank will be considered separate groups, |
michael@0 | 1278 | // but at least they'll be grouped somehow. |
michael@0 | 1279 | |
michael@0 | 1280 | // This also means that each individual file: URL will be considered |
michael@0 | 1281 | // its own group. This seems suboptimal, but so does treating the entire |
michael@0 | 1282 | // file: URL space as a single group (especially if folks start setting |
michael@0 | 1283 | // group-specific capabilities prefs). |
michael@0 | 1284 | |
michael@0 | 1285 | // XXX Is there something better we can do here? |
michael@0 | 1286 | |
michael@0 | 1287 | try { |
michael@0 | 1288 | var url = aURI.QueryInterface(Ci.nsIURL); |
michael@0 | 1289 | group = aURI.prePath + url.filePath; |
michael@0 | 1290 | } |
michael@0 | 1291 | catch(ex) { |
michael@0 | 1292 | group = aURI.spec; |
michael@0 | 1293 | } |
michael@0 | 1294 | } |
michael@0 | 1295 | |
michael@0 | 1296 | return group; |
michael@0 | 1297 | } |
michael@0 | 1298 | }; |
michael@0 | 1299 | |
michael@0 | 1300 | function AsyncStatement(aStatement) { |
michael@0 | 1301 | this.stmt = aStatement; |
michael@0 | 1302 | } |
michael@0 | 1303 | |
michael@0 | 1304 | AsyncStatement.prototype = { |
michael@0 | 1305 | execute: function AsyncStmt_execute(aCallback) { |
michael@0 | 1306 | let stmt = this.stmt; |
michael@0 | 1307 | stmt.executeAsync({ |
michael@0 | 1308 | _callback: aCallback, |
michael@0 | 1309 | _hadResult: false, |
michael@0 | 1310 | handleResult: function(aResult) { |
michael@0 | 1311 | this._hadResult = true; |
michael@0 | 1312 | if (this._callback) { |
michael@0 | 1313 | let row = aResult.getNextRow(); |
michael@0 | 1314 | this._callback.onResult(row.getResultByName("value")); |
michael@0 | 1315 | } |
michael@0 | 1316 | }, |
michael@0 | 1317 | handleCompletion: function(aReason) { |
michael@0 | 1318 | if (!this._hadResult && this._callback && |
michael@0 | 1319 | aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) |
michael@0 | 1320 | this._callback.onResult(undefined); |
michael@0 | 1321 | }, |
michael@0 | 1322 | handleError: function(aError) {} |
michael@0 | 1323 | }); |
michael@0 | 1324 | } |
michael@0 | 1325 | }; |
michael@0 | 1326 | |
michael@0 | 1327 | //****************************************************************************// |
michael@0 | 1328 | // XPCOM Plumbing |
michael@0 | 1329 | |
michael@0 | 1330 | var components = [ContentPrefService, HostnameGrouper]; |
michael@0 | 1331 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); |