diff --git a/dom/settings/SettingsManager.js b/dom/settings/SettingsManager.js index 51f068762409..07bbdab4a820 100644 --- a/dom/settings/SettingsManager.js +++ b/dom/settings/SettingsManager.js @@ -108,11 +108,13 @@ SettingsLock.prototype = { 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 (DEBUG) debug("receiveMessage (" + this._id + "): " + aMessage.name); // Finalizing a transaction does not return a request ID since we are // supposed to fire callbacks. diff --git a/dom/settings/SettingsService.js b/dom/settings/SettingsService.js index dc8e6b32f2b9..cf5b4269a314 100644 --- a/dom/settings/SettingsService.js +++ b/dom/settings/SettingsService.js @@ -5,20 +5,24 @@ "use strict" /* static functions */ -let DEBUG = 0; -let debug; -if (DEBUG) - debug = function (s) { dump("-*- SettingsService: " + s + "\n"); } -else - debug = function (s) {} +const DEBUG = false; +function debug(s) { + dump("-*- SettingsService: " + s + "\n"); +} const Ci = Components.interfaces; const Cu = Components.utils; -Cu.import("resource://gre/modules/SettingsQueue.jsm"); -Cu.import("resource://gre/modules/SettingsDB.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import('resource://gre/modules/SettingsRequestManager.jsm'); + +XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", + "@mozilla.org/uuid-generator;1", + "nsIUUIDGenerator"); +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); const nsIClassInfo = Ci.nsIClassInfo; @@ -26,18 +30,149 @@ const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1"; const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}"); const nsISettingsServiceLock = Ci.nsISettingsServiceLock; -function SettingsServiceLock(aSettingsService, aTransactionCallback) -{ +function makeSettingsServiceRequest(aCallback, aName, aValue) { + return { + callback: aCallback, + name: aName, + value: aValue + }; +}; + +function SettingsServiceLock(aSettingsService, aTransactionCallback) { if (DEBUG) debug("settingsServiceLock constr!"); this._open = true; - this._busy = false; - this._requests = new Queue(); this._settingsService = aSettingsService; - this._transaction = null; + this._id = uuidgen.generateUUID().toString(); this._transactionCallback = aTransactionCallback; + this._requests = {}; + let closeHelper = function() { + if (DEBUG) debug("closing lock " + this._id); + this._open = false; + this.runOrFinalizeQueries(); + }.bind(this); + + let msgs = ["Settings:Get:OK", "Settings:Get:KO", + "Settings:Clear:OK", "Settings:Clear:KO", + "Settings:Set:OK", "Settings:Set:KO", + "Settings:Finalize:OK", "Settings:Finalize:KO"]; + + for (let msg in msgs) { + cpmm.addMessageListener(msgs[msg], this); + } + + cpmm.sendAsyncMessage("Settings:CreateLock", {lockID: this._id, isServiceLock: true}, undefined, Services.scriptSecurityManager.getSystemPrincipal()); + Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL); } SettingsServiceLock.prototype = { + get closed() { + return !this._open; + }, + + runOrFinalizeQueries: function() { + if (!this._requests || Object.keys(this._requests).length == 0) { + cpmm.sendAsyncMessage("Settings:Finalize", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal()); + } else { + cpmm.sendAsyncMessage("Settings:Run", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal()); + } + }, + + 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 (DEBUG) 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 (DEBUG) 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"); + } + 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 (DEBUG) debug("get (" + this._id + "): " + aName); + if (!this._open) { + dump("Settings lock not open!\n"); + throw Components.results.NS_ERROR_ABORT; + } + let reqID = uuidgen.generateUUID().toString(); + this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName); + cpmm.sendAsyncMessage("Settings:Get", {requestID: reqID, + lockID: this._id, + name: aName}, + undefined, + Services.scriptSecurityManager.getSystemPrincipal()); + }, + + set: function set(aName, aValue, aCallback) { + if (DEBUG) 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; + cpmm.sendAsyncMessage("Settings:Set", {requestID: reqID, + lockID: this._id, + settings: settings}, + undefined, + Services.scriptSecurityManager.getSystemPrincipal()); + }, callHandle: function callHandle(aCallback, aName, aValue) { try { @@ -71,149 +206,6 @@ SettingsServiceLock.prototype = { } }, - process: function process() { - debug("process!"); - let lock = this; - lock._open = false; - let store = lock._transaction.objectStore(SETTINGSSTORE_NAME); - - while (!lock._requests.isEmpty()) { - if (lock._isBusy) { - return; - } - let info = lock._requests.dequeue(); - if (DEBUG) debug("info:" + info.intent); - let callback = info.callback; - let name = info.name; - switch (info.intent) { - case "set": - let value = info.value; - let message = info.message; - if(DEBUG && typeof(value) == 'object') { - debug("object name:" + name + ", val: " + JSON.stringify(value)); - } - lock._isBusy = true; - let checkKeyRequest = store.get(name); - - checkKeyRequest.onsuccess = function (event) { - let defaultValue; - if (event.target.result) { - defaultValue = event.target.result.defaultValue; - } else { - defaultValue = null; - if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + name + " is not in the database.\n"); - } - let setReq = store.put({ settingName: name, defaultValue: defaultValue, userValue: value }); - - setReq.onsuccess = function() { - lock._isBusy = false; - lock._open = true; - lock.callHandle(callback, name, value); - Services.obs.notifyObservers(lock, "mozsettings-changed", JSON.stringify({ - key: name, - value: value, - message: message - })); - lock._open = false; - lock.process(); - }; - - setReq.onerror = function(event) { - lock._isBusy = false; - lock.callError(callback, event.target.errorMessage); - lock.process(); - }; - } - - checkKeyRequest.onerror = function(event) { - lock._isBusy = false; - lock.callError(callback, event.target.errorMessage); - lock.process(); - }; - break; - case "get": - let getReq = store.mozGetAll(name); - getReq.onsuccess = function(event) { - if (DEBUG) { - debug("Request successful. Record count:" + event.target.result.length); - debug("result: " + JSON.stringify(event.target.result)); - } - this._open = true; - if (callback) { - if (event.target.result[0]) { - if (event.target.result.length > 1) { - if (DEBUG) debug("Warning: overloaded setting:" + name); - } - let result = event.target.result[0]; - let value = result.userValue !== undefined - ? result.userValue - : result.defaultValue; - lock.callHandle(callback, name, value); - } else { - lock.callHandle(callback, name, null); - } - } else { - if (DEBUG) debug("no callback defined!"); - } - this._open = false; - }.bind(lock); - getReq.onerror = function error(event) { - lock.callError(callback, event.target.errorMessage); - }; - break; - } - } - lock._open = true; - }, - - createTransactionAndProcess: function() { - if (this._settingsService._settingsDB._db) { - let lock; - while (lock = this._settingsService._locks.dequeue()) { - if (!lock._transaction) { - lock._transaction = lock._settingsService._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite"); - if (lock._transactionCallback) { - lock._transaction.oncomplete = lock.callTransactionHandle.bind(lock); - lock._transaction.onabort = function(event) { - let message = ''; - if (event.target.error) { - message = event.target.error.name + ': ' + event.target.error.message; - } - this.callAbort(lock._transactionCallback.handleAbort, message); - }; - } - } - if (!lock._isBusy) { - lock.process(); - } else { - this._settingsService._locks.enqueue(lock); - return; - } - } - if (!this._requests.isEmpty() && !this._isBusy) { - this.process(); - } - } - }, - - get: function get(aName, aCallback) { - if (DEBUG) debug("get: " + aName + ", " + aCallback); - this._requests.enqueue({ callback: aCallback, intent:"get", name: aName }); - this.createTransactionAndProcess(); - }, - - set: function set(aName, aValue, aCallback, aMessage) { - debug("set: " + aName + ": " + JSON.stringify(aValue)); - if (aMessage === undefined) - aMessage = null; - this._requests.enqueue({ callback: aCallback, - intent: "set", - name: aName, - value: this._settingsService._settingsDB.prepareValue(aValue), - message: aMessage }); - this.createTransactionAndProcess(); - }, - classID : SETTINGSSERVICELOCK_CID, QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]) }; @@ -222,34 +214,18 @@ const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-08002 function SettingsService() { - debug("settingsService Constructor"); - this._locks = new Queue(); - this._settingsDB = new SettingsDB(); - this._settingsDB.init(); + if (DEBUG) debug("settingsService Constructor"); } SettingsService.prototype = { - nextTick: function nextTick(aCallback, thisObj) { - if (thisObj) - aCallback = aCallback.bind(thisObj); - - Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL); - }, - createLock: function createLock(aCallback) { var lock = new SettingsServiceLock(this, aCallback); - this._locks.enqueue(lock); - this._settingsDB.ensureDB( - function() { lock.createTransactionAndProcess(); }, - function() { dump("SettingsService failed to open DB!\n"); } - ); - this.nextTick(function() { this._open = false; }, lock); return lock; }, classID : SETTINGSSERVICE_CID, QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService]) -} +}; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock]) +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock]);