Bug 1137469 - If an uncaught exception occurs whilst processing an action, the dispatcher can fail, rendering parts of Loop inactive. r=mikedeboer,a=sledru
authorMark Banner <standard8@mozilla.com>
Fri, 27 Feb 2015 10:51:14 +0000
changeset 250082 c0698821db6c
parent 250081 4a73e4bc3ee5
child 250083 193fb1410c0d
push id4499
push usermbanner@mozilla.com
push date2015-02-27 17:23 +0000
treeherdermozilla-beta@c0698821db6c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer, sledru
bugs1137469
milestone37.0
Bug 1137469 - If an uncaught exception occurs whilst processing an action, the dispatcher can fail, rendering parts of Loop inactive. r=mikedeboer,a=sledru
browser/components/loop/content/shared/js/dispatcher.js
browser/components/loop/test/shared/dispatcher_test.js
--- a/browser/components/loop/content/shared/js/dispatcher.js
+++ b/browser/components/loop/content/shared/js/dispatcher.js
@@ -67,17 +67,21 @@ loop.Dispatcher = (function() {
 
       this._active = true;
 
       if (this._debug) {
         console.log("[Dispatcher] Dispatching action", action);
       }
 
       registeredStores.forEach(function(store) {
-        store[type](action);
+        try {
+          store[type](action);
+        } catch (x) {
+          console.error("[Dispatcher] Dispatching action caused an exception: ", x);
+        }
       });
 
       this._active = false;
       this._dispatchNextAction();
     }
   };
 
   return Dispatcher;
--- a/browser/components/loop/test/shared/dispatcher_test.js
+++ b/browser/components/loop/test/shared/dispatcher_test.js
@@ -99,16 +99,41 @@ describe("loop.Dispatcher", function () 
       dispatcher.dispatch(cancelAction);
       dispatcher.dispatch(getDataAction);
 
       sinon.assert.calledOnce(cancelStore1.cancelCall);
       sinon.assert.calledOnce(getDataStore1.getWindowData);
       sinon.assert.calledOnce(getDataStore2.getWindowData);
     });
 
+    describe("Error handling", function() {
+      beforeEach(function() {
+        sandbox.stub(console, "error");
+      });
+
+      it("should handle uncaught exceptions", function() {
+        getDataStore1.getWindowData.throws("Uncaught Error");
+
+        dispatcher.dispatch(getDataAction);
+        dispatcher.dispatch(cancelAction);
+
+        sinon.assert.calledOnce(getDataStore1.getWindowData);
+        sinon.assert.calledOnce(getDataStore2.getWindowData);
+        sinon.assert.calledOnce(cancelStore1.cancelCall);
+      });
+
+      it("should log uncaught exceptions", function() {
+        getDataStore1.getWindowData.throws("Uncaught Error");
+
+        dispatcher.dispatch(getDataAction);
+
+        sinon.assert.calledOnce(console.error);
+      });
+    });
+
     describe("Queued actions", function() {
       beforeEach(function() {
         // Restore the stub, so that we can easily add a function to be
         // returned. Unfortunately, sinon doesn't make this easy.
         sandbox.stub(connectStore1, "connectCall", function() {
           dispatcher.dispatch(getDataAction);
 
           sinon.assert.notCalled(getDataStore1.getWindowData);