1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/settings/SettingsService.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,246 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict" 1.9 + 1.10 +/* static functions */ 1.11 +let DEBUG = 0; 1.12 +let debug; 1.13 +if (DEBUG) 1.14 + debug = function (s) { dump("-*- SettingsService: " + s + "\n"); } 1.15 +else 1.16 + debug = function (s) {} 1.17 + 1.18 +const Ci = Components.interfaces; 1.19 +const Cu = Components.utils; 1.20 + 1.21 +Cu.import("resource://gre/modules/SettingsQueue.jsm"); 1.22 +Cu.import("resource://gre/modules/SettingsDB.jsm"); 1.23 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.24 +Cu.import("resource://gre/modules/Services.jsm"); 1.25 + 1.26 +const nsIClassInfo = Ci.nsIClassInfo; 1.27 + 1.28 +const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1"; 1.29 +const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}"); 1.30 +const nsISettingsServiceLock = Ci.nsISettingsServiceLock; 1.31 + 1.32 +function SettingsServiceLock(aSettingsService) 1.33 +{ 1.34 + if (DEBUG) debug("settingsServiceLock constr!"); 1.35 + this._open = true; 1.36 + this._busy = false; 1.37 + this._requests = new Queue(); 1.38 + this._settingsService = aSettingsService; 1.39 + this._transaction = null; 1.40 +} 1.41 + 1.42 +SettingsServiceLock.prototype = { 1.43 + 1.44 + callHandle: function callHandle(aCallback, aName, aValue) { 1.45 + try { 1.46 + aCallback ? aCallback.handle(aName, aValue) : null; 1.47 + } catch (e) { 1.48 + dump("settings 'handle' callback threw an exception, dropping: " + e + "\n"); 1.49 + } 1.50 + }, 1.51 + 1.52 + callAbort: function callAbort(aCallback, aMessage) { 1.53 + try { 1.54 + aCallback ? aCallback.handleAbort(aMessage) : null; 1.55 + } catch (e) { 1.56 + dump("settings 'abort' callback threw an exception, dropping: " + e + "\n"); 1.57 + } 1.58 + }, 1.59 + 1.60 + callError: function callError(aCallback, aMessage) { 1.61 + try { 1.62 + aCallback ? aCallback.handleError(aMessage) : null; 1.63 + } catch (e) { 1.64 + dump("settings 'error' callback threw an exception, dropping: " + e + "\n"); 1.65 + } 1.66 + }, 1.67 + 1.68 + process: function process() { 1.69 + debug("process!"); 1.70 + let lock = this; 1.71 + lock._open = false; 1.72 + let store = lock._transaction.objectStore(SETTINGSSTORE_NAME); 1.73 + 1.74 + while (!lock._requests.isEmpty()) { 1.75 + if (lock._isBusy) { 1.76 + return; 1.77 + } 1.78 + let info = lock._requests.dequeue(); 1.79 + if (DEBUG) debug("info:" + info.intent); 1.80 + let callback = info.callback; 1.81 + let name = info.name; 1.82 + switch (info.intent) { 1.83 + case "set": 1.84 + let value = info.value; 1.85 + let message = info.message; 1.86 + if(DEBUG && typeof(value) == 'object') { 1.87 + debug("object name:" + name + ", val: " + JSON.stringify(value)); 1.88 + } 1.89 + lock._isBusy = true; 1.90 + let checkKeyRequest = store.get(name); 1.91 + 1.92 + checkKeyRequest.onsuccess = function (event) { 1.93 + let defaultValue; 1.94 + if (event.target.result) { 1.95 + defaultValue = event.target.result.defaultValue; 1.96 + } else { 1.97 + defaultValue = null; 1.98 + if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + name + " is not in the database.\n"); 1.99 + } 1.100 + let setReq = store.put({ settingName: name, defaultValue: defaultValue, userValue: value }); 1.101 + 1.102 + setReq.onsuccess = function() { 1.103 + lock._isBusy = false; 1.104 + lock._open = true; 1.105 + lock.callHandle(callback, name, value); 1.106 + Services.obs.notifyObservers(lock, "mozsettings-changed", JSON.stringify({ 1.107 + key: name, 1.108 + value: value, 1.109 + message: message 1.110 + })); 1.111 + lock._open = false; 1.112 + lock.process(); 1.113 + }; 1.114 + 1.115 + setReq.onerror = function(event) { 1.116 + lock._isBusy = false; 1.117 + lock.callError(callback, event.target.errorMessage); 1.118 + lock.process(); 1.119 + }; 1.120 + } 1.121 + 1.122 + checkKeyRequest.onerror = function(event) { 1.123 + lock._isBusy = false; 1.124 + lock.callError(callback, event.target.errorMessage); 1.125 + lock.process(); 1.126 + }; 1.127 + break; 1.128 + case "get": 1.129 + let getReq = store.mozGetAll(name); 1.130 + getReq.onsuccess = function(event) { 1.131 + if (DEBUG) { 1.132 + debug("Request successful. Record count:" + event.target.result.length); 1.133 + debug("result: " + JSON.stringify(event.target.result)); 1.134 + } 1.135 + this._open = true; 1.136 + if (callback) { 1.137 + if (event.target.result[0]) { 1.138 + if (event.target.result.length > 1) { 1.139 + if (DEBUG) debug("Warning: overloaded setting:" + name); 1.140 + } 1.141 + let result = event.target.result[0]; 1.142 + let value = result.userValue !== undefined 1.143 + ? result.userValue 1.144 + : result.defaultValue; 1.145 + lock.callHandle(callback, name, value); 1.146 + } else { 1.147 + lock.callHandle(callback, name, null); 1.148 + } 1.149 + } else { 1.150 + if (DEBUG) debug("no callback defined!"); 1.151 + } 1.152 + this._open = false; 1.153 + }.bind(lock); 1.154 + getReq.onerror = function error(event) { 1.155 + lock.callError(callback, event.target.errorMessage); 1.156 + }; 1.157 + break; 1.158 + } 1.159 + } 1.160 + lock._open = true; 1.161 + }, 1.162 + 1.163 + createTransactionAndProcess: function(aCallback) { 1.164 + if (this._settingsService._settingsDB._db) { 1.165 + let lock; 1.166 + while (lock = this._settingsService._locks.dequeue()) { 1.167 + if (!lock._transaction) { 1.168 + lock._transaction = lock._settingsService._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite"); 1.169 + if (aCallback) { 1.170 + lock._transaction.oncomplete = aCallback.handle; 1.171 + lock._transaction.onabort = function(event) { 1.172 + let message = ''; 1.173 + if (event.target.error) { 1.174 + message = event.target.error.name + ': ' + event.target.error.message; 1.175 + } 1.176 + this.callAbort(aCallback, message); 1.177 + }; 1.178 + } 1.179 + } 1.180 + if (!lock._isBusy) { 1.181 + lock.process(); 1.182 + } else { 1.183 + this._settingsService._locks.enqueue(lock); 1.184 + return; 1.185 + } 1.186 + } 1.187 + if (!this._requests.isEmpty() && !this._isBusy) { 1.188 + this.process(); 1.189 + } 1.190 + } 1.191 + }, 1.192 + 1.193 + get: function get(aName, aCallback) { 1.194 + if (DEBUG) debug("get: " + aName + ", " + aCallback); 1.195 + this._requests.enqueue({ callback: aCallback, intent:"get", name: aName }); 1.196 + this.createTransactionAndProcess(); 1.197 + }, 1.198 + 1.199 + set: function set(aName, aValue, aCallback, aMessage) { 1.200 + debug("set: " + aName + ": " + JSON.stringify(aValue)); 1.201 + if (aMessage === undefined) 1.202 + aMessage = null; 1.203 + this._requests.enqueue({ callback: aCallback, 1.204 + intent: "set", 1.205 + name: aName, 1.206 + value: this._settingsService._settingsDB.prepareValue(aValue), 1.207 + message: aMessage }); 1.208 + this.createTransactionAndProcess(); 1.209 + }, 1.210 + 1.211 + classID : SETTINGSSERVICELOCK_CID, 1.212 + QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]) 1.213 +}; 1.214 + 1.215 +const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}"); 1.216 + 1.217 +function SettingsService() 1.218 +{ 1.219 + debug("settingsService Constructor"); 1.220 + this._locks = new Queue(); 1.221 + this._settingsDB = new SettingsDB(); 1.222 + this._settingsDB.init(); 1.223 +} 1.224 + 1.225 +SettingsService.prototype = { 1.226 + 1.227 + nextTick: function nextTick(aCallback, thisObj) { 1.228 + if (thisObj) 1.229 + aCallback = aCallback.bind(thisObj); 1.230 + 1.231 + Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL); 1.232 + }, 1.233 + 1.234 + createLock: function createLock(aCallback) { 1.235 + var lock = new SettingsServiceLock(this); 1.236 + this._locks.enqueue(lock); 1.237 + this._settingsDB.ensureDB( 1.238 + function() { lock.createTransactionAndProcess(aCallback); }, 1.239 + function() { dump("SettingsService failed to open DB!\n"); } 1.240 + ); 1.241 + this.nextTick(function() { this._open = false; }, lock); 1.242 + return lock; 1.243 + }, 1.244 + 1.245 + classID : SETTINGSSERVICE_CID, 1.246 + QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService]) 1.247 +} 1.248 + 1.249 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock])