Add offline support! This will cache messages in memory while we are offline and send them at a later time when we come back online. Future versions may want to store this information into a database.
authorShawn Wilsher <me@shawnwilsher.com>
Tue, 01 Dec 2009 07:20:55 -0800
changeset 37 684748bcf7e2241c531302e0bf0a6548c85b8efe
parent 36 8b728f2e281f0d74486ae935ccc63f94fc3f8e8a
child 38 36f92f118371e7d2de4831a9c5e135115e8e43fc
push id21
push usersdwilsh@shawnwilsher.com
push dateTue, 01 Dec 2009 16:34:33 +0000
Add offline support! This will cache messages in memory while we are offline and send them at a later time when we come back online. Future versions may want to store this information into a database.
resource/BugzillaAPI.jsm
--- a/resource/BugzillaAPI.jsm
+++ b/resource/BugzillaAPI.jsm
@@ -49,16 +49,19 @@ let EXPORTED_SYMBOLS = [
 //// Constants
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 const SEC_ERROR_UNKNOWN_ISSUER = 2153390067;
 
+const kOfflineStatusChanged = "network:offline-status-changed";
+const kXpcomShutdown = "xpcom-shutdown";
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 this.__defineGetter__("ioService", function() {
   delete this.ioService;
   return this.ioService = Cc["@mozilla.org/network/io-service;1"].
                           getService(Ci.nsIIOService);
 });
@@ -244,16 +247,22 @@ BadCertListener.prototype = {
     Ci.nsIIntefaceRequestor,
   ]),
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Global Functions
 
 /**
+ * Holds all pending outgoing messages.
+ * XXX look into storing this in a database for atomicity.
+ */
+let gOfflineQueue = [];
+
+/**
  * @param aServer
  *        The server to connect to.
  * @param aURI
  *        The URI to connect to on aServer.
  * @param aCallback
  *        The function to call on error or completion with the following
  *        arguments:
  *        aResponseCode - the HTTP response code from the request
@@ -261,17 +270,22 @@ BadCertListener.prototype = {
  * @param aData [optional]
  *        The data to send in the request.
  */
 function performRequest(aServer,
                         aURI,
                         aCallback,
                         aData)
 {
-  // TODO handle offline
+  // If we are in offline mode, save our arguments and try when we go online.
+  if (ioService.offline) {
+    gOfflineQueue.push(arguments);
+    return;
+  }
+
   let channel = ioService.newChannel(aURI, null, null);
   channel.QueryInterface(Ci.nsIHttpChannel);
   channel.QueryInterface(Ci.nsIUploadChannel);
   let bcl = new BadCertListener(aServer.fingerprint);
   channel.notificationCallbacks = bcl;
   channel.QueryInterface(Ci.nsIHttpChannel);
   channel.setRequestHeader("Accept", "application/json", true);
   channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
@@ -315,8 +329,41 @@ function performRequest(aServer,
           aCallback(channel.responseStatus, is.read(len));
         else
           aCallback(0, null);
       },
     });
     channel.asyncOpen(listener, null);
   }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+//// Initialization
+
+(function() {
+  let os = Cc["@mozilla.org/observer-service;1"].
+           getService(Ci.nsIObserverService);
+
+  let observer = {
+    observe: function(aSubject, aTopic, aData)
+    {
+      switch (aTopic) {
+        case kOfflineStatusChanged: {
+          if (aData == "online") {
+            // Process our queue of pending events.
+            gOfflineQueue.forEach(function(args) {
+              performRequest.apply({ }, args);
+            });
+          }
+        }
+        case kXpcomShutdown: {
+          // XXX prompt user about unsent messages on shutdown (or store for
+          // later sending).
+          os.removeObserver(observer, kOfflineStatusChanged);
+          os.removeObserver(observer, kXpcomShutdown);
+        }
+      };
+    }
+  };
+
+  os.addObserver(observer, kOfflineStatusChanged, false);
+  os.addObserver(observer, kXpcomShutdown, false);
+})();