Bug 743336 - Settings API: Add service. r=fabrice
authorGregor Wagner <gwagner@mozilla.com>
Fri, 27 Apr 2012 16:21:31 -0700
changeset 92624 df3acd8332803a52c5fd106b4f913f73f5f1d410
parent 92623 75b367f73319c4f850d5384ef0282bd51b9a1e10
child 92625 f40e503d68402755a9f774e0167424957361f18e
push id1116
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 19:38:18 +0000
treeherdermozilla-esr52@26dcd1b1a208 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs743336
milestone15.0a1
Bug 743336 - Settings API: Add service. r=fabrice
b2g/installer/package-manifest.in
dom/interfaces/settings/Makefile.in
dom/interfaces/settings/nsISettingsService.idl
dom/settings/Makefile.in
dom/settings/SettingsDB.jsm
dom/settings/SettingsManager.js
dom/settings/SettingsQueue.jsm
dom/settings/SettingsService.js
dom/settings/SettingsService.manifest
dom/settings/tests/Makefile.in
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -403,16 +403,18 @@
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
+@BINPATH@/components/SettingsService.js
+@BINPATH@/components/SettingsService.manifest
 @BINPATH@/components/nsFilePicker.js
 @BINPATH@/components/nsFilePicker.manifest
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/NetworkManager.manifest
 @BINPATH@/components/NetworkManager.js
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/RILContentHelper.js
--- a/dom/interfaces/settings/Makefile.in
+++ b/dom/interfaces/settings/Makefile.in
@@ -10,11 +10,12 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 XPIDL_MODULE   = dom_settings
 GRE_MODULE     = 1
 
 XPIDLSRCS =                           \
             nsIDOMSettingsManager.idl \
+            nsISettingsService.idl    \
             $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/settings/nsISettingsService.idl
@@ -0,0 +1,27 @@
+/* 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/. */
+
+#include "domstubs.idl"
+
+[scriptable, uuid(83d67430-8516-11e1-b0c4-0800200c9a66)]
+interface nsISettingsServiceCallback : nsISupports
+{
+  [implicit_jscontext]
+  void handle(in DOMString aName, in jsval aResult);
+  [implicit_jscontext]
+  void handleError(in DOMString aErrorMessage);
+};
+
+[scriptable, uuid(3ab3cbc0-8513-11e1-b0c4-0800200c9a66)]
+interface nsISettingsServiceLock : nsISupports
+{
+  void set(in string aName, in jsval aValue, in nsISettingsServiceCallback aCallback);
+  void get(in string aName, in nsISettingsServiceCallback aCallback);
+};
+
+[scriptable, uuid(3458e760-8513-11e1-b0c4-0800200c9a66)]
+interface nsISettingsService : nsISupports
+{
+  nsISettingsServiceLock getLock();
+};
--- a/dom/settings/Makefile.in
+++ b/dom/settings/Makefile.in
@@ -13,16 +13,23 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 LIBRARY_NAME   = jsdomsettings_s
 LIBXUL_LIBRARY = 1
 
 EXTRA_COMPONENTS =         \
   SettingsManager.js       \
   SettingsManager.manifest \
+  SettingsService.js \
+  SettingsService.manifest \
+  $(NULL)
+
+EXTRA_JS_MODULES =   \
+  SettingsQueue.jsm  \
+  SettingsDB.jsm     \
   $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += tests
 endif
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
 # subdirectory (and the ipc one).
new file mode 100644
--- /dev/null
+++ b/dom/settings/SettingsDB.jsm
@@ -0,0 +1,36 @@
+/* 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/. */
+
+let EXPORTED_SYMBOLS = ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"];
+
+/* static functions */
+let DEBUG = 0;
+if (DEBUG) {
+  debug = function (s) { dump("-*- SettingsDB: " + s + "\n"); }
+} else {
+  debug = function (s) {}
+}
+
+const SETTINGSDB_NAME = "settings";
+const SETTINGSDB_VERSION = 1;
+const SETTINGSSTORE_NAME = "settings";
+
+Components.utils.import("resource://gre/modules/IndexedDBHelper.jsm");
+
+function SettingsDB() {}
+
+SettingsDB.prototype = {
+
+  __proto__: IndexedDBHelper.prototype,
+
+  createSchema: function createSchema(aDb) {
+    let objectStore = aDb.createObjectStore(SETTINGSSTORE_NAME, { keyPath: "settingName" });
+    objectStore.createIndex("settingValue", "settingValue", { unique: false });
+    debug("Created object stores and indexes");
+  },
+
+  init: function init(aGlobal) {
+      this.initDBHelper(SETTINGSDB_NAME, SETTINGSDB_VERSION, SETTINGSSTORE_NAME, aGlobal);
+  }
+}
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -6,67 +6,22 @@
 
 /* static functions */
 let DEBUG = 0;
 if (DEBUG)
   debug = function (s) { dump("-*- SettingsManager: " + s + "\n"); }
 else
   debug = function (s) {}
 
-function Queue() {
-  this._queue = [];
-  this._index = 0;
-}
-
-Queue.prototype = {
-  getLength: function() { return (this._queue.length - this._index); },
-
-  isEmpty: function() { return (this._queue.length == 0); },
-
-  enqueue: function(item) { this._queue.push(item); },
-
-  dequeue: function() {
-    if(this.isEmpty())
-      return undefined;
-
-    var item = this._queue[this._index];
-    if (++this._index * 2 >= this._queue.length){
-      this._queue  = this._queue.slice(this._index);
-      this._index = 0;
-    }
-    return item;
-  }
-}
-
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
-
-const DB_NAME = "settings";
-const DB_VERSION = 1;
-const STORE_NAME = "settings";
-
-function SettingsDB() {}
-
-SettingsDB.prototype = {
-  __proto__: IndexedDBHelper.prototype,
-
-  createSchema: function createSchema(aDb) {
-    let objectStore = aDb.createObjectStore(STORE_NAME, { keyPath: "settingName" });
-    objectStore.createIndex("settingValue", "settingValue", { unique: false });
-    debug("Created object stores and indexes");
-  },
-
-  init: function init(aGlobal) {
-      this.initDBHelper(DB_NAME, DB_VERSION, STORE_NAME, aGlobal);
-  }
-}
-
+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 SETTINGSLOCK_CONTRACTID = "@mozilla.org/settingsLock;1";
 const SETTINGSLOCK_CID        = Components.ID("{ef95ddd0-6308-11e1-b86c-0800200c9a66}");
 const nsIDOMSettingsLock      = Ci.nsIDOMSettingsLock;
 
@@ -78,17 +33,17 @@ function SettingsLock(aSettingsManager)
   this._transaction = null;
 }
 
 SettingsLock.prototype = {
 
   process: function process() {
     let lock = this;
     lock._open = false;
-    let store = lock._transaction.objectStore(STORE_NAME);
+    let store = lock._transaction.objectStore(SETTINGSSTORE_NAME);
 
     while (!lock._requests.isEmpty()) {
       let info = lock._requests.dequeue();
       debug("info:" + info.intent);
       let request = info.request;
       switch (info.intent) {
         case "clear":
           let req = store.clear();
@@ -147,17 +102,17 @@ SettingsLock.prototype = {
     lock._open = true;
   },
 
   createTransactionAndProcess: function() {
     if (this._settingsManager._settingsDB._db) {
       var lock;
       while (lock = this._settingsManager._locks.dequeue()) {
         if (!lock._transaction) {
-          lock._transaction = lock._settingsManager._settingsDB._db.transaction(STORE_NAME, "readwrite");
+          lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
         }
         lock.process();
       }
       if (!this._requests.isEmpty())
         this.process();
     }
   },
 
new file mode 100644
--- /dev/null
+++ b/dom/settings/SettingsQueue.jsm
@@ -0,0 +1,30 @@
+/* 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/. */
+
+let EXPORTED_SYMBOLS = ["Queue"];
+
+function Queue() {
+  this._queue = [];
+  this._index = 0;
+}
+
+Queue.prototype = {
+  getLength: function() { return (this._queue.length - this._index); },
+
+  isEmpty: function() { return (this._queue.length == 0); },
+
+  enqueue: function(item) { this._queue.push(item); },
+
+  dequeue: function() {
+    if(this.isEmpty())
+      return undefined;
+
+    var item = this._queue[this._index];
+    if (++this._index * 2 >= this._queue.length){
+      this._queue  = this._queue.slice(this._index);
+      this._index = 0;
+    }
+    return item;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/settings/SettingsService.js
@@ -0,0 +1,183 @@
+/* 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;
+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("{3ab3cbc0-8513-11e1-b0c4-0800200c9a66}");
+const nsISettingsServiceLock         = Ci.nsISettingsServiceLock;
+
+function SettingsServiceLock(aSettingsService)
+{
+  debug("settingsServiceLock constr!");
+  this._open = true;
+  this._requests = new Queue();
+  this._settingsService = aSettingsService;
+  this._transaction = null;
+}
+
+SettingsServiceLock.prototype = {
+
+  process: function process() {
+    debug("process!");
+    let lock = this;
+    lock._open = false;
+    let store = lock._transaction.objectStore(SETTINGSSTORE_NAME);
+
+    while (!lock._requests.isEmpty()) {
+      let info = lock._requests.dequeue();
+      debug("info:" + info.intent);
+      let callback = info.callback;
+      let req;
+      let name = info.name;
+      switch (info.intent) {
+        case "set":
+          let value = info.value;
+          if(typeof(value) == 'object')
+            debug("object name:" + name + ", val: " + JSON.stringify(value));
+          req = store.put({settingName: name, settingValue: value});
+
+          req.onsuccess = function() {
+            debug("set on success");
+            lock._open = true;
+            if (callback)
+              callback.handle(name, value);
+            Services.obs.notifyObservers(lock, "mozsettings-changed", JSON.stringify({
+              key: name,
+              value: value
+            }));
+            lock._open = false;
+          };
+
+          req.onerror = function(event) { callback ? callback.handleError(event.target.errorMessage) : null; };
+          break;
+        case "get":
+          req = store.getAll(name);
+          req.onsuccess = function(event) {
+            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) {
+                  debug("Warning: overloaded setting:" + name);
+                }
+                callback.handle(name, event.target.result[0].settingValue);
+              } else
+                callback.handle(name, null);
+            } else {
+              debug("no callback defined!");
+            }
+            this._open = false;
+          }.bind(lock);
+          req.onerror = function error(event) { callback ? callback.handleError(event.target.errorMessage) : null; };
+          break;
+      }
+    }
+    if (!lock._requests.isEmpty())
+      throw Components.results.NS_ERROR_ABORT;
+    lock._open = true;
+  },
+
+  createTransactionAndProcess: function createTransactionAndProcess() {
+    if (this._settingsService._settingsDB._db) {
+      var lock;
+      while (lock = this._settingsService._locks.dequeue()) {
+        if (!lock._transaction) {
+          lock._transaction = lock._settingsService._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
+        }
+        lock.process();
+      }
+      if (!this._requests.isEmpty())
+        this.process();
+    }
+  },
+
+  get: function get(aName, aCallback) {
+    debug("get: " + aName + ", " + aCallback);
+    this._requests.enqueue({ callback: aCallback, intent:"get", name: aName });
+    this.createTransactionAndProcess();
+  },
+
+  set: function set(aName, aValue, aCallback) {
+    debug("set: " + aName + ": " + JSON.stringify(aValue));
+    this._requests.enqueue({ callback: aCallback, intent: "set", name: aName, value: aValue});
+    this.createTransactionAndProcess();
+  },
+
+  classID : SETTINGSSERVICELOCK_CID,
+  QueryInterface : XPCOMUtils.generateQI([nsISettingsServiceLock]),
+
+  classInfo : XPCOMUtils.generateCI({classID: SETTINGSSERVICELOCK_CID,
+                                     contractID: SETTINGSSERVICELOCK_CONTRACTID,
+                                     classDescription: "SettingsServiceLock",
+                                     interfaces: [nsISettingsServiceLock],
+                                     flags: nsIClassInfo.DOM_OBJECT})
+};
+
+const SETTINGSSERVICE_CONTRACTID = "@mozilla.org/settingsService;1";
+const SETTINGSSERVICE_CID        = Components.ID("{3458e760-8513-11e1-b0c4-0800200c9a66}");
+const nsISettingsService         = Ci.nsISettingsService;
+
+let myGlobal = this;
+
+function SettingsService()
+{
+  debug("settingsService Constructor");
+  this._locks = new Queue();
+  var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
+  idbManager.initWindowless(myGlobal);
+  this._settingsDB = new SettingsDB();
+  this._settingsDB.init(myGlobal);
+}
+
+SettingsService.prototype = {
+
+  nextTick: function nextTick(aCallback, thisObj) {
+    if (thisObj)
+      aCallback = aCallback.bind(thisObj);
+
+    Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
+  },
+
+  getLock: function getLock() {
+    debug("get lock!");
+    var lock = new SettingsServiceLock(this);
+    this._locks.enqueue(lock);
+    this._settingsDB.ensureDB(
+      function() { lock.createTransactionAndProcess(); },
+      function() { dump("ensureDB error cb!\n"); },
+      myGlobal );
+    this.nextTick(function() { this._open = false; }, lock);
+    return lock;
+  },
+
+  classID : SETTINGSSERVICE_CID,
+  QueryInterface : XPCOMUtils.generateQI([nsISettingsService]),
+
+  classInfo : XPCOMUtils.generateCI({classID: SETTINGSSERVICE_CID,
+                                     contractID: SETTINGSSERVICE_CONTRACTID,
+                                     classDescription: "SettingsService",
+                                     interfaces: [nsISettingsService],
+                                     flags: nsIClassInfo.DOM_OBJECT})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsService, SettingsServiceLock])
new file mode 100644
--- /dev/null
+++ b/dom/settings/SettingsService.manifest
@@ -0,0 +1,5 @@
+component {3ab3cbc0-8513-11e1-b0c4-0800200c9a66} SettingsService.js
+contract @mozilla.org/settingsServiceLock;1 {3ab3cbc0-8513-11e1-b0c4-0800200c9a66}
+
+component {3458e760-8513-11e1-b0c4-0800200c9a66} SettingsService.js
+contract @mozilla.org/settingsService;1 {3458e760-8513-11e1-b0c4-0800200c9a66}
--- a/dom/settings/tests/Makefile.in
+++ b/dom/settings/tests/Makefile.in
@@ -6,19 +6,16 @@ DEPTH            = ../../..
 topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 relativesrcdir   = dom/settings/tests
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS = \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
   test_settings_basics.html \
   test_settings_events.html \
   $(NULL)
 
 _CHROME_TEST_FILES = \