Merge "Account for border and padding in commit message box size"
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
index c921234..d486f1e 160000
--- a/plugins/plugin-manager
+++ b/plugins/plugin-manager
@@ -1 +1 @@
-Subproject commit c921234cec4c0d058b35a8d117f55fce4d4c5e65
+Subproject commit d486f1edac54c63c46f92c93fa15958f89cfe60f
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
index abebc26..518a013 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
@@ -108,6 +108,20 @@
         } else if (unresolvedOnly) {
           return c.unresolved;
         } else {
+          const comments = c && c.thread && c.thread.comments;
+          let robotComment = false;
+          let humanReplyToRobotComment = false;
+          comments.forEach(comment => {
+            if (comment.robot_id) {
+              robotComment = true;
+            } else if (robotComment) {
+              // Robot comment exists and human comment exists after it
+              humanReplyToRobotComment = true;
+            }
+          });
+          if (robotComment) {
+            return humanReplyToRobotComment ? c : false;
+          }
           return c;
         }
       }).map(threadInfo => threadInfo.thread);
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
index bd4a6ac..ff65aa8 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
@@ -170,6 +170,68 @@
           rootId: 'zcf0b9fa_fe1a5f62',
           start_datetime: '2018-02-09 18:49:18.000000000',
         },
+        {
+          comments: [
+            {
+              __path: '/COMMIT_MSG',
+              author: {
+                _account_id: 1000000,
+                name: 'user',
+                username: 'user',
+              },
+              patch_set: 4,
+              id: 'rc1',
+              line: 5,
+              updated: '2019-02-08 18:49:18.000000000',
+              message: 'test',
+              unresolved: true,
+              robot_id: 'rc1',
+            },
+          ],
+          patchNum: 4,
+          path: '/COMMIT_MSG',
+          line: 5,
+          rootId: 'rc1',
+          start_datetime: '2019-02-08 18:49:18.000000000',
+        },
+        {
+          comments: [
+            {
+              __path: '/COMMIT_MSG',
+              author: {
+                _account_id: 1000000,
+                name: 'user',
+                username: 'user',
+              },
+              patch_set: 4,
+              id: 'rc2',
+              line: 5,
+              updated: '2019-03-08 18:49:18.000000000',
+              message: 'test',
+              unresolved: true,
+              robot_id: 'rc2',
+            },
+            {
+              __path: '/COMMIT_MSG',
+              author: {
+                _account_id: 1000000,
+                name: 'user',
+                username: 'user',
+              },
+              patch_set: 4,
+              id: 'c2_1',
+              line: 5,
+              updated: '2019-03-08 18:49:18.000000000',
+              message: 'test',
+              unresolved: true,
+            },
+          ],
+          patchNum: 4,
+          path: '/COMMIT_MSG',
+          line: 5,
+          rootId: 'rc2',
+          start_datetime: '2019-03-08 18:49:18.000000000',
+        },
       ];
       flushAsynchronousOperations();
       threadElements = Polymer.dom(element.root)
@@ -194,46 +256,69 @@
     });
 
     test('_computeSortedThreads', () => {
-      assert.equal(element._sortedThreads.length, 5);
+      assert.equal(element._sortedThreads.length, 7);
       // Draft and unresolved
       assert.equal(element._sortedThreads[0].thread.rootId,
           'ecf0b9fa_fe1a5f62');
-      // unresolved
+      // Unresolved robot comment
       assert.equal(element._sortedThreads[1].thread.rootId,
+          'rc2');
+      // Unresolved robot comment
+      assert.equal(element._sortedThreads[2].thread.rootId,
+          'rc1');
+      // unresolved
+      assert.equal(element._sortedThreads[3].thread.rootId,
           'scaddf38_44770ec1');
       // unresolved
-      assert.equal(element._sortedThreads[2].thread.rootId,
+      assert.equal(element._sortedThreads[4].thread.rootId,
           '8caddf38_44770ec1');
       // resolved and draft
-      assert.equal(element._sortedThreads[3].thread.rootId,
+      assert.equal(element._sortedThreads[5].thread.rootId,
           'zcf0b9fa_fe1a5f62');
       // resolved
-      assert.equal(element._sortedThreads[4].thread.rootId,
+      assert.equal(element._sortedThreads[6].thread.rootId,
           '09a9fb0a_1484e6cf');
     });
 
+    test('filtered threads do not contain robot comments without reply', () => {
+      const thread = element.threads.find(thread => thread.rootId === 'rc1');
+      assert.equal(element._filteredThreads.includes(thread), false);
+    });
+
+    test('filtered threads contains robot comments with reply', () => {
+      const thread = element.threads.find(thread => thread.rootId === 'rc2');
+      assert.equal(element._filteredThreads.includes(thread), true);
+    });
+
+
     test('thread removal', () => {
-      threadElements[1].fire('thread-discard', {rootId: 'scaddf38_44770ec1'});
+      threadElements[1].fire('thread-discard', {rootId: 'rc2'});
       flushAsynchronousOperations();
-      assert.equal(element._sortedThreads.length, 4);
+      assert.equal(element._sortedThreads.length, 6);
       assert.equal(element._sortedThreads[0].thread.rootId,
           'ecf0b9fa_fe1a5f62');
-      // unresolved
+      // Unresolved robot comment
       assert.equal(element._sortedThreads[1].thread.rootId,
+          'rc1');
+      // unresolved
+      assert.equal(element._sortedThreads[2].thread.rootId,
+          'scaddf38_44770ec1');
+      // unresolved
+      assert.equal(element._sortedThreads[3].thread.rootId,
           '8caddf38_44770ec1');
       // resolved and draft
-      assert.equal(element._sortedThreads[2].thread.rootId,
+      assert.equal(element._sortedThreads[4].thread.rootId,
           'zcf0b9fa_fe1a5f62');
       // resolved
-      assert.equal(element._sortedThreads[3].thread.rootId,
+      assert.equal(element._sortedThreads[5].thread.rootId,
           '09a9fb0a_1484e6cf');
     });
 
-    test('toggle unresolved only shows unressolved comments', () => {
+    test('toggle unresolved only shows unresolved comments', () => {
       MockInteractions.tap(element.$.unresolvedToggle);
       flushAsynchronousOperations();
       assert.equal(Polymer.dom(element.root)
-          .querySelectorAll('gr-comment-thread').length, 3);
+          .querySelectorAll('gr-comment-thread').length, 5);
     });
 
     test('toggle drafts only shows threads with draft comments', () => {
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 6306933..c62daf9 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -271,7 +271,8 @@
     },
 
     /**
-     * Page load time, should be reported at any time after navigation.
+     * Page load time and other metrics, should be reported at any time
+     * after navigation.
      */
     pageLoaded() {
       if (this.performanceTiming.loadEventEnd === 0) {
@@ -281,7 +282,35 @@
         const loadTime = this.performanceTiming.loadEventEnd -
             this.performanceTiming.navigationStart;
         this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
-            TIMING.PAGE_LOADED, loadTime);
+            TIMING.PAGE_LOADED, loadTime, true);
+
+        const requestStart = this.performanceTiming.requestStart -
+            this.performanceTiming.navigationStart;
+        this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+            'requestStart', requestStart, true);
+
+        const responseEnd = this.performanceTiming.responseEnd -
+            this.performanceTiming.navigationStart;
+        this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+            'responseEnd', responseEnd, true);
+
+        const domLoading = this.performanceTiming.domLoading -
+          this.performanceTiming.navigationStart;
+        this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+            'domLoading', domLoading, true);
+
+        const domContentLoadedEventStart =
+          this.performanceTiming.domContentLoadedEventStart -
+          this.performanceTiming.navigationStart;
+        this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+            'domContentLoadedEventStart', domContentLoadedEventStart, true);
+
+        if (this.performanceTiming.redirectEnd > 0) {
+          const redirectEnd = this.performanceTiming.redirectEnd -
+              this.performanceTiming.navigationStart;
+          this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+              'redirectEnd', redirectEnd, true);
+        }
       }
     },
 
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 163bba5..736fa54 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
@@ -84,7 +84,8 @@
       assert.isTrue(
           element.reporter.calledWithExactly(
               'timing-report', 'UI Latency', 'Page Loaded',
-              fakePerformance.loadEventEnd - fakePerformance.navigationStart)
+              fakePerformance.loadEventEnd - fakePerformance.navigationStart,
+              true)
       );
     });
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
index 11bea8c..283b7fd 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -95,7 +95,7 @@
         image._width = imageEl.naturalWidth;
         this._updateImageLabel(section, className, image);
       }.bind(this);
-      imageEl.src = 'data:' + image.type + ';base64, ' + image.body;
+      imageEl.setAttribute('src', `data:${image.type};base64, ${image.body}`);
       imageEl.addEventListener('error', () => {
         imageEl.remove();
         td.textContent = '[Image failed to load]';