Merge "Add support for dueToMove chunks in gr-diff"
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index a773276..b4ae469 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -379,13 +379,13 @@
 notifications of these events by implementing the corresponding
 listeners.
 
-* `com.google.gerrit.common.EventListener`:
+* `com.google.gerrit.server.events.EventListener`:
 +
 Allows to listen to events without user visibility restrictions. These
 are the same link:cmd-stream-events.html#events[events] that are also streamed by
 the link:cmd-stream-events.html[gerrit stream-events] command.
 
-* `com.google.gerrit.common.UserScopedEventListener`:
+* `com.google.gerrit.server.events.UserScopedEventListener`:
 +
 Allows to listen to events visible to the specified user. These are the
 same link:cmd-stream-events.html#events[events] that are also streamed
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
index 6f8a863..77d02c1 100644
--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -68,7 +68,7 @@
 
     Set<String> enabledExperiments = experimentData(urlParameterMap);
     if (!enabledExperiments.isEmpty()) {
-      data.put("enabledExperiments", serializeObject(GSON, enabledExperiments));
+      data.put("enabledExperiments", serializeObject(GSON, enabledExperiments).toString());
     }
     return data.build();
   }
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 2504419..ce08a86 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -2138,7 +2138,8 @@
     }
 
     this._updateCheckTimerHandle = this.async(() => {
-      fetchChangeUpdates(this._change, this.$.restAPI).then(result => {
+      const change = this._change;
+      fetchChangeUpdates(change, this.$.restAPI).then(result => {
         let toastMessage = null;
         if (!result.isLatest) {
           toastMessage = ReloadToastMessage.NEWER_REVISION;
@@ -2152,7 +2153,12 @@
           toastMessage = ReloadToastMessage.NEW_MESSAGE;
         }
 
-        if (!toastMessage) {
+        // We have to make sure that the update is still relevant for the user.
+        // Since starting to fetch the change update the user may have sent a
+        // reply, or the change might have been reloaded, or it could be in the
+        // process of being reloaded.
+        const changeWasReloaded = change !== this._change;
+        if (!toastMessage || this._loading || changeWasReloaded) {
           this._startUpdateCheckTimer();
           return;
         }
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
index 3d530b5..179caaa 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
@@ -1971,7 +1971,7 @@
         assert.isFalse(getChangeDetailStub.called);
       });
 
-      test('_startUpdateCheckTimer up-to-date', () => {
+      test('_startUpdateCheckTimer up-to-date', async () => {
         const getChangeDetailStub =
             sinon.stub(element.$.restAPI, 'getChangeDetail')
                 .callsFake(() => Promise.resolve(generateChange({
@@ -1981,8 +1981,9 @@
                 })));
 
         element._serverConfig = {change: {update_delay: 12345}};
+        await flush();
 
-        assert.isTrue(element._startUpdateCheckTimer.called);
+        assert.equal(element._startUpdateCheckTimer.callCount, 2);
         assert.isTrue(getChangeDetailStub.called);
         assert.equal(element.async.lastCall.args[1], 12345 * 1000);
       });
@@ -2001,6 +2002,24 @@
           done();
         });
         element._serverConfig = {change: {update_delay: 12345}};
+
+        assert.equal(element._startUpdateCheckTimer.callCount, 1);
+      });
+
+      test('_startUpdateCheckTimer respects _loading', async () => {
+        sinon.stub(element.$.restAPI, 'getChangeDetail')
+            .callsFake(() => Promise.resolve(generateChange({
+              // new patchset was uploaded
+              revisionsCount: 2,
+              messagesCount: 1,
+            })));
+
+        element._loading = true;
+        element._serverConfig = {change: {update_delay: 12345}};
+        await flush();
+
+        // No toast, instead a second call to _startUpdateCheckTimer().
+        assert.equal(element._startUpdateCheckTimer.callCount, 2);
       });
 
       test('_startUpdateCheckTimer new status shows an alert', done => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 90acad3..8d57085 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -1210,9 +1210,10 @@
 
   _onLineSelected(e, detail) {
     if (!this._change) { return; }
-    const cursorAddress = this.$.cursor.getAddress();
-    const number = cursorAddress ? cursorAddress.number : undefined;
-    const leftSide = cursorAddress ? cursorAddress.leftSide : undefined;
+    const number = detail.number;
+    // for on-comment-anchor-tap side can be PARENT/REVISIONS
+    // for on-line-selected side can be LEFT/RIGHT
+    const leftSide = detail.side === 'LEFT' || detail.side === 'PARENT';
     const url = GerritNav.getUrlForDiffById(this._changeNum,
         this._change.project, this._path, this._patchRange.patchNum,
         this._patchRange.basePatchNum, number, leftSide);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index 8b5a972..62963c8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -1272,20 +1272,6 @@
       assert.isTrue(getUrlStub.called);
     });
 
-    test('_onLineSelected w/o line address', () => {
-      const getUrlStub = sinon.stub(GerritNav, 'getUrlForDiffById');
-      sinon.stub(history, 'replaceState');
-      sinon.stub(element.$.cursor, 'moveToLineNumber');
-      sinon.stub(element.$.cursor, 'getAddress').returns(null);
-      element._changeNum = 321;
-      element._change = {_number: 321, project: 'foo/bar'};
-      element._patchRange = {basePatchNum: '3', patchNum: '5'};
-      element._onLineSelected({}, {number: 123, side: 'right'});
-      assert.isTrue(getUrlStub.calledOnce);
-      assert.isUndefined(getUrlStub.lastCall.args[5]);
-      assert.isUndefined(getUrlStub.lastCall.args[6]);
-    });
-
     test('_getDiffViewMode', () => {
       // No user prefs or change view state set.
       assert.equal(element._getDiffViewMode(), 'SIDE_BY_SIDE');