Wrap API event handlers in try/catch blocks

This prevents one bad handler from stopping others in the queue
from being executed.

Change-Id: Id1702d0181f8c80af0b41703f3e5871f08a6e0a6
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
index 147ef9f..862a2d2 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
@@ -59,9 +59,13 @@
 
     canSubmitChange: function() {
       var submitCallbacks = this._getEventCallbacks(EventType.SUBMIT_CHANGE);
-
       var cancelSubmit = submitCallbacks.some(function(callback) {
-        return callback() === false;
+        try {
+          return callback() === false;
+        } catch (err) {
+          console.error(err);
+        }
+        return false;
       });
 
       return !cancelSubmit;
@@ -75,7 +79,11 @@
 
     _handleHistory: function(detail) {
       this._getEventCallbacks(EventType.HISTORY).forEach(function(cb) {
-        cb(detail.path);
+        try {
+          cb(detail.path);
+        } catch (err) {
+          console.error(err);
+        }
       });
     },
 
@@ -90,13 +98,21 @@
             break;
           }
         }
-        cb(change, revision);
+        try {
+          cb(change, revision);
+        } catch (err) {
+          console.error(err);
+        }
       });
     },
 
     _handleComment: function(detail) {
       this._getEventCallbacks(EventType.COMMENT).forEach(function(cb) {
-        cb(detail.node);
+        try {
+          cb(detail.node);
+        } catch (err) {
+          console.error(err);
+        }
       });
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index c10c73c..3be232e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -32,9 +32,14 @@
   suite('gr-js-api-interface tests', function() {
     var element;
     var plugin;
+    var errorStub;
+    var throwErrFn = function() {
+      throw Error('Unfortunately, this handler has stopped');
+    };
 
     setup(function() {
       element = fixture('basic');
+      errorStub = sinon.stub(console, 'error');
       Gerrit.install(function(p) { plugin = p; },
           'http://test.com/plugins/testplugin/static/test.js');
     });
@@ -42,6 +47,7 @@
     teardown(function() {
       element._removeEventCallbacks();
       plugin = null;
+      errorStub.restore();
     });
 
     test('url', function() {
@@ -51,8 +57,10 @@
     });
 
     test('history event', function(done) {
+      plugin.on(element.EventType.HISTORY, throwErrFn);
       plugin.on(element.EventType.HISTORY, function(path) {
         assert.equal(path, '/path/to/awesomesauce');
+        assert.isTrue(errorStub.calledOnce);
         done();
       });
       element.handleEvent(element.EventType.HISTORY,
@@ -67,9 +75,11 @@
           abc: {_number: 1},
         },
       };
+      plugin.on(element.EventType.SHOW_CHANGE, throwErrFn);
       plugin.on(element.EventType.SHOW_CHANGE, function(change, revision) {
         assert.deepEqual(change, testChange);
         assert.deepEqual(revision, testChange.revisions.abc);
+        assert.isTrue(errorStub.calledOnce);
         done();
       });
       element.handleEvent(element.EventType.SHOW_CHANGE,
@@ -78,19 +88,24 @@
 
     test('comment event', function(done) {
       var testCommentNode = {foo: 'bar'};
+      plugin.on(element.EventType.COMMENT, throwErrFn);
       plugin.on(element.EventType.COMMENT, function(commentNode) {
         assert.deepEqual(commentNode, testCommentNode);
+        assert.isTrue(errorStub.calledOnce);
         done();
       });
       element.handleEvent(element.EventType.COMMENT, {node: testCommentNode});
     });
 
     test('submitchange', function() {
+      plugin.on(element.EventType.SUBMIT_CHANGE, throwErrFn);
       plugin.on(element.EventType.SUBMIT_CHANGE, function() { return true; });
       assert.isTrue(element.canSubmitChange());
+      assert.isTrue(errorStub.calledOnce);
       plugin.on(element.EventType.SUBMIT_CHANGE, function() { return false; });
       plugin.on(element.EventType.SUBMIT_CHANGE, function() { return true; });
       assert.isFalse(element.canSubmitChange());
+      assert.isTrue(errorStub.calledTwice);
     });
 
   });