summaryrefslogtreecommitdiffstats
path: root/dom/settings/SettingsService.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/settings/SettingsService.js')
-rw-r--r--dom/settings/SettingsService.js358
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]);