diff options
Diffstat (limited to 'dom/settings/SettingsService.js')
-rw-r--r-- | dom/settings/SettingsService.js | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/dom/settings/SettingsService.js b/dom/settings/SettingsService.js new file mode 100644 index 000000000..09bd3ca72 --- /dev/null +++ b/dom/settings/SettingsService.js @@ -0,0 +1,358 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import('resource://gre/modules/SettingsRequestManager.jsm'); + +/* static functions */ +var DEBUG = false; +var VERBOSE = false; + +try { + DEBUG = + Services.prefs.getBoolPref("dom.mozSettings.SettingsService.debug.enabled"); + VERBOSE = + Services.prefs.getBoolPref("dom.mozSettings.SettingsService.verbose.enabled"); +} catch (ex) { } + +function debug(s) { + dump("-*- SettingsService: " + s + "\n"); +} + +XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", + "@mozilla.org/uuid-generator;1", + "nsIUUIDGenerator"); +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); +XPCOMUtils.defineLazyServiceGetter(this, "mrm", + "@mozilla.org/memory-reporter-manager;1", + "nsIMemoryReporterManager"); + +const nsIClassInfo = Ci.nsIClassInfo; +const kXpcomShutdownObserverTopic = "xpcom-shutdown"; + +const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1"; +const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}"); +const nsISettingsServiceLock = Ci.nsISettingsServiceLock; + +function makeSettingsServiceRequest(aCallback, aName, aValue) { + return { + callback: aCallback, + name: aName, + value: aValue + }; +}; + +const kLockListeners = ["Settings:Get:OK", "Settings:Get:KO", + "Settings:Clear:OK", "Settings:Clear:KO", + "Settings:Set:OK", "Settings:Set:KO", + "Settings:Finalize:OK", "Settings:Finalize:KO"]; + +function SettingsServiceLock(aSettingsService, aTransactionCallback) { + if (VERBOSE) debug("settingsServiceLock constr!"); + this._open = true; + this._settingsService = aSettingsService; + this._id = uuidgen.generateUUID().toString(); + this._transactionCallback = aTransactionCallback; + this._requests = {}; + let closeHelper = function() { + if (VERBOSE) debug("closing lock " + this._id); + this._open = false; + this.runOrFinalizeQueries(); + }.bind(this); + + this.addListeners(); + + let createLockPayload = { + lockID: this._id, + isServiceLock: true, + windowID: undefined, + lockStack: (new Error).stack + }; + + this.returnMessage("Settings:CreateLock", createLockPayload); + Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL); +} + +SettingsServiceLock.prototype = { + get closed() { + return !this._open; + }, + + addListeners: function() { + for (let msg of kLockListeners) { + cpmm.addMessageListener(msg, this); + } + }, + + removeListeners: function() { + for (let msg of kLockListeners) { + cpmm.removeMessageListener(msg, this); + } + }, + + returnMessage: function(aMessage, aData) { + SettingsRequestManager.receiveMessage({ + name: aMessage, + data: aData, + target: undefined, + principal: Services.scriptSecurityManager.getSystemPrincipal() + }); + }, + + runOrFinalizeQueries: function() { + if (!this._requests || Object.keys(this._requests).length == 0) { + this.returnMessage("Settings:Finalize", {lockID: this._id}); + } else { + this.returnMessage("Settings:Run", {lockID: this._id}); + } + }, + + receiveMessage: function(aMessage) { + + let msg = aMessage.data; + // SettingsRequestManager broadcasts changes to all locks in the child. If + // our lock isn't being addressed, just return. + if(msg.lockID != this._id) { + return; + } + if (VERBOSE) debug("receiveMessage (" + this._id + "): " + aMessage.name); + // Finalizing a transaction does not return a request ID since we are + // supposed to fire callbacks. + if (!msg.requestID) { + switch (aMessage.name) { + case "Settings:Finalize:OK": + if (VERBOSE) debug("Lock finalize ok!"); + this.callTransactionHandle(); + break; + case "Settings:Finalize:KO": + if (DEBUG) debug("Lock finalize failed!"); + this.callAbort(); + break; + default: + if (DEBUG) debug("Message type " + aMessage.name + " is missing a requestID"); + } + + this._settingsService.unregisterLock(this._id); + return; + } + + let req = this._requests[msg.requestID]; + if (!req) { + if (DEBUG) debug("Matching request not found."); + return; + } + delete this._requests[msg.requestID]; + switch (aMessage.name) { + case "Settings:Get:OK": + this._open = true; + let settings_names = Object.keys(msg.settings); + if (settings_names.length > 0) { + let name = settings_names[0]; + if (DEBUG && settings_names.length > 1) { + debug("Warning: overloaded setting:" + name); + } + let result = msg.settings[name]; + this.callHandle(req.callback, name, result); + } else { + this.callHandle(req.callback, req.name, null); + } + this._open = false; + break; + case "Settings:Set:OK": + this._open = true; + // We don't pass values back from sets in SettingsManager... + this.callHandle(req.callback, req.name, req.value); + this._open = false; + break; + case "Settings:Get:KO": + case "Settings:Set:KO": + if (DEBUG) debug("error:" + msg.errorMsg); + this.callError(req.callback, msg.error); + break; + default: + if (DEBUG) debug("Wrong message: " + aMessage.name); + } + this.runOrFinalizeQueries(); + }, + + get: function get(aName, aCallback) { + if (VERBOSE) debug("get (" + this._id + "): " + aName); + if (!this._open) { + if (DEBUG) debug("Settings lock not open!\n"); + throw Components.results.NS_ERROR_ABORT; + } + let reqID = uuidgen.generateUUID().toString(); + this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName); + this.returnMessage("Settings:Get", {requestID: reqID, + lockID: this._id, + name: aName}); + }, + + set: function set(aName, aValue, aCallback) { + if (VERBOSE) debug("set: " + aName + " " + aValue); + if (!this._open) { + throw "Settings lock not open"; + } + let reqID = uuidgen.generateUUID().toString(); + this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName, aValue); + let settings = {}; + settings[aName] = aValue; + this.returnMessage("Settings:Set", {requestID: reqID, + lockID: this._id, + settings: settings}); + }, + + callHandle: function callHandle(aCallback, aName, aValue) { + try { + aCallback && aCallback.handle ? aCallback.handle(aName, aValue) : null; + } catch (e) { + if (DEBUG) debug("settings 'handle' for " + aName + " callback threw an exception, dropping: " + e + "\n"); + } + }, + + callAbort: function callAbort(aCallback, aMessage) { + try { + aCallback && aCallback.handleAbort ? aCallback.handleAbort(aMessage) : null; + } catch (e) { + if (DEBUG) debug("settings 'abort' callback threw an exception, dropping: " + e + "\n"); + } + }, + + callError: function callError(aCallback, aMessage) { + try { + aCallback && aCallback.handleError ? aCallback.handleError(aMessage) : null; + } catch (e) { + if (DEBUG) debug("settings 'error' callback threw an exception, dropping: " + e + "\n"); + } + }, + + callTransactionHandle: function callTransactionHandle() { + try { + this._transactionCallback && this._transactionCallback.handle ? this._transactionCallback.handle() : null; + } catch (e) { + if (DEBUG) debug("settings 'Transaction handle' callback threw an exception, dropping: " + e + "\n"); + } + }, + + classID : SETTINGSSERVICELOCK_CID, + QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]) +}; + +const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}"); + +function SettingsService() +{ + if (VERBOSE) debug("settingsService Constructor"); + this._locks = []; + this._serviceLocks = {}; + this._createdLocks = 0; + this._unregisteredLocks = 0; + this.init(); +} + +SettingsService.prototype = { + + init: function() { + Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false); + mrm.registerStrongReporter(this); + }, + + uninit: function() { + Services.obs.removeObserver(this, kXpcomShutdownObserverTopic); + mrm.unregisterStrongReporter(this); + }, + + observe: function(aSubject, aTopic, aData) { + if (VERBOSE) debug("observe: " + aTopic); + if (aTopic === kXpcomShutdownObserverTopic) { + this.uninit(); + } + }, + + receiveMessage: function(aMessage) { + if (VERBOSE) debug("Entering receiveMessage"); + + let lockID = aMessage.data.lockID; + if (!lockID) { + if (DEBUG) debug("No lock ID"); + return; + } + + if (!(lockID in this._serviceLocks)) { + if (DEBUG) debug("Received message for lock " + lockID + " but no lock"); + return; + } + + if (VERBOSE) debug("Delivering message"); + this._serviceLocks[lockID].receiveMessage(aMessage); + }, + + createLock: function createLock(aCallback) { + if (VERBOSE) debug("Calling createLock"); + var lock = new SettingsServiceLock(this, aCallback); + if (VERBOSE) debug("Created lock " + lock._id); + this.registerLock(lock); + return lock; + }, + + registerLock: function(aLock) { + if (VERBOSE) debug("Registering lock " + aLock._id); + this._locks.push(aLock._id); + this._serviceLocks[aLock._id] = aLock; + this._createdLocks++; + }, + + unregisterLock: function(aLockID) { + let lock_index = this._locks.indexOf(aLockID); + if (lock_index != -1) { + if (VERBOSE) debug("Unregistering lock " + aLockID); + this._locks.splice(lock_index, 1); + this._serviceLocks[aLockID].removeListeners(); + this._serviceLocks[aLockID] = null; + delete this._serviceLocks[aLockID]; + this._unregisteredLocks++; + } + }, + + collectReports: function(aCallback, aData, aAnonymize) { + aCallback.callback("", + "settings-service-locks/alive", + Ci.nsIMemoryReporter.KIND_OTHER, + Ci.nsIMemoryReporter.UNITS_COUNT, + this._locks.length, + "The number of service locks that are currently alives.", + aData); + + aCallback.callback("", + "settings-service-locks/created", + Ci.nsIMemoryReporter.KIND_OTHER, + Ci.nsIMemoryReporter.UNITS_COUNT, + this._createdLocks, + "The number of service locks that were created.", + aData); + + aCallback.callback("", + "settings-service-locks/deleted", + Ci.nsIMemoryReporter.KIND_OTHER, + Ci.nsIMemoryReporter.UNITS_COUNT, + this._unregisteredLocks, + "The number of service locks that were deleted.", + aData); + }, + + classID : SETTINGSSERVICE_CID, + QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService, + Ci.nsIObserver, + Ci.nsIMemoryReporter]) +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock]); |