Bug 743336 - Settings API: Add service. r=fabrice
authorGregor Wagner <gwagner@mozilla.com>
Fri, 27 Apr 2012 16:21:31 -0700
changeset 96708 df3acd8332803a52c5fd106b4f913f73f5f1d410
parent 96707 75b367f73319c4f850d5384ef0282bd51b9a1e10
child 96709 f40e503d68402755a9f774e0167424957361f18e
push id1116
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 19:38:18 +0000
treeherdermozilla-beta@95f959a8b4dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs743336
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 = \