Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const DEBUG = false; |
michael@0 | 8 | function debug(s) { |
michael@0 | 9 | if (DEBUG) dump("-*- SettingsManager: " + s + "\n"); |
michael@0 | 10 | } |
michael@0 | 11 | |
michael@0 | 12 | const Cc = Components.classes; |
michael@0 | 13 | const Ci = Components.interfaces; |
michael@0 | 14 | const Cu = Components.utils; |
michael@0 | 15 | |
michael@0 | 16 | Cu.import("resource://gre/modules/SettingsQueue.jsm"); |
michael@0 | 17 | Cu.import("resource://gre/modules/SettingsDB.jsm"); |
michael@0 | 18 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 19 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 20 | Cu.import("resource://gre/modules/ObjectWrapper.jsm"); |
michael@0 | 21 | |
michael@0 | 22 | XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
michael@0 | 23 | "@mozilla.org/childprocessmessagemanager;1", |
michael@0 | 24 | "nsIMessageSender"); |
michael@0 | 25 | XPCOMUtils.defineLazyServiceGetter(this, "mrm", |
michael@0 | 26 | "@mozilla.org/memory-reporter-manager;1", |
michael@0 | 27 | "nsIMemoryReporterManager"); |
michael@0 | 28 | |
michael@0 | 29 | function SettingsLock(aSettingsManager) { |
michael@0 | 30 | this._open = true; |
michael@0 | 31 | this._isBusy = false; |
michael@0 | 32 | this._requests = new Queue(); |
michael@0 | 33 | this._settingsManager = aSettingsManager; |
michael@0 | 34 | this._transaction = null; |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | SettingsLock.prototype = { |
michael@0 | 38 | get closed() { |
michael@0 | 39 | return !this._open; |
michael@0 | 40 | }, |
michael@0 | 41 | |
michael@0 | 42 | _wrap: function _wrap(obj) { |
michael@0 | 43 | return Cu.cloneInto(obj, this._settingsManager._window); |
michael@0 | 44 | }, |
michael@0 | 45 | |
michael@0 | 46 | process: function process() { |
michael@0 | 47 | let lock = this; |
michael@0 | 48 | let store = lock._transaction.objectStore(SETTINGSSTORE_NAME); |
michael@0 | 49 | |
michael@0 | 50 | while (!lock._requests.isEmpty()) { |
michael@0 | 51 | let info = lock._requests.dequeue(); |
michael@0 | 52 | if (DEBUG) debug("info: " + info.intent); |
michael@0 | 53 | let request = info.request; |
michael@0 | 54 | switch (info.intent) { |
michael@0 | 55 | case "clear": |
michael@0 | 56 | let clearReq = store.clear(); |
michael@0 | 57 | clearReq.onsuccess = function() { |
michael@0 | 58 | this._open = true; |
michael@0 | 59 | Services.DOMRequest.fireSuccess(request, 0); |
michael@0 | 60 | this._open = false; |
michael@0 | 61 | }.bind(lock); |
michael@0 | 62 | clearReq.onerror = function() { |
michael@0 | 63 | Services.DOMRequest.fireError(request, 0) |
michael@0 | 64 | }; |
michael@0 | 65 | break; |
michael@0 | 66 | case "set": |
michael@0 | 67 | let keys = Object.getOwnPropertyNames(info.settings); |
michael@0 | 68 | for (let i = 0; i < keys.length; i++) { |
michael@0 | 69 | let key = keys[i]; |
michael@0 | 70 | let last = i === keys.length - 1; |
michael@0 | 71 | if (DEBUG) debug("key: " + key + ", val: " + JSON.stringify(info.settings[key]) + ", type: " + typeof(info.settings[key])); |
michael@0 | 72 | lock._isBusy = true; |
michael@0 | 73 | let checkKeyRequest = store.get(key); |
michael@0 | 74 | |
michael@0 | 75 | checkKeyRequest.onsuccess = function (event) { |
michael@0 | 76 | let defaultValue; |
michael@0 | 77 | let userValue = info.settings[key]; |
michael@0 | 78 | if (event.target.result) { |
michael@0 | 79 | defaultValue = event.target.result.defaultValue; |
michael@0 | 80 | } else { |
michael@0 | 81 | defaultValue = null; |
michael@0 | 82 | if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database.\n"); |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue}; |
michael@0 | 86 | if (DEBUG) debug("store1: " + JSON.stringify(obj)); |
michael@0 | 87 | let setReq = store.put(obj); |
michael@0 | 88 | |
michael@0 | 89 | setReq.onsuccess = function() { |
michael@0 | 90 | lock._isBusy = false; |
michael@0 | 91 | cpmm.sendAsyncMessage("Settings:Changed", { key: key, value: userValue }); |
michael@0 | 92 | if (last && !request.error) { |
michael@0 | 93 | lock._open = true; |
michael@0 | 94 | Services.DOMRequest.fireSuccess(request, 0); |
michael@0 | 95 | lock._open = false; |
michael@0 | 96 | if (!lock._requests.isEmpty()) { |
michael@0 | 97 | lock.process(); |
michael@0 | 98 | } |
michael@0 | 99 | } |
michael@0 | 100 | }; |
michael@0 | 101 | |
michael@0 | 102 | setReq.onerror = function() { |
michael@0 | 103 | if (!request.error) { |
michael@0 | 104 | Services.DOMRequest.fireError(request, setReq.error.name) |
michael@0 | 105 | } |
michael@0 | 106 | }; |
michael@0 | 107 | }; |
michael@0 | 108 | checkKeyRequest.onerror = function(event) { |
michael@0 | 109 | if (!request.error) { |
michael@0 | 110 | Services.DOMRequest.fireError(request, checkKeyRequest.error.name) |
michael@0 | 111 | } |
michael@0 | 112 | }; |
michael@0 | 113 | } |
michael@0 | 114 | break; |
michael@0 | 115 | case "get": |
michael@0 | 116 | let getReq = (info.name === "*") ? store.mozGetAll() |
michael@0 | 117 | : store.mozGetAll(info.name); |
michael@0 | 118 | |
michael@0 | 119 | getReq.onsuccess = function(event) { |
michael@0 | 120 | if (DEBUG) debug("Request for '" + info.name + "' successful. " + |
michael@0 | 121 | "Record count: " + event.target.result.length); |
michael@0 | 122 | |
michael@0 | 123 | if (event.target.result.length == 0) { |
michael@0 | 124 | if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + info.name + " is not in the database.\n"); |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | let results = {}; |
michael@0 | 128 | |
michael@0 | 129 | for (var i in event.target.result) { |
michael@0 | 130 | let result = event.target.result[i]; |
michael@0 | 131 | var name = result.settingName; |
michael@0 | 132 | if (DEBUG) debug("VAL: " + result.userValue +", " + result.defaultValue + "\n"); |
michael@0 | 133 | var value = result.userValue !== undefined ? result.userValue : result.defaultValue; |
michael@0 | 134 | results[name] = this._wrap(value); |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | this._open = true; |
michael@0 | 138 | Services.DOMRequest.fireSuccess(request, this._wrap(results)); |
michael@0 | 139 | this._open = false; |
michael@0 | 140 | }.bind(lock); |
michael@0 | 141 | |
michael@0 | 142 | getReq.onerror = function() { |
michael@0 | 143 | Services.DOMRequest.fireError(request, 0) |
michael@0 | 144 | }; |
michael@0 | 145 | break; |
michael@0 | 146 | } |
michael@0 | 147 | } |
michael@0 | 148 | }, |
michael@0 | 149 | |
michael@0 | 150 | createTransactionAndProcess: function() { |
michael@0 | 151 | if (this._settingsManager._settingsDB._db) { |
michael@0 | 152 | var lock; |
michael@0 | 153 | while (lock = this._settingsManager._locks.dequeue()) { |
michael@0 | 154 | if (!lock._transaction) { |
michael@0 | 155 | let transactionType = this._settingsManager.hasWritePrivileges ? "readwrite" : "readonly"; |
michael@0 | 156 | lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType); |
michael@0 | 157 | } |
michael@0 | 158 | if (!lock._isBusy) { |
michael@0 | 159 | lock.process(); |
michael@0 | 160 | } else { |
michael@0 | 161 | this._settingsManager._locks.enqueue(lock); |
michael@0 | 162 | } |
michael@0 | 163 | } |
michael@0 | 164 | if (!this._requests.isEmpty() && !this._isBusy) { |
michael@0 | 165 | this.process(); |
michael@0 | 166 | } |
michael@0 | 167 | } |
michael@0 | 168 | }, |
michael@0 | 169 | |
michael@0 | 170 | get: function get(aName) { |
michael@0 | 171 | if (!this._open) { |
michael@0 | 172 | dump("Settings lock not open!\n"); |
michael@0 | 173 | throw Components.results.NS_ERROR_ABORT; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | if (this._settingsManager.hasReadPrivileges) { |
michael@0 | 177 | let req = Services.DOMRequest.createRequest(this._settingsManager._window); |
michael@0 | 178 | this._requests.enqueue({ request: req, intent:"get", name: aName }); |
michael@0 | 179 | this.createTransactionAndProcess(); |
michael@0 | 180 | return req; |
michael@0 | 181 | } else { |
michael@0 | 182 | if (DEBUG) debug("get not allowed"); |
michael@0 | 183 | throw Components.results.NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 184 | } |
michael@0 | 185 | }, |
michael@0 | 186 | |
michael@0 | 187 | _serializePreservingBinaries: function _serializePreservingBinaries(aObject) { |
michael@0 | 188 | // We need to serialize settings objects, otherwise they can change between |
michael@0 | 189 | // the set() call and the enqueued request being processed. We can't simply |
michael@0 | 190 | // parse(stringify(obj)) because that breaks things like Blobs, Files and |
michael@0 | 191 | // Dates, so we use stringify's replacer and parse's reviver parameters to |
michael@0 | 192 | // preserve binaries. |
michael@0 | 193 | let manager = this._settingsManager; |
michael@0 | 194 | let binaries = Object.create(null); |
michael@0 | 195 | let stringified = JSON.stringify(aObject, function(key, value) { |
michael@0 | 196 | value = manager._settingsDB.prepareValue(value); |
michael@0 | 197 | let kind = ObjectWrapper.getObjectKind(value); |
michael@0 | 198 | if (kind == "file" || kind == "blob" || kind == "date") { |
michael@0 | 199 | let uuid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator) |
michael@0 | 200 | .generateUUID().toString(); |
michael@0 | 201 | binaries[uuid] = value; |
michael@0 | 202 | return uuid; |
michael@0 | 203 | } |
michael@0 | 204 | return value; |
michael@0 | 205 | }); |
michael@0 | 206 | return JSON.parse(stringified, function(key, value) { |
michael@0 | 207 | if (value in binaries) { |
michael@0 | 208 | return binaries[value]; |
michael@0 | 209 | } |
michael@0 | 210 | return value; |
michael@0 | 211 | }); |
michael@0 | 212 | }, |
michael@0 | 213 | |
michael@0 | 214 | set: function set(aSettings) { |
michael@0 | 215 | if (!this._open) { |
michael@0 | 216 | throw "Settings lock not open"; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | if (this._settingsManager.hasWritePrivileges) { |
michael@0 | 220 | let req = Services.DOMRequest.createRequest(this._settingsManager._window); |
michael@0 | 221 | if (DEBUG) debug("send: " + JSON.stringify(aSettings)); |
michael@0 | 222 | let settings = this._serializePreservingBinaries(aSettings); |
michael@0 | 223 | this._requests.enqueue({request: req, intent: "set", settings: settings}); |
michael@0 | 224 | this.createTransactionAndProcess(); |
michael@0 | 225 | return req; |
michael@0 | 226 | } else { |
michael@0 | 227 | if (DEBUG) debug("set not allowed"); |
michael@0 | 228 | throw "No permission to call set"; |
michael@0 | 229 | } |
michael@0 | 230 | }, |
michael@0 | 231 | |
michael@0 | 232 | clear: function clear() { |
michael@0 | 233 | if (!this._open) { |
michael@0 | 234 | throw "Settings lock not open"; |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | if (this._settingsManager.hasWritePrivileges) { |
michael@0 | 238 | let req = Services.DOMRequest.createRequest(this._settingsManager._window); |
michael@0 | 239 | this._requests.enqueue({ request: req, intent: "clear"}); |
michael@0 | 240 | this.createTransactionAndProcess(); |
michael@0 | 241 | return req; |
michael@0 | 242 | } else { |
michael@0 | 243 | if (DEBUG) debug("clear not allowed"); |
michael@0 | 244 | throw "No permission to call clear"; |
michael@0 | 245 | } |
michael@0 | 246 | }, |
michael@0 | 247 | |
michael@0 | 248 | classID: Components.ID("{60c9357c-3ae0-4222-8f55-da01428470d5}"), |
michael@0 | 249 | contractID: "@mozilla.org/settingsLock;1", |
michael@0 | 250 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]), |
michael@0 | 251 | }; |
michael@0 | 252 | |
michael@0 | 253 | function SettingsManager() { |
michael@0 | 254 | this._locks = new Queue(); |
michael@0 | 255 | this._settingsDB = new SettingsDB(); |
michael@0 | 256 | this._settingsDB.init(); |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | SettingsManager.prototype = { |
michael@0 | 260 | _callbacks: null, |
michael@0 | 261 | |
michael@0 | 262 | _wrap: function _wrap(obj) { |
michael@0 | 263 | return Cu.cloneInto(obj, this._window); |
michael@0 | 264 | }, |
michael@0 | 265 | |
michael@0 | 266 | nextTick: function nextTick(aCallback, thisObj) { |
michael@0 | 267 | if (thisObj) |
michael@0 | 268 | aCallback = aCallback.bind(thisObj); |
michael@0 | 269 | |
michael@0 | 270 | Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL); |
michael@0 | 271 | }, |
michael@0 | 272 | |
michael@0 | 273 | set onsettingchange(aHandler) { |
michael@0 | 274 | this.__DOM_IMPL__.setEventHandler("onsettingchange", aHandler); |
michael@0 | 275 | }, |
michael@0 | 276 | |
michael@0 | 277 | get onsettingchange() { |
michael@0 | 278 | return this.__DOM_IMPL__.getEventHandler("onsettingchange"); |
michael@0 | 279 | }, |
michael@0 | 280 | |
michael@0 | 281 | createLock: function() { |
michael@0 | 282 | if (DEBUG) debug("get lock!"); |
michael@0 | 283 | var lock = new SettingsLock(this); |
michael@0 | 284 | this._locks.enqueue(lock); |
michael@0 | 285 | this._settingsDB.ensureDB( |
michael@0 | 286 | function() { lock.createTransactionAndProcess(); }, |
michael@0 | 287 | function() { dump("Cannot open Settings DB. Trying to open an old version?\n"); } |
michael@0 | 288 | ); |
michael@0 | 289 | this.nextTick(function() { this._open = false; }, lock); |
michael@0 | 290 | return lock; |
michael@0 | 291 | }, |
michael@0 | 292 | |
michael@0 | 293 | receiveMessage: function(aMessage) { |
michael@0 | 294 | if (DEBUG) debug("Settings::receiveMessage: " + aMessage.name); |
michael@0 | 295 | let msg = aMessage.json; |
michael@0 | 296 | |
michael@0 | 297 | switch (aMessage.name) { |
michael@0 | 298 | case "Settings:Change:Return:OK": |
michael@0 | 299 | if (DEBUG) debug('data:' + msg.key + ':' + msg.value + '\n'); |
michael@0 | 300 | |
michael@0 | 301 | let event = new this._window.MozSettingsEvent("settingchange", this._wrap({ |
michael@0 | 302 | settingName: msg.key, |
michael@0 | 303 | settingValue: msg.value |
michael@0 | 304 | })); |
michael@0 | 305 | this.__DOM_IMPL__.dispatchEvent(event); |
michael@0 | 306 | |
michael@0 | 307 | if (this._callbacks && this._callbacks[msg.key]) { |
michael@0 | 308 | if (DEBUG) debug("observe callback called! " + msg.key + " " + this._callbacks[msg.key].length); |
michael@0 | 309 | this._callbacks[msg.key].forEach(function(cb) { |
michael@0 | 310 | cb(this._wrap({settingName: msg.key, settingValue: msg.value})); |
michael@0 | 311 | }.bind(this)); |
michael@0 | 312 | } else { |
michael@0 | 313 | if (DEBUG) debug("no observers stored!"); |
michael@0 | 314 | } |
michael@0 | 315 | break; |
michael@0 | 316 | default: |
michael@0 | 317 | if (DEBUG) debug("Wrong message: " + aMessage.name); |
michael@0 | 318 | } |
michael@0 | 319 | }, |
michael@0 | 320 | |
michael@0 | 321 | addObserver: function addObserver(aName, aCallback) { |
michael@0 | 322 | if (DEBUG) debug("addObserver " + aName); |
michael@0 | 323 | if (!this._callbacks) { |
michael@0 | 324 | cpmm.sendAsyncMessage("Settings:RegisterForMessages"); |
michael@0 | 325 | this._callbacks = {}; |
michael@0 | 326 | } |
michael@0 | 327 | if (!this._callbacks[aName]) { |
michael@0 | 328 | this._callbacks[aName] = [aCallback]; |
michael@0 | 329 | } else { |
michael@0 | 330 | this._callbacks[aName].push(aCallback); |
michael@0 | 331 | } |
michael@0 | 332 | }, |
michael@0 | 333 | |
michael@0 | 334 | removeObserver: function removeObserver(aName, aCallback) { |
michael@0 | 335 | if (DEBUG) debug("deleteObserver " + aName); |
michael@0 | 336 | if (this._callbacks && this._callbacks[aName]) { |
michael@0 | 337 | let index = this._callbacks[aName].indexOf(aCallback) |
michael@0 | 338 | if (index != -1) { |
michael@0 | 339 | this._callbacks[aName].splice(index, 1) |
michael@0 | 340 | } else { |
michael@0 | 341 | if (DEBUG) debug("Callback not found for: " + aName); |
michael@0 | 342 | } |
michael@0 | 343 | } else { |
michael@0 | 344 | if (DEBUG) debug("No observers stored for " + aName); |
michael@0 | 345 | } |
michael@0 | 346 | }, |
michael@0 | 347 | |
michael@0 | 348 | init: function(aWindow) { |
michael@0 | 349 | mrm.registerStrongReporter(this); |
michael@0 | 350 | cpmm.addMessageListener("Settings:Change:Return:OK", this); |
michael@0 | 351 | this._window = aWindow; |
michael@0 | 352 | Services.obs.addObserver(this, "inner-window-destroyed", false); |
michael@0 | 353 | let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
michael@0 | 354 | this.innerWindowID = util.currentInnerWindowID; |
michael@0 | 355 | |
michael@0 | 356 | let readPerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-read"); |
michael@0 | 357 | let writePerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-write"); |
michael@0 | 358 | this.hasReadPrivileges = readPerm == Ci.nsIPermissionManager.ALLOW_ACTION; |
michael@0 | 359 | this.hasWritePrivileges = writePerm == Ci.nsIPermissionManager.ALLOW_ACTION; |
michael@0 | 360 | |
michael@0 | 361 | if (this.hasReadPrivileges) { |
michael@0 | 362 | cpmm.sendAsyncMessage("Settings:RegisterForMessages"); |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | if (!this.hasReadPrivileges && !this.hasWritePrivileges) { |
michael@0 | 366 | dump("No settings permission for: " + aWindow.document.nodePrincipal.origin + "\n"); |
michael@0 | 367 | Cu.reportError("No settings permission for: " + aWindow.document.nodePrincipal.origin); |
michael@0 | 368 | } |
michael@0 | 369 | }, |
michael@0 | 370 | |
michael@0 | 371 | observe: function(aSubject, aTopic, aData) { |
michael@0 | 372 | if (DEBUG) debug("Topic: " + aTopic); |
michael@0 | 373 | if (aTopic == "inner-window-destroyed") { |
michael@0 | 374 | let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; |
michael@0 | 375 | if (wId == this.innerWindowID) { |
michael@0 | 376 | this.cleanup(); |
michael@0 | 377 | } |
michael@0 | 378 | } |
michael@0 | 379 | }, |
michael@0 | 380 | |
michael@0 | 381 | collectReports: function(aCallback, aData) { |
michael@0 | 382 | for (var topic in this._callbacks) { |
michael@0 | 383 | let length = this._callbacks[topic].length; |
michael@0 | 384 | if (length == 0) { |
michael@0 | 385 | continue; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | let path; |
michael@0 | 389 | if (length < 20) { |
michael@0 | 390 | path = "settings-observers"; |
michael@0 | 391 | } else { |
michael@0 | 392 | path = "settings-observers-suspect/referent(topic=" + topic + ")"; |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | aCallback.callback("", path, |
michael@0 | 396 | Ci.nsIMemoryReporter.KIND_OTHER, |
michael@0 | 397 | Ci.nsIMemoryReporter.UNITS_COUNT, |
michael@0 | 398 | this._callbacks[topic].length, |
michael@0 | 399 | "The number of settings observers for this topic.", |
michael@0 | 400 | aData); |
michael@0 | 401 | } |
michael@0 | 402 | }, |
michael@0 | 403 | |
michael@0 | 404 | cleanup: function() { |
michael@0 | 405 | Services.obs.removeObserver(this, "inner-window-destroyed"); |
michael@0 | 406 | cpmm.removeMessageListener("Settings:Change:Return:OK", this); |
michael@0 | 407 | mrm.unregisterStrongReporter(this); |
michael@0 | 408 | this._requests = null; |
michael@0 | 409 | this._window = null; |
michael@0 | 410 | this._innerWindowID = null; |
michael@0 | 411 | this._settingsDB.close(); |
michael@0 | 412 | }, |
michael@0 | 413 | |
michael@0 | 414 | classID: Components.ID("{c40b1c70-00fb-11e2-a21f-0800200c9a66}"), |
michael@0 | 415 | contractID: "@mozilla.org/settingsManager;1", |
michael@0 | 416 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, |
michael@0 | 417 | Ci.nsIDOMGlobalPropertyInitializer, |
michael@0 | 418 | Ci.nsIObserver, |
michael@0 | 419 | Ci.nsIMemoryReporter]), |
michael@0 | 420 | }; |
michael@0 | 421 | |
michael@0 | 422 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsManager, SettingsLock]) |