Bug 844558 - Send contacts from parent to child in chunks. r=gwagner a=tef+
authorReuben Morais <reuben.morais@gmail.com>
Sat, 23 Feb 2013 03:41:41 -0800
changeset 118655 4931ec89ebbe
parent 118654 03a9ad79a72d
child 118656 c516d7e67150
push id69
push userreuben.morais@gmail.com
push date2013-03-22 04:00 +0000
reviewersgwagner, tef
bugs844558
milestone18.0
Bug 844558 - Send contacts from parent to child in chunks. r=gwagner a=tef+
dom/contacts/ContactManager.js
dom/contacts/fallback/ContactDB.jsm
dom/contacts/fallback/ContactService.jsm
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -393,16 +393,20 @@ ContactManager.prototype = {
   _fireSuccessOrDone: function(aCursor, aResult) {
     if (aResult == null) {
       Services.DOMRequest.fireDone(aCursor);
     } else {
       Services.DOMRequest.fireSuccess(aCursor, aResult);
     }
   },
 
+  _pushArray: function(aArr1, aArr2) {
+    aArr1.push.apply(aArr1, aArr2);
+  },
+
   receiveMessage: function(aMessage) {
     if (DEBUG) debug("receiveMessage: " + aMessage.name);
     let msg = aMessage.json;
     let contacts = msg.contacts;
 
     let req;
     switch (aMessage.name) {
       case "Contacts:Find:Return:OK":
@@ -411,24 +415,26 @@ ContactManager.prototype = {
           let result = this._convertContacts(contacts);
           Services.DOMRequest.fireSuccess(req.request, result);
         } else {
           if (DEBUG) debug("no request stored!" + msg.requestID);
         }
         break;
       case "Contacts:GetAll:Next":
         let data = this._cursorData[msg.cursorId];
-        let contact = msg.contact ? this._convertContact(msg.contact) : null;
+        let result = contacts ? this._convertContacts(contacts) : [null];
         if (data.waitingForNext) {
           if (DEBUG) debug("cursor waiting for contact, sending");
           data.waitingForNext = false;
+          let contact = result.shift();
+          this._pushArray(data.cachedContacts, result);
           this.nextTick(this._fireSuccessOrDone.bind(this, data.cursor, contact));
         } else {
           if (DEBUG) debug("cursor not waiting, saving");
-          data.cachedContacts.push(contact);
+          this._pushArray(data.cachedContacts, result);
         }
         break;
       case "Contacts:GetSimContacts:Return:OK":
         req = this.getRequest(msg.requestID);
         if (req) {
           let result = contacts.map(function(c) {
             let contact = new Contact();
             let prop = {name: [c.alphaId], tel: [ { value: c.number } ]};
@@ -629,17 +635,18 @@ ContactManager.prototype = {
     Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
   handleContinue: function CM_handleContinue(aCursorId) {
     if (DEBUG) debug("handleContinue: " + aCursorId);
     let data = this._cursorData[aCursorId];
     if (data.cachedContacts.length > 0) {
       if (DEBUG) debug("contact in cache");
-      this.nextTick(this._fireSuccessOrDone.bind(this, data.cursor, data.cachedContacts.shift()));
+      let contact = data.cachedContacts.shift();
+      this.nextTick(this._fireSuccessOrDone.bind(this, data.cursor, contact));
     } else {
       if (DEBUG) debug("waiting for contact");
       data.waitingForNext = true;
     }
   },
 
   remove: function removeContact(aRecord) {
     let request;
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -16,16 +16,17 @@ const Ci = Components.interfaces;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 
 const DB_NAME = "contacts";
 const DB_VERSION = 8;
 const STORE_NAME = "contacts";
 const SAVED_GETALL_STORE_NAME = "getallcache";
+const CHUNK_SIZE = 20;
 
 this.ContactDB = function ContactDB(aGlobal) {
   if (DEBUG) debug("Constructor");
   this._global = aGlobal;
 }
 
 ContactDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
@@ -498,57 +499,74 @@ ContactDB.prototype = {
     // Here we try to get the cached results for query `aQuery'. If they don't
     // exist, it means the cache was invalidated and needs to be recreated, so
     // we do that. Otherwise, we just return the existing cache.
     this.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) {
       let req = store.get(aQuery);
       req.onsuccess = function(e) {
         if (e.target.result) {
           if (DEBUG) debug("cache exists");
-          debug("sending: " + JSON.stringify(e.target.result));
           aSuccessCb(e.target.result, false);
         } else {
           if (DEBUG) debug("creating cache for query " + aQuery);
           this.createCacheForQuery(aQuery, aSuccessCb);
         }
       }.bind(this);
       req.onerror = function() {
 
       };
     }.bind(this));
   },
 
+  //TODO Use Timer.jsm (bug 840360) when it's available on b2g18
+  nextTick: function nextTick(aCallback, thisObj) {
+    if (thisObj)
+      aCallback = aCallback.bind(thisObj);
+
+    Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
+  },
+
   getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions) {
+    if (DEBUG) debug("getAll")
     let optionStr = JSON.stringify(aOptions);
     this.getCacheForQuery(optionStr, function(aCachedResults, aFullContacts) {
       // aFullContacts is true if the cache didn't exist and had to be created.
       // In that case, we receive the full contacts since we already have them
       // in memory to create the cache anyway. This allows us to avoid accessing
       // the main object store again.
       if (aCachedResults && aCachedResults.length > 0) {
-        if (DEBUG) debug("query returned at least one contact");
+        if (DEBUG) debug("query returned " + aCachedResults.length + " contacts");
         if (aFullContacts) {
           if (DEBUG) debug("full contacts: " + aCachedResults.length);
-          for (let i = 0; i < aCachedResults.length; ++i) {
-            aSuccessCb(aCachedResults[i]);
+          while(aCachedResults.length) {
+            aSuccessCb(aCachedResults.splice(0, CHUNK_SIZE));
           }
           aSuccessCb(null);
         } else {
           let count = 0;
-          this.newTxn("readonly", STORE_NAME, function(txn, store) {
-            for (let i = 0; i < aCachedResults.length; ++i) {
-              store.get(aCachedResults[i]).onsuccess = function(e) {
-                aSuccessCb(e.target.result);
-                count++;
-                if (count == aCachedResults.length) {
-                  aSuccessCb(null);
-                }
-              };
-            }
-          });
+          let sendChunk = function(start) {
+            let chunk = [];
+            this.newTxn("readonly", STORE_NAME, function(txn, store) {
+              for (let i = start; i < Math.min(start+CHUNK_SIZE, aCachedResults.length); ++i) {
+                store.get(aCachedResults[i]).onsuccess = function(e) {
+                  chunk.push(e.target.result);
+                  count++;
+                  if (count == aCachedResults.length) {
+                    aSuccessCb(chunk);
+                    aSuccessCb(null);
+                  } else if (chunk.length == CHUNK_SIZE) {
+                    aSuccessCb(chunk);
+                    chunk.length = 0;
+                    this.nextTick(sendChunk.bind(this, start+CHUNK_SIZE));
+                  }
+                };
+              }
+            });
+          }.bind(this);
+          sendChunk(0);
         }
       } else { // no contacts
         if (DEBUG) debug("query returned no contacts");
         aSuccessCb(null);
       }
     }.bind(this));
   },
 
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -109,21 +109,21 @@ this.DOMContactManager = {
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this),
           msg.options.findOptions);
         break;
       case "Contacts:GetAll":
         if (!this.assertPermission(aMessage, "contacts-read")) {
           return null;
         }
         this._db.getAll(
-          function(aContact) {
-            mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contact: aContact});
+          function(aContacts) {
+            mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts});
           },
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
-          msg.findOptions, msg.cursorId);
+          msg.findOptions);
         break;
       case "Contact:Save":
         if (msg.options.reason === "create") {
           if (!this.assertPermission(aMessage, "contacts-create")) {
             return null;
           }
         } else {
           if (!this.assertPermission(aMessage, "contacts-write")) {