Merge "Merge branch 'stable-3.1'"
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-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index e36587b..45df471 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -136,7 +136,8 @@
         word-break: break-word;
       }
       #commitMessageEditor {
-        min-width: 72ch;
+        /* Account for border and padding */
+        min-width: calc(72ch + 2px + 2*var(--spacing-m));
       }
       .editCommitMessage {
         margin-top: var(--spacing-l);
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..ed73055 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -142,6 +142,22 @@
   };
   catchErrors();
 
+  // PerformanceObserver interface is a browser API.
+  if (PerformanceObserver) {
+    const catchLongJsTasks = new PerformanceObserver(list => {
+      for (const task of list.getEntries()) {
+        // We are interested in longtask longer than 200 ms (default is 50 ms)
+        if (task.duration > 200) {
+          GrReporting.prototype.reporter(TIMING.TYPE,
+              TIMING.CATEGORY_UI_LATENCY, `Task ${task.name}`,
+              Math.round(task.duration), false);
+        }
+      }
+    });
+    catchLongJsTasks.observe({entryTypes: ['longtask']});
+  }
+
+
   // The Polymer pass of JSCompiler requires this to be reassignable
   // eslint-disable-next-line prefer-const
   let GrReporting = Polymer({
@@ -271,7 +287,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 +298,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]';