Merge changes from topic 'impersonation-2'

* changes:
  PostReview: Disallow modifying other people's drafts
  More consistency in on_behalf_of implementation
  Expand on_behalf_of tests
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 7a9c4f9..a38152a 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -18,6 +18,7 @@
   var CHECK_SIGN_IN_INTERVAL_MS = 60000;
   var SIGN_IN_WIDTH_PX = 690;
   var SIGN_IN_HEIGHT_PX = 500;
+  var TOO_MANY_FILES = 'too many files to find conflicts';
 
   Polymer({
     is: 'gr-error-manager',
@@ -38,6 +39,10 @@
       this.unlisten(document, 'network-error', '_handleNetworkError');
     },
 
+    _shouldSupressError: function(msg) {
+      return msg.indexOf(TOO_MANY_FILES) > -1;
+    },
+
     _handleServerError: function(e) {
       if (e.detail.response.status === 403) {
         this._getLoggedIn().then(function(loggedIn) {
@@ -49,7 +54,9 @@
         }.bind(this));
       } else {
         e.detail.response.text().then(function(text) {
-          this._showAlert('Server error: ' + text);
+          if (!this._shouldSupressError(text)) {
+            this._showAlert('Server error: ' + text);
+          }
         }.bind(this));
       }
     },
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
index f633a7e..44cbde0 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
@@ -70,6 +70,20 @@
       });
     });
 
+    test('suppress TOO_MANY_FILES error', function(done) {
+      var showAlertStub = sandbox.stub(element, '_showAlert');
+      var textSpy = sandbox.spy(function() {
+        return Promise.resolve('too many files to find conflicts');
+      });
+      element.fire('server-error', {response: {status: 500, text: textSpy}});
+
+      assert.isTrue(textSpy.called);
+      textSpy.lastCall.returnValue.then(function() {
+        assert.isFalse(showAlertStub.called);
+        done();
+      });
+    });
+
     test('show network error', function(done) {
       var consoleErrorStub = sandbox.stub(console, 'error');
       var showAlertStub = sandbox.stub(element, '_showAlert');
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
index 7653655..d10567c 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
@@ -15,6 +15,7 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 
 <dom-module id="gr-reporting">
   <script src="gr-reporting.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 5236a1c..32dd687 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -33,6 +33,8 @@
   var CHANGE_VIEW_REGEX = /^\/c\/\d+\/?\d*$/;
   var DIFF_VIEW_REGEX = /^\/c\/\d+\/\d+\/.+$/;
 
+  var pending = [];
+
   Polymer({
     is: 'gr-reporting',
 
@@ -40,7 +42,7 @@
       _baselines: {
         type: Array,
         value: function() { return {}; },
-      }
+      },
     },
 
     get performanceTiming() {
@@ -51,8 +53,13 @@
       return Math.round(10 * window.performance.now()) / 10;
     },
 
-    reporter: function(type, category, eventName, eventValue) {
-      eventValue = eventValue;
+    reporter: function() {
+      var report = (Gerrit._arePluginsLoaded() && !pending.length) ?
+        this.defaultReporter : this.cachingReporter;
+      report.apply(this, arguments);
+    },
+
+    defaultReporter: function(type, category, eventName, eventValue) {
       var detail = {
         type: type,
         category: category,
@@ -63,6 +70,19 @@
       console.log(eventName + ': ' + eventValue);
     },
 
+    cachingReporter: function(type, category, eventName, eventValue) {
+      if (Gerrit._arePluginsLoaded()) {
+        if (pending.length) {
+          pending.splice(0).forEach(function(args) {
+            this.reporter.apply(this, args);
+          }, this);
+        }
+        this.reporter(type, category, eventName, eventValue);
+      } else {
+        pending.push([type, category, eventName, eventValue]);
+      }
+    },
+
     /**
      * User-perceived app start time, should be reported when the app is ready.
      */
@@ -105,6 +125,10 @@
           NAVIGATION.TYPE, NAVIGATION.CATEGORY, NAVIGATION.PAGE, page);
     },
 
+    pluginsLoaded: function() {
+      this.timeEnd('PluginsLoaded');
+    },
+
     _getPathname: function() {
       return window.location.pathname;
     },
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index b9d07fc..082f81b 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -90,6 +90,48 @@
       ));
     });
 
+    suite('plugins', function() {
+      setup(function() {
+        element.reporter.restore();
+        sandbox.stub(element, 'defaultReporter');
+        sandbox.stub(Gerrit, '_arePluginsLoaded');
+      });
+
+      test('pluginsLoaded reports time', function() {
+        Gerrit._arePluginsLoaded.returns(true);
+        var nowStub = sinon.stub(element, 'now').returns(42);
+        element.pluginsLoaded();
+        assert.isTrue(element.defaultReporter.calledWithExactly(
+            'timing-report', 'UI Latency', 'PluginsLoaded', 42
+        ));
+      });
+
+      test('caches reports if plugins are not loaded', function() {
+        Gerrit._arePluginsLoaded.returns(false);
+        element.timeEnd('foo');
+        assert.isFalse(element.defaultReporter.called);
+      });
+
+      test('reports if plugins are loaded', function() {
+        Gerrit._arePluginsLoaded.returns(true);
+        element.timeEnd('foo');
+        assert.isTrue(element.defaultReporter.called);
+      });
+
+      test('reports cached events preserving order', function() {
+        Gerrit._arePluginsLoaded.returns(false);
+        element.timeEnd('foo');
+        Gerrit._arePluginsLoaded.returns(true);
+        element.timeEnd('bar');
+        assert.isTrue(element.defaultReporter.firstCall.calledWith(
+            'timing-report', 'UI Latency', 'foo'
+        ));
+        assert.isTrue(element.defaultReporter.secondCall.calledWith(
+            'timing-report', 'UI Latency', 'bar'
+        ));
+      });
+    });
+
     suite('location changed', function() {
       var pathnameStub;
       setup(function() {
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 2c7a999..1580609 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -110,10 +110,12 @@
     },
 
     _loadPlugins: function(plugins) {
+      Gerrit._setPluginsCount(plugins.length);
       for (var i = 0; i < plugins.length; i++) {
         var scriptEl = document.createElement('script');
         scriptEl.defer = true;
         scriptEl.src = '/' + plugins[i];
+        scriptEl.onerror = Gerrit._pluginInstalled;
         document.body.appendChild(scriptEl);
       }
     },
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index c5c68ea..b04cd4d 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -78,5 +78,11 @@
       assert.equal(gwtLink.href,
           'http://' + location.host + '/?polygerrit=0#/c/1/1/testfile.txt@2');
     });
+
+    test('sets plugins count', function() {
+      sandbox.stub(Gerrit, '_setPluginsCount');
+      element._loadPlugins([]);
+      assert.isTrue(Gerrit._setPluginsCount.calledWithExactly(0));
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index 1967b80..5c0535b 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -14,6 +14,7 @@
 limitations under the License.
 -->
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
 
 <dom-module id="gr-js-api-interface">
@@ -23,4 +24,3 @@
   <script src="gr-js-api-interface.js"></script>
   <script src="gr-public-js-api.js"></script>
 </dom-module>
-
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 97e0a18..2e7c53d 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
@@ -172,5 +172,49 @@
       });
     });
 
+    test('_setPluginsCount', function(done) {
+      stub('gr-reporting', {
+        pluginsLoaded: function() {
+          assert.equal(Gerrit._pluginsPending, 0);
+          done();
+        }
+      });
+      Gerrit._setPluginsCount(0);
+    });
+
+    test('_arePluginsLoaded', function() {
+      assert.isFalse(Gerrit._arePluginsLoaded());
+      Gerrit._setPluginsCount(1);
+      assert.isFalse(Gerrit._arePluginsLoaded());
+      Gerrit._setPluginsCount(0);
+      assert.isTrue(Gerrit._arePluginsLoaded());
+    });
+
+    test('_pluginInstalled', function(done) {
+      stub('gr-reporting', {
+        pluginsLoaded: function() {
+          done();
+        }
+      });
+      Gerrit._setPluginsCount(2);
+      Gerrit._pluginInstalled();
+      assert.equal(Gerrit._pluginsPending, 1);
+      Gerrit._pluginInstalled();
+    });
+
+    test('install calls _pluginInstalled', function() {
+      var stub = sinon.stub(Gerrit, '_pluginInstalled');
+      Gerrit.install(function(p) { plugin = p; }, '0.1',
+          'http://test.com/plugins/testplugin/static/test.js');
+      assert.isTrue(stub.calledOnce);
+      stub.restore();
+    });
+
+    test('install calls _pluginInstalled on error', function() {
+      var stub = sinon.stub(Gerrit, '_pluginInstalled');
+      Gerrit.install(function() {}, '0.0pre-alpha');
+      assert.isTrue(stub.calledOnce);
+      stub.restore();
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 21d76f1..ad8c135 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -64,6 +64,9 @@
 
   var Gerrit = window.Gerrit || {};
 
+  // Number of plugins to initialize, -1 means 'not yet known'.
+  Gerrit._pluginsPending = -1;
+
   Gerrit.getPluginName = function() {
     console.warn('Gerrit.getPluginName is not supported in PolyGerrit.',
         'Please use self.getPluginName() instead.');
@@ -85,12 +88,14 @@
     if (opt_version && opt_version !== API_VERSION) {
       console.warn('Only version ' + API_VERSION +
           ' is supported in PolyGerrit. ' + opt_version + ' was given.');
+      Gerrit._pluginInstalled();
       return;
     }
 
     // TODO(andybons): Polyfill currentScript for IE10/11 (edge supports it).
     var src = opt_src || (document.currentScript && document.currentScript.src);
     callback(new Plugin(src));
+    Gerrit._pluginInstalled();
   };
 
   Gerrit.getLoggedIn = function() {
@@ -101,5 +106,20 @@
     // NOOP since PolyGerrit doesn’t support GWT plugins.
   };
 
+  Gerrit._setPluginsCount = function(count) {
+    Gerrit._pluginsPending = count;
+    if (Gerrit._arePluginsLoaded()) {
+      document.createElement('gr-reporting').pluginsLoaded();
+    }
+  };
+
+  Gerrit._pluginInstalled = function() {
+    Gerrit._setPluginsCount(Gerrit._pluginsPending - 1);
+  };
+
+  Gerrit._arePluginsLoaded = function() {
+    return Gerrit._pluginsPending === 0;
+  };
+
   window.Gerrit = Gerrit;
 })(window);