|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict" |
|
6 |
|
7 /* static functions */ |
|
8 let DEBUG = 0; |
|
9 let debug; |
|
10 if (DEBUG) |
|
11 debug = function (s) { dump("-*- SettingsService: " + s + "\n"); } |
|
12 else |
|
13 debug = function (s) {} |
|
14 |
|
15 const Ci = Components.interfaces; |
|
16 const Cu = Components.utils; |
|
17 |
|
18 Cu.import("resource://gre/modules/SettingsQueue.jsm"); |
|
19 Cu.import("resource://gre/modules/SettingsDB.jsm"); |
|
20 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
21 Cu.import("resource://gre/modules/Services.jsm"); |
|
22 |
|
23 const nsIClassInfo = Ci.nsIClassInfo; |
|
24 |
|
25 const SETTINGSSERVICELOCK_CONTRACTID = "@mozilla.org/settingsServiceLock;1"; |
|
26 const SETTINGSSERVICELOCK_CID = Components.ID("{d7a395a0-e292-11e1-834e-1761d57f5f99}"); |
|
27 const nsISettingsServiceLock = Ci.nsISettingsServiceLock; |
|
28 |
|
29 function SettingsServiceLock(aSettingsService) |
|
30 { |
|
31 if (DEBUG) debug("settingsServiceLock constr!"); |
|
32 this._open = true; |
|
33 this._busy = false; |
|
34 this._requests = new Queue(); |
|
35 this._settingsService = aSettingsService; |
|
36 this._transaction = null; |
|
37 } |
|
38 |
|
39 SettingsServiceLock.prototype = { |
|
40 |
|
41 callHandle: function callHandle(aCallback, aName, aValue) { |
|
42 try { |
|
43 aCallback ? aCallback.handle(aName, aValue) : null; |
|
44 } catch (e) { |
|
45 dump("settings 'handle' callback threw an exception, dropping: " + e + "\n"); |
|
46 } |
|
47 }, |
|
48 |
|
49 callAbort: function callAbort(aCallback, aMessage) { |
|
50 try { |
|
51 aCallback ? aCallback.handleAbort(aMessage) : null; |
|
52 } catch (e) { |
|
53 dump("settings 'abort' callback threw an exception, dropping: " + e + "\n"); |
|
54 } |
|
55 }, |
|
56 |
|
57 callError: function callError(aCallback, aMessage) { |
|
58 try { |
|
59 aCallback ? aCallback.handleError(aMessage) : null; |
|
60 } catch (e) { |
|
61 dump("settings 'error' callback threw an exception, dropping: " + e + "\n"); |
|
62 } |
|
63 }, |
|
64 |
|
65 process: function process() { |
|
66 debug("process!"); |
|
67 let lock = this; |
|
68 lock._open = false; |
|
69 let store = lock._transaction.objectStore(SETTINGSSTORE_NAME); |
|
70 |
|
71 while (!lock._requests.isEmpty()) { |
|
72 if (lock._isBusy) { |
|
73 return; |
|
74 } |
|
75 let info = lock._requests.dequeue(); |
|
76 if (DEBUG) debug("info:" + info.intent); |
|
77 let callback = info.callback; |
|
78 let name = info.name; |
|
79 switch (info.intent) { |
|
80 case "set": |
|
81 let value = info.value; |
|
82 let message = info.message; |
|
83 if(DEBUG && typeof(value) == 'object') { |
|
84 debug("object name:" + name + ", val: " + JSON.stringify(value)); |
|
85 } |
|
86 lock._isBusy = true; |
|
87 let checkKeyRequest = store.get(name); |
|
88 |
|
89 checkKeyRequest.onsuccess = function (event) { |
|
90 let defaultValue; |
|
91 if (event.target.result) { |
|
92 defaultValue = event.target.result.defaultValue; |
|
93 } else { |
|
94 defaultValue = null; |
|
95 if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + name + " is not in the database.\n"); |
|
96 } |
|
97 let setReq = store.put({ settingName: name, defaultValue: defaultValue, userValue: value }); |
|
98 |
|
99 setReq.onsuccess = function() { |
|
100 lock._isBusy = false; |
|
101 lock._open = true; |
|
102 lock.callHandle(callback, name, value); |
|
103 Services.obs.notifyObservers(lock, "mozsettings-changed", JSON.stringify({ |
|
104 key: name, |
|
105 value: value, |
|
106 message: message |
|
107 })); |
|
108 lock._open = false; |
|
109 lock.process(); |
|
110 }; |
|
111 |
|
112 setReq.onerror = function(event) { |
|
113 lock._isBusy = false; |
|
114 lock.callError(callback, event.target.errorMessage); |
|
115 lock.process(); |
|
116 }; |
|
117 } |
|
118 |
|
119 checkKeyRequest.onerror = function(event) { |
|
120 lock._isBusy = false; |
|
121 lock.callError(callback, event.target.errorMessage); |
|
122 lock.process(); |
|
123 }; |
|
124 break; |
|
125 case "get": |
|
126 let getReq = store.mozGetAll(name); |
|
127 getReq.onsuccess = function(event) { |
|
128 if (DEBUG) { |
|
129 debug("Request successful. Record count:" + event.target.result.length); |
|
130 debug("result: " + JSON.stringify(event.target.result)); |
|
131 } |
|
132 this._open = true; |
|
133 if (callback) { |
|
134 if (event.target.result[0]) { |
|
135 if (event.target.result.length > 1) { |
|
136 if (DEBUG) debug("Warning: overloaded setting:" + name); |
|
137 } |
|
138 let result = event.target.result[0]; |
|
139 let value = result.userValue !== undefined |
|
140 ? result.userValue |
|
141 : result.defaultValue; |
|
142 lock.callHandle(callback, name, value); |
|
143 } else { |
|
144 lock.callHandle(callback, name, null); |
|
145 } |
|
146 } else { |
|
147 if (DEBUG) debug("no callback defined!"); |
|
148 } |
|
149 this._open = false; |
|
150 }.bind(lock); |
|
151 getReq.onerror = function error(event) { |
|
152 lock.callError(callback, event.target.errorMessage); |
|
153 }; |
|
154 break; |
|
155 } |
|
156 } |
|
157 lock._open = true; |
|
158 }, |
|
159 |
|
160 createTransactionAndProcess: function(aCallback) { |
|
161 if (this._settingsService._settingsDB._db) { |
|
162 let lock; |
|
163 while (lock = this._settingsService._locks.dequeue()) { |
|
164 if (!lock._transaction) { |
|
165 lock._transaction = lock._settingsService._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite"); |
|
166 if (aCallback) { |
|
167 lock._transaction.oncomplete = aCallback.handle; |
|
168 lock._transaction.onabort = function(event) { |
|
169 let message = ''; |
|
170 if (event.target.error) { |
|
171 message = event.target.error.name + ': ' + event.target.error.message; |
|
172 } |
|
173 this.callAbort(aCallback, message); |
|
174 }; |
|
175 } |
|
176 } |
|
177 if (!lock._isBusy) { |
|
178 lock.process(); |
|
179 } else { |
|
180 this._settingsService._locks.enqueue(lock); |
|
181 return; |
|
182 } |
|
183 } |
|
184 if (!this._requests.isEmpty() && !this._isBusy) { |
|
185 this.process(); |
|
186 } |
|
187 } |
|
188 }, |
|
189 |
|
190 get: function get(aName, aCallback) { |
|
191 if (DEBUG) debug("get: " + aName + ", " + aCallback); |
|
192 this._requests.enqueue({ callback: aCallback, intent:"get", name: aName }); |
|
193 this.createTransactionAndProcess(); |
|
194 }, |
|
195 |
|
196 set: function set(aName, aValue, aCallback, aMessage) { |
|
197 debug("set: " + aName + ": " + JSON.stringify(aValue)); |
|
198 if (aMessage === undefined) |
|
199 aMessage = null; |
|
200 this._requests.enqueue({ callback: aCallback, |
|
201 intent: "set", |
|
202 name: aName, |
|
203 value: this._settingsService._settingsDB.prepareValue(aValue), |
|
204 message: aMessage }); |
|
205 this.createTransactionAndProcess(); |
|
206 }, |
|
207 |
|
208 classID : SETTINGSSERVICELOCK_CID, |
|
209 QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]) |
|
210 }; |
|
211 |
|
212 const SETTINGSSERVICE_CID = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}"); |
|
213 |
|
214 function SettingsService() |
|
215 { |
|
216 debug("settingsService Constructor"); |
|
217 this._locks = new Queue(); |
|
218 this._settingsDB = new SettingsDB(); |
|
219 this._settingsDB.init(); |
|
220 } |
|
221 |
|
222 SettingsService.prototype = { |
|
223 |
|
224 nextTick: function nextTick(aCallback, thisObj) { |
|
225 if (thisObj) |
|
226 aCallback = aCallback.bind(thisObj); |
|
227 |
|
228 Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL); |
|
229 }, |
|
230 |
|
231 createLock: function createLock(aCallback) { |
|
232 var lock = new SettingsServiceLock(this); |
|
233 this._locks.enqueue(lock); |
|
234 this._settingsDB.ensureDB( |
|
235 function() { lock.createTransactionAndProcess(aCallback); }, |
|
236 function() { dump("SettingsService failed to open DB!\n"); } |
|
237 ); |
|
238 this.nextTick(function() { this._open = false; }, lock); |
|
239 return lock; |
|
240 }, |
|
241 |
|
242 classID : SETTINGSSERVICE_CID, |
|
243 QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService]) |
|
244 } |
|
245 |
|
246 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock]) |