diff -r 000000000000 -r 6474c204b198 dom/settings/SettingsService.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/settings/SettingsService.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,246 @@ +/* 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" + +/* static functions */ +let DEBUG = 0; +let debug; +if (DEBUG) + debug = function (s) { dump("-*- SettingsService: " + s + "\n"); } +else + debug = function (s) {} + +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"); + +const nsIClassInfo = Ci.nsIClassInfo; + +const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1"; +const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}"); +const nsISettingsServiceLock = Ci.nsISettingsServiceLock; + +function SettingsServiceLock(aSettingsService) +{ + if (DEBUG) debug("settingsServiceLock constr!"); + this._open = true; + this._busy = false; + this._requests = new Queue(); + this._settingsService = aSettingsService; + this._transaction = null; +} + +SettingsServiceLock.prototype = { + + callHandle: function callHandle(aCallback, aName, aValue) { + try { + aCallback ? aCallback.handle(aName, aValue) : null; + } catch (e) { + dump("settings 'handle' callback threw an exception, dropping: " + e + "\n"); + } + }, + + callAbort: function callAbort(aCallback, aMessage) { + try { + aCallback ? aCallback.handleAbort(aMessage) : null; + } catch (e) { + dump("settings 'abort' callback threw an exception, dropping: " + e + "\n"); + } + }, + + callError: function callError(aCallback, aMessage) { + try { + aCallback ? aCallback.handleError(aMessage) : null; + } catch (e) { + dump("settings 'error' callback threw an exception, dropping: " + e + "\n"); + } + }, + + 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(aCallback) { + 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 (aCallback) { + lock._transaction.oncomplete = aCallback.handle; + lock._transaction.onabort = function(event) { + let message = ''; + if (event.target.error) { + message = event.target.error.name + ': ' + event.target.error.message; + } + this.callAbort(aCallback, 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]) +}; + +const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}"); + +function SettingsService() +{ + debug("settingsService Constructor"); + this._locks = new Queue(); + this._settingsDB = new SettingsDB(); + this._settingsDB.init(); +} + +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); + this._locks.enqueue(lock); + this._settingsDB.ensureDB( + function() { lock.createTransactionAndProcess(aCallback); }, + 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])