Merge "Estimate render times for file list rows"
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index b59742e..3f26628 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -283,6 +283,7 @@
           as="file"
           initial-count="[[fileListIncrement]]"
           target-framerate="1">
+        [[_reportRenderedRow(index)]]
         <div class="stickyArea">
           <div class$="file-row row [[_computePathClass(file.__path, _expandedFilePaths.*)]]"
               data-path$="[[file.__path]]" tabindex="-1">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 0fa037c..c1eb39c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -26,6 +26,8 @@
   const SIZE_BAR_GAP_WIDTH = 1;
   const SIZE_BAR_MIN_WIDTH = 1.5;
 
+  const RENDER_TIME = 'FileListRenderTime';
+
   const FileStatus = {
     A: 'Added',
     C: 'Copied',
@@ -797,6 +799,12 @@
     _computeFilesShown(numFilesShown, files) {
       const filesShown = files.base.slice(0, numFilesShown);
       this.fire('files-shown-changed', {length: filesShown.length});
+
+      // Start the timer for the rendering work hwere because this is where the
+      // _shownFiles property is being set, and _shownFiles is used in the
+      // dom-repeat binding.
+      this.$.reporting.time(RENDER_TIME);
+
       return filesShown;
     },
 
@@ -1175,5 +1183,21 @@
     _noDiffsExpanded() {
       return this.filesExpanded === GrFileListConstants.FilesExpandedState.NONE;
     },
+
+    /**
+     * Method to call via binding when each file list row is rendered. This
+     * allows approximate detection of when the dom-repeat has completed
+     * rendering.
+     * @param {number} index The index of the row being rendered.
+     * @return {string} an empty string.
+     */
+    _reportRenderedRow(index) {
+      if (index === this._shownFiles.length - 1) {
+        this.async(() => {
+          this.$.reporting.timeEnd(RENDER_TIME);
+        }, 1);
+      }
+      return '';
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index 8541edf..0e0a39e 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -127,6 +127,19 @@
       assert.isTrue(controlRow.classList.contains('invisible'));
     });
 
+    test('rendering each row calls the _reportRenderedRow method', () => {
+      const renderedStub = sandbox.stub(element, '_reportRenderedRow');
+      element._filesByPath = _.range(10)
+          .reduce((_filesByPath, i) => {
+            _filesByPath['/file' + i] = {lines_inserted: 9};
+            return _filesByPath;
+          }, {});
+      flushAsynchronousOperations();
+      assert.equal(
+          Polymer.dom(element.root).querySelectorAll('.file-row').length, 10);
+      assert.equal(renderedStub.callCount, 10);
+    });
+
     test('calculate totals for patch number', () => {
       element._filesByPath = {
         '/COMMIT_MSG': {