Title: [210880] trunk/Websites/perf.webkit.org
Revision
210880
Author
rn...@webkit.org
Date
2017-01-18 13:22:39 -0800 (Wed, 18 Jan 2017)

Log Message

Make calls to render() functions async
https://bugs.webkit.org/show_bug.cgi?id=167151

Reviewed by Andreas Kling.

Make calls to render() async by coalescing calls inside enqueueToRender(), which has been renamed from
updateRendering(). We now queue up all the components and wait until the next animation frame to invoke
render() on all those components.

This reduces render() calls in the summary page of our internal dashboard by 15x from ~9400 to ~600 by
eliminating pathological O(n^2) behavior between render() calls.

Consolidated TimeSeriesChart's enqueueRender into this newly added feature of ComponentBase along with
the support to call render() on a resize event. New implementation makes use of connectedCallback and
disconnectedCallback to avoid the work when the component is not in a document tree.

The rest of the patch concerns with renaming updateRendering to enqueueToRender and fixing a few minor bugs
that I encountered while working on this patch.

* browser-tests/component-base-tests.js: Added tests for ComponentBase.enqueueToRender().
* browser-tests/index.html:
(BrowserContext.prototype.importScripts): Renamed from importScript; Now supports loading multiple scripts.
(BrowserContext.prototype.importScript): Added.
(BrowserContext): Removed the unused createWithScripts.

* public/v3/components/analysis-results-viewer.js:
(AnalysisResultsViewer.prototype._expandBetween):
* public/v3/components/bar-graph-group.js:
(BarGraphGroup.prototype.updateGroupRendering):

* public/v3/components/base.js:
(ComponentBase): When the browser doesn't support custom elements and 
(ComponentBase.prototype.enqueueToRender): Renamed from updateRendering. Queues up the component to call
render() instead of immediately invoking it.
(ComponentBase.renderingTimerDidFire): Call render(). Since render() function often calls enqueueToRender
on child components, go ahead and invoke render() on any components enqueued during render() calls.
(ComponentBase._connectedComponentToRenderOnResize): Added.
(ComponentBase._disconnectedComponentToRenderOnResize): Added.
(ComponentBase.defineElement.elementClass.prototype.connectedCallback): Added. This is an optimization to
avoid the work when the component is not in the document; e.g. because the entire page component has been
detached from the document. The old implementation in TimeSeriesChart was not doing this.
(ComponentBase.defineElement.elementClass.prototype.disconnectedCallback): Added.
(ComponentBase): Replaced unused static variables with _componentsToRender and _componentsToRenderOnResize.

* public/v3/components/chart-pane-base.js:
(ChartPaneBase.prototype.fetchAnalysisTasks):
(ChartPaneBase.prototype.didUpdateAnnotations): Added. Addresses the bug that the annotation bars in the
charts shown an an analysis task doesn't update its color when the state is updated in the UI. 
(ChartPaneBase.prototype._mainSelectionDidZoom):
(ChartPaneBase.prototype._updateStatus):
(ChartPaneBase.prototype._requestOpeningCommitViewer):
(ChartPaneBase.prototype._keyup):
(ChartPaneBase.prototype.render):
* public/v3/components/commit-log-viewer.js:
* public/v3/components/customizable-test-group-form.js:
(CustomizableTestGroupForm):
(CustomizableTestGroupForm.prototype._customize):
* public/v3/components/editable-text.js:
(EditableText.prototype._didUpdate):
* public/v3/components/interactive-time-series-chart.js:
* public/v3/components/pane-selector.js:
(PaneSelector.prototype._selectedItem):
* public/v3/components/time-series-chart.js:
(TimeSeriesChart): Removed the logic to update upon resize. See _connectedComponentToRenderOnResize above.
(TimeSeriesChart.prototype.get enqueueToRenderOnResize): Added. Returns true.
(TimeSeriesChart.prototype.enqueueToRender): Deleted.
(TimeSeriesChart._renderEnqueuedCharts): Deleted.
(TimeSeriesChart): Call ComponentBase.defineElement to make this a proper component so that the logic in
connectedCallback to update upon resize event would work.
* public/v3/instrumentation.js:
(Instrumentation.dumpStatistics): Sort results by the key names.
* public/v3/models/time-series.js:
(TimeSeries.prototype.values): Added. This method was never ported to v3 in r198462, and broke the feature
to show moving averages, etc... on the charts page.
* public/v3/pages/analysis-category-page.js:
(AnalysisCategoryPage.prototype.open):
(AnalysisCategoryPage.prototype.updateFromSerializedState):
(AnalysisCategoryPage.prototype.filterDidChange):
(AnalysisCategoryPage.prototype.render):
* public/v3/pages/analysis-task-page.js:
(AnalysisTaskChartPane.prototype._updateStatus):
(AnalysisTaskPage.prototype.updateFromSerializedState):
(AnalysisTaskPage.prototype._didFetchTask):
(AnalysisTaskPage.prototype._didFetchRelatedAnalysisTasks):
(AnalysisTaskPage.prototype._didFetchMeasurement):
(AnalysisTaskPage.prototype._didFetchTestGroups):
(AnalysisTaskPage.prototype._showAllTestGroups):
(AnalysisTaskPage.prototype._didFetchAnalysisResults):
(AnalysisTaskPage.prototype.render):
(AnalysisTaskPage.prototype._renderTestGroupList.):
(AnalysisTaskPage.prototype._renderTestGroupList):
(AnalysisTaskPage.prototype._createTestGroupListItem):
(AnalysisTaskPage.prototype._showTestGroup):
(AnalysisTaskPage.prototype._didStartEditingTaskName):
(AnalysisTaskPage.prototype._updateTaskName):
(AnalysisTaskPage.prototype._updateTestGroupName):
(AnalysisTaskPage.prototype._hideCurrentTestGroup):
(AnalysisTaskPage.prototype._updateChangeType): Fixed the bug that we were never updating annotation bars
in the main chart by calling didUpdateAnnotations.
(AnalysisTaskPage.prototype._associateBug):
(AnalysisTaskPage.prototype._dissociateBug):
(AnalysisTaskPage.prototype._associateCommit):
(AnalysisTaskPage.prototype._dissociateCommit):
(AnalysisTaskPage.prototype._chartSelectionDidChange):
(AnalysisTaskPage.prototype._selectedRowInAnalysisResultsViewer):
* public/v3/pages/build-request-queue-page.js:
(BuildRequestQueuePage.prototype.open.):
(BuildRequestQueuePage.prototype.open):
* public/v3/pages/chart-pane.js:
(ChartPane.prototype.setOpenRepository):
(ChartPane.prototype._renderTrendLinePopover): Fixed a race condition. Insert a select element as needed
before trying to assign the current value on it.
(ChartPane.prototype._trendLineTypeDidChange):
(ChartPane.prototype._updateTrendLine):
* public/v3/pages/charts-page.js:
(ChartsPage.prototype.updateFromSerializedState):
(ChartsPage.prototype._updateDomainsFromSerializedState):
(ChartsPage.prototype.setNumberOfDaysFromToolbar):
(ChartsPage.prototype._didMutatePaneList):
(ChartsPage.prototype.render):
* public/v3/pages/charts-toolbar.js:
(ChartsToolbar.prototype.render):
* public/v3/pages/create-analysis-task-page.js:
(CreateAnalysisTaskPage.prototype.updateFromSerializedState):
* public/v3/pages/dashboard-page.js:
(DashboardPage.prototype.updateFromSerializedState):
(DashboardPage.prototype._fetchedData):
* public/v3/pages/heading.js:
(Heading.prototype.render):
* public/v3/pages/page-with-heading.js:
(PageWithHeading.prototype.render):
* public/v3/pages/page.js:
(Page.prototype.open):
* public/v3/pages/summary-page.js:
(SummaryPage.prototype.open):
(SummaryPage.prototype.this._renderQueue.push):
(SummaryPage):
(SummaryPage.prototype._renderCell):

Modified Paths

Diff

Modified: trunk/Websites/perf.webkit.org/ChangeLog (210879 => 210880)


--- trunk/Websites/perf.webkit.org/ChangeLog	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/ChangeLog	2017-01-18 21:22:39 UTC (rev 210880)
@@ -1,3 +1,144 @@
+2017-01-17  Ryosuke Niwa  <rn...@webkit.org>
+
+        Make calls to render() functions async
+        https://bugs.webkit.org/show_bug.cgi?id=167151
+
+        Reviewed by Andreas Kling.
+
+        Make calls to render() async by coalescing calls inside enqueueToRender(), which has been renamed from
+        updateRendering(). We now queue up all the components and wait until the next animation frame to invoke
+        render() on all those components.
+
+        This reduces render() calls in the summary page of our internal dashboard by 15x from ~9400 to ~600 by
+        eliminating pathological O(n^2) behavior between render() calls.
+
+        Consolidated TimeSeriesChart's enqueueRender into this newly added feature of ComponentBase along with
+        the support to call render() on a resize event. New implementation makes use of connectedCallback and
+        disconnectedCallback to avoid the work when the component is not in a document tree.
+
+        The rest of the patch concerns with renaming updateRendering to enqueueToRender and fixing a few minor bugs
+        that I encountered while working on this patch.
+
+        * browser-tests/component-base-tests.js: Added tests for ComponentBase.enqueueToRender().
+        * browser-tests/index.html:
+        (BrowserContext.prototype.importScripts): Renamed from importScript; Now supports loading multiple scripts.
+        (BrowserContext.prototype.importScript): Added.
+        (BrowserContext): Removed the unused createWithScripts.
+
+        * public/v3/components/analysis-results-viewer.js:
+        (AnalysisResultsViewer.prototype._expandBetween):
+        * public/v3/components/bar-graph-group.js:
+        (BarGraphGroup.prototype.updateGroupRendering):
+
+        * public/v3/components/base.js:
+        (ComponentBase): When the browser doesn't support custom elements and 
+        (ComponentBase.prototype.enqueueToRender): Renamed from updateRendering. Queues up the component to call
+        render() instead of immediately invoking it.
+        (ComponentBase.renderingTimerDidFire): Call render(). Since render() function often calls enqueueToRender
+        on child components, go ahead and invoke render() on any components enqueued during render() calls.
+        (ComponentBase._connectedComponentToRenderOnResize): Added.
+        (ComponentBase._disconnectedComponentToRenderOnResize): Added.
+        (ComponentBase.defineElement.elementClass.prototype.connectedCallback): Added. This is an optimization to
+        avoid the work when the component is not in the document; e.g. because the entire page component has been
+        detached from the document. The old implementation in TimeSeriesChart was not doing this.
+        (ComponentBase.defineElement.elementClass.prototype.disconnectedCallback): Added.
+        (ComponentBase): Replaced unused static variables with _componentsToRender and _componentsToRenderOnResize.
+
+        * public/v3/components/chart-pane-base.js:
+        (ChartPaneBase.prototype.fetchAnalysisTasks):
+        (ChartPaneBase.prototype.didUpdateAnnotations): Added. Addresses the bug that the annotation bars in the
+        charts shown an an analysis task doesn't update its color when the state is updated in the UI. 
+        (ChartPaneBase.prototype._mainSelectionDidZoom):
+        (ChartPaneBase.prototype._updateStatus):
+        (ChartPaneBase.prototype._requestOpeningCommitViewer):
+        (ChartPaneBase.prototype._keyup):
+        (ChartPaneBase.prototype.render):
+        * public/v3/components/commit-log-viewer.js:
+        * public/v3/components/customizable-test-group-form.js:
+        (CustomizableTestGroupForm):
+        (CustomizableTestGroupForm.prototype._customize):
+        * public/v3/components/editable-text.js:
+        (EditableText.prototype._didUpdate):
+        * public/v3/components/interactive-time-series-chart.js:
+        * public/v3/components/pane-selector.js:
+        (PaneSelector.prototype._selectedItem):
+        * public/v3/components/time-series-chart.js:
+        (TimeSeriesChart): Removed the logic to update upon resize. See _connectedComponentToRenderOnResize above.
+        (TimeSeriesChart.prototype.get enqueueToRenderOnResize): Added. Returns true.
+        (TimeSeriesChart.prototype.enqueueToRender): Deleted.
+        (TimeSeriesChart._renderEnqueuedCharts): Deleted.
+        (TimeSeriesChart): Call ComponentBase.defineElement to make this a proper component so that the logic in
+        connectedCallback to update upon resize event would work.
+        * public/v3/instrumentation.js:
+        (Instrumentation.dumpStatistics): Sort results by the key names.
+        * public/v3/models/time-series.js:
+        (TimeSeries.prototype.values): Added. This method was never ported to v3 in r198462, and broke the feature
+        to show moving averages, etc... on the charts page.
+        * public/v3/pages/analysis-category-page.js:
+        (AnalysisCategoryPage.prototype.open):
+        (AnalysisCategoryPage.prototype.updateFromSerializedState):
+        (AnalysisCategoryPage.prototype.filterDidChange):
+        (AnalysisCategoryPage.prototype.render):
+        * public/v3/pages/analysis-task-page.js:
+        (AnalysisTaskChartPane.prototype._updateStatus):
+        (AnalysisTaskPage.prototype.updateFromSerializedState):
+        (AnalysisTaskPage.prototype._didFetchTask):
+        (AnalysisTaskPage.prototype._didFetchRelatedAnalysisTasks):
+        (AnalysisTaskPage.prototype._didFetchMeasurement):
+        (AnalysisTaskPage.prototype._didFetchTestGroups):
+        (AnalysisTaskPage.prototype._showAllTestGroups):
+        (AnalysisTaskPage.prototype._didFetchAnalysisResults):
+        (AnalysisTaskPage.prototype.render):
+        (AnalysisTaskPage.prototype._renderTestGroupList.):
+        (AnalysisTaskPage.prototype._renderTestGroupList):
+        (AnalysisTaskPage.prototype._createTestGroupListItem):
+        (AnalysisTaskPage.prototype._showTestGroup):
+        (AnalysisTaskPage.prototype._didStartEditingTaskName):
+        (AnalysisTaskPage.prototype._updateTaskName):
+        (AnalysisTaskPage.prototype._updateTestGroupName):
+        (AnalysisTaskPage.prototype._hideCurrentTestGroup):
+        (AnalysisTaskPage.prototype._updateChangeType): Fixed the bug that we were never updating annotation bars
+        in the main chart by calling didUpdateAnnotations.
+        (AnalysisTaskPage.prototype._associateBug):
+        (AnalysisTaskPage.prototype._dissociateBug):
+        (AnalysisTaskPage.prototype._associateCommit):
+        (AnalysisTaskPage.prototype._dissociateCommit):
+        (AnalysisTaskPage.prototype._chartSelectionDidChange):
+        (AnalysisTaskPage.prototype._selectedRowInAnalysisResultsViewer):
+        * public/v3/pages/build-request-queue-page.js:
+        (BuildRequestQueuePage.prototype.open.):
+        (BuildRequestQueuePage.prototype.open):
+        * public/v3/pages/chart-pane.js:
+        (ChartPane.prototype.setOpenRepository):
+        (ChartPane.prototype._renderTrendLinePopover): Fixed a race condition. Insert a select element as needed
+        before trying to assign the current value on it.
+        (ChartPane.prototype._trendLineTypeDidChange):
+        (ChartPane.prototype._updateTrendLine):
+        * public/v3/pages/charts-page.js:
+        (ChartsPage.prototype.updateFromSerializedState):
+        (ChartsPage.prototype._updateDomainsFromSerializedState):
+        (ChartsPage.prototype.setNumberOfDaysFromToolbar):
+        (ChartsPage.prototype._didMutatePaneList):
+        (ChartsPage.prototype.render):
+        * public/v3/pages/charts-toolbar.js:
+        (ChartsToolbar.prototype.render):
+        * public/v3/pages/create-analysis-task-page.js:
+        (CreateAnalysisTaskPage.prototype.updateFromSerializedState):
+        * public/v3/pages/dashboard-page.js:
+        (DashboardPage.prototype.updateFromSerializedState):
+        (DashboardPage.prototype._fetchedData):
+        * public/v3/pages/heading.js:
+        (Heading.prototype.render):
+        * public/v3/pages/page-with-heading.js:
+        (PageWithHeading.prototype.render):
+        * public/v3/pages/page.js:
+        (Page.prototype.open):
+        * public/v3/pages/summary-page.js:
+        (SummaryPage.prototype.open):
+        (SummaryPage.prototype.this._renderQueue.push):
+        (SummaryPage):
+        (SummaryPage.prototype._renderCell):
+
 2017-01-15  Ryosuke Niwa  <rn...@webkit.org>
 
         Add the build fix for browsers that don't yet support custom elements SPI.

Modified: trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -4,7 +4,7 @@
     function createTestToCheckExistenceOfShadowTree(callback, options = {htmlTemplate: false, cssTemplate: true})
     {
         const context = new BrowsingContext();
-        return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+        return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
             class SomeComponent extends ComponentBase { }
             if (options.htmlTemplate)
                 SomeComponent.htmlTemplate = () => { return '<div style="height: 10px;"></div>'; };
@@ -20,13 +20,13 @@
 
     describe('constructor', () => {
         it('is a function', () => {
-            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 expect(ComponentBase).toBeA('function');
             });
         });
 
         it('can be instantiated', () => {
-            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 let callCount = 0;
                 class SomeComponent extends ComponentBase {
                     constructor() {
@@ -51,7 +51,7 @@
     describe('element()', () => {
         it('must return an element', () => {
             const context = new BrowsingContext();
-            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 class SomeComponent extends ComponentBase { }
                 let instance = new SomeComponent('some-component');
                 expect(instance.element()).toBeA(context.global.HTMLElement);
@@ -59,7 +59,7 @@
         });
 
         it('must return an element whose component() matches the component', () => {
-            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return new BrowsingContext().importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 class SomeComponent extends ComponentBase { }
                 let instance = new SomeComponent('some-component');
                 expect(instance.element().component()).toBe(instance);
@@ -89,6 +89,187 @@
         });
     });
 
+    describe('enqueueToRender()', () => {
+        it('must not immediately call render()', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                context.global.requestAnimationFrame = () => {}
+
+                let renderCallCount = 0;
+                const SomeComponent = class extends ComponentBase {
+                    render() { renderCallCount++; }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                (new SomeComponent).enqueueToRender();
+                expect(renderCallCount).toBe(0);
+
+                (new SomeComponent).enqueueToRender();
+                expect(renderCallCount).toBe(0);
+            });
+        });
+
+        it('must request an animation frame exactly once', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                let requestAnimationFrameCount = 0;
+                context.global.requestAnimationFrame = () => { requestAnimationFrameCount++; }
+
+                const SomeComponent = class extends ComponentBase { }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                expect(requestAnimationFrameCount).toBe(0);
+                let instance = new SomeComponent;
+                instance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+
+                instance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+
+                (new SomeComponent).enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+
+                const AnotherComponent = class extends ComponentBase { }
+                ComponentBase.defineElement('another-component', AnotherComponent);
+                (new AnotherComponent).enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+            });
+        });
+
+        it('must invoke render() when the callback to requestAnimationFrame is called', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                let callback = null;
+                context.global.requestAnimationFrame = (newCallback) => {
+                    expect(callback).toBe(null);
+                    expect(newCallback).toNotBe(null);
+                    callback = newCallback;
+                }
+
+                let renderCalls = [];
+                const SomeComponent = class extends ComponentBase {
+                    render() {
+                        renderCalls.push(this);
+                    }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                expect(renderCalls.length).toBe(0);
+                const instance = new SomeComponent;
+                instance.enqueueToRender();
+                instance.enqueueToRender();
+
+                const anotherInstance = new SomeComponent;
+                anotherInstance.enqueueToRender();
+                expect(renderCalls.length).toBe(0);
+
+                callback();
+
+                expect(renderCalls.length).toBe(2);
+                expect(renderCalls[0]).toBe(instance);
+                expect(renderCalls[1]).toBe(anotherInstance);
+            });
+        });
+
+        it('must immediately invoke render() on a component enqueued inside another render() call', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                let callback = null;
+                context.global.requestAnimationFrame = (newCallback) => {
+                    expect(callback).toBe(null);
+                    expect(newCallback).toNotBe(null);
+                    callback = newCallback;
+                }
+
+                let renderCalls = [];
+                let instanceToEnqueue = null;
+                const SomeComponent = class extends ComponentBase {
+                    render() {
+                        renderCalls.push(this);
+                        if (instanceToEnqueue)
+                            instanceToEnqueue.enqueueToRender();
+                        instanceToEnqueue = null;
+                    }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                expect(renderCalls.length).toBe(0);
+                const instance = new SomeComponent;
+                const anotherInstance = new SomeComponent;
+                instance.enqueueToRender();
+                instanceToEnqueue = anotherInstance;
+                callback();
+                callback = null;
+                expect(renderCalls.length).toBe(2);
+                expect(renderCalls[0]).toBe(instance);
+                expect(renderCalls[1]).toBe(anotherInstance);
+                renderCalls = [];
+
+                instance.enqueueToRender();
+                anotherInstance.enqueueToRender();
+                instanceToEnqueue = instance;
+                callback();
+                expect(renderCalls.length).toBe(3);
+                expect(renderCalls[0]).toBe(instance);
+                expect(renderCalls[1]).toBe(anotherInstance);
+                expect(renderCalls[2]).toBe(instance);
+            });
+        });
+
+        it('must request a new animation frame once it exited the callback from requestAnimationFrame', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                let requestAnimationFrameCount = 0;
+                let callback = null;
+                context.global.requestAnimationFrame = (newCallback) => {
+                    expect(callback).toBe(null);
+                    expect(newCallback).toNotBe(null);
+                    callback = newCallback;
+                    requestAnimationFrameCount++;
+                }
+
+                let renderCalls = [];
+                const SomeComponent = class extends ComponentBase {
+                    render() { renderCalls.push(this); }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                const instance = new SomeComponent;
+                const anotherInstance = new SomeComponent;
+                expect(requestAnimationFrameCount).toBe(0);
+
+                instance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+                anotherInstance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(1);
+
+                expect(renderCalls.length).toBe(0);
+                callback();
+                callback = null;
+                expect(renderCalls.length).toBe(2);
+                expect(renderCalls[0]).toBe(instance);
+                expect(renderCalls[1]).toBe(anotherInstance);
+                expect(requestAnimationFrameCount).toBe(1);
+
+                anotherInstance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(2);
+                instance.enqueueToRender();
+                expect(requestAnimationFrameCount).toBe(2);
+
+                expect(renderCalls.length).toBe(2);
+                callback();
+                callback = null;
+                expect(renderCalls.length).toBe(4);
+                expect(renderCalls[0]).toBe(instance);
+                expect(renderCalls[1]).toBe(anotherInstance);
+                expect(renderCalls[2]).toBe(anotherInstance);
+                expect(renderCalls[3]).toBe(instance);
+                expect(requestAnimationFrameCount).toBe(2);
+            });
+        });
+
+    });
+
     describe('render()', () => {
         it('must create shadow tree', () => {
             return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
@@ -123,7 +304,7 @@
 
         it('must define a custom element with a class of an appropriate name', () => {
             const context = new BrowsingContext();
-            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 class SomeComponent extends ComponentBase { }
                 ComponentBase.defineElement('some-component', SomeComponent);
 
@@ -135,7 +316,7 @@
 
         it('must define a custom element that can be instantiated via document.createElement', () => {
             const context = new BrowsingContext();
-            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 let instances = [];
                 class SomeComponent extends ComponentBase {
                     constructor() {
@@ -158,7 +339,7 @@
 
         it('must define a custom element that can be instantiated via new', () => {
             const context = new BrowsingContext();
-            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) => {
                 let instances = [];
                 class SomeComponent extends ComponentBase {
                     constructor() {
@@ -179,6 +360,52 @@
             });
         });
 
+        it('must enqueue a connected component to render upon a resize event if enqueueToRenderOnResize is true', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                class SomeComponent extends ComponentBase {
+                    static get enqueueToRenderOnResize() { return true; }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                let requestAnimationFrameCount = 0;
+                let callback = null;
+                context.global.requestAnimationFrame = (newCallback) => {
+                    callback = newCallback;
+                    requestAnimationFrameCount++;
+                }
+
+                expect(requestAnimationFrameCount).toBe(0);
+                const instance = new SomeComponent;
+                context.global.dispatchEvent(new Event('resize'));
+                context.document.body.appendChild(instance.element());
+                context.global.dispatchEvent(new Event('resize'));
+                expect(requestAnimationFrameCount).toBe(1);
+            });
+        });
+
+        it('must not enqueue a disconnected component to render upon a resize event if enqueueToRenderOnResize is true', () => {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) => {
+                class SomeComponent extends ComponentBase {
+                    static get enqueueToRenderOnResize() { return true; }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                let requestAnimationFrameCount = 0;
+                let callback = null;
+                context.global.requestAnimationFrame = (newCallback) => {
+                    callback = newCallback;
+                    requestAnimationFrameCount++;
+                }
+
+                const instance = new SomeComponent;
+                expect(requestAnimationFrameCount).toBe(0);
+                context.global.dispatchEvent(new Event('resize'));
+                expect(requestAnimationFrameCount).toBe(0);
+            });
+        });
+
     });
 
 });

Modified: trunk/Websites/perf.webkit.org/browser-tests/index.html (210879 => 210880)


--- trunk/Websites/perf.webkit.org/browser-tests/index.html	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html	2017-01-18 21:22:39 UTC (rev 210880)
@@ -36,17 +36,20 @@
         this.document = this._iframe.contentDocument;
     }
 
-    importScript(path, ...symbolList)
+    importScripts(pathList, ...symbolList)
     {
         const doc = this._iframe.contentDocument;
         const global = this._iframe.contentWindow;
-        return new Promise((resolve, reject) => {
-            let script = doc.createElement('script');
-            script.addEventListener('load', resolve);
-            script.addEventListener('error', reject);
-            script.src = ""
-            doc.body.appendChild(script);
-        }).then(() => {
+
+        return Promise.all(pathList.map((path) => {
+            return new Promise((resolve, reject) => {
+                let script = doc.createElement('script');
+                script.addEventListener('load', resolve);
+                script.addEventListener('error', reject);
+                script.src = '' + path;
+                doc.body.appendChild(script);
+            });
+        })).then(() => {
             const script = doc.createElement('script');
             script.textContent = `window.importedSymbols = [${symbolList.join(', ')}];`;
             doc.body.appendChild(script);
@@ -59,36 +62,16 @@
         });
     }
 
+    importScript(path, ...symbols)
+    {
+        return this.importScripts([path], ...symbols);
+    }
+
     static cleanup()
     {
         BrowsingContext._iframes.forEach((iframe) => { iframe.remove(); });
         BrowsingContext._iframes = [];
     }
-
-    static createWithScripts(scriptList)
-    {
-        let iframe = document.createElement('iframe');
-        document.body.appendChild(iframe);
-        const doc = iframe.contentDocument;
-
-        let symbolList = [];
-        return Promise.all(scriptList.map((entry) => {
-            let [path, ...symbols] = entry;
-            symbolList = symbolList.concat(symbols);
-            return new Promise((resolve, reject) => {
-                let script = doc.createElement('script');
-                script.addEventListener('load', resolve);
-                script.addEventListener('error', reject);
-                script.src = ""
-                doc.body.appendChild(script);
-            });
-        })).then(() => {
-            const script = doc.createElement('script');
-            script.textContent = `var symbols = { ${symbolList.join(', ')} };`;
-            doc.body.appendChild(script);
-            return iframe.contentWindow;
-        });
-    }
 }
 BrowsingContext._iframes = [];
 

Modified: trunk/Websites/perf.webkit.org/public/v3/components/analysis-results-viewer.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/analysis-results-viewer.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/analysis-results-viewer.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -260,7 +260,7 @@
         for (var i = indexBeforeStart + 1; i < indexAfterEnd; i += increment)
             this._expandedPoints.add(series.findPointByIndex(i));
         this._shouldRenderTable = true;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     static htmlTemplate()

Modified: trunk/Websites/perf.webkit.org/public/v3/components/bar-graph-group.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/bar-graph-group.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/bar-graph-group.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -35,7 +35,7 @@
             var start = min - (range - diff) / 2;
 
             entry.bar.update((value - start) / range, formattedValue);
-            entry.bar.updateRendering();
+            entry.bar.enqueueToRender();
         }
 
         Instrumentation.endMeasuringTime('BarGraphGroup', 'updateGroupRendering');

Modified: trunk/Websites/perf.webkit.org/public/v3/components/base.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/base.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/base.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -15,6 +15,9 @@
 
         this._element = element;
         this._shadow = null;
+
+        if (!window.customElements && new.target.enqueueToRenderOnResize)
+            ComponentBase._connectedComponentToRenderOnResize(this);
     }
 
     element() { return this._element; }
@@ -26,13 +29,54 @@
 
     render() { this._ensureShadowTree(); }
 
-    updateRendering()
+    enqueueToRender()
     {
         Instrumentation.startMeasuringTime('ComponentBase', 'updateRendering');
-        this.render();
+
+        if (!ComponentBase._componentsToRender) {
+            ComponentBase._componentsToRender = new Set;
+            requestAnimationFrame(() => ComponentBase.renderingTimerDidFire());
+        }
+        ComponentBase._componentsToRender.add(this);
+
         Instrumentation.endMeasuringTime('ComponentBase', 'updateRendering');
     }
 
+    static renderingTimerDidFire()
+    {
+        Instrumentation.startMeasuringTime('ComponentBase', 'renderingTimerDidFire');
+
+        do {
+            const currentSet = [...ComponentBase._componentsToRender];
+            ComponentBase._componentsToRender.clear();
+            for (let component of currentSet) {
+                Instrumentation.startMeasuringTime('ComponentBase', 'renderingTimerDidFire.render');
+                component.render();
+                Instrumentation.endMeasuringTime('ComponentBase', 'renderingTimerDidFire.render');
+            }
+        } while (ComponentBase._componentsToRender.size);
+        ComponentBase._componentsToRender = null;
+
+        Instrumentation.endMeasuringTime('ComponentBase', 'renderingTimerDidFire');
+    }
+
+    static _connectedComponentToRenderOnResize(component)
+    {
+        if (!ComponentBase._componentsToRenderOnResize) {
+            ComponentBase._componentsToRenderOnResize = new Set;
+            window.addEventListener('resize', () => {
+                for (let component of ComponentBase._componentsToRenderOnResize)
+                    component.enqueueToRender();
+            });
+        }
+        ComponentBase._componentsToRenderOnResize.add(component);
+    }
+
+    static _disconnectedComponentToRenderOnResize(component)
+    {
+        ComponentBase._componentsToRenderOnResize.delete(component);
+    }
+
     renderReplace(element, content) { ComponentBase.renderReplace(element, content); }
 
     static renderReplace(element, content)
@@ -94,6 +138,8 @@
         ComponentBase._componentByName.set(name, elementInterface);
         ComponentBase._componentByClass.set(elementInterface, name);
 
+        const enqueueToRenderOnResize = elementInterface.enqueueToRenderOnResize;
+
         if (!window.customElements)
             return;
 
@@ -111,6 +157,18 @@
                 new elementInterface();
                 currentlyConstructed.delete(elementInterface);
             }
+
+            connectedCallback()
+            {
+                if (enqueueToRenderOnResize)
+                    ComponentBase._connectedComponentToRenderOnResize(this.component());
+            }
+
+            disconnectedCallback()
+            {
+                if (enqueueToRenderOnResize)
+                    ComponentBase._disconnectedComponentToRenderOnResize(this.component());
+            }
         }
 
         const nameDescriptor = Object.getOwnPropertyDescriptor(elementClass, 'name');
@@ -193,7 +251,5 @@
 ComponentBase._componentByName = new Map;
 ComponentBase._componentByClass = new Map;
 ComponentBase._currentlyConstructedByInterface = new Map;
-
-ComponentBase.css = Symbol();
-ComponentBase.html = Symbol();
-ComponentBase.map = {};
+ComponentBase._componentsToRender = null;
+ComponentBase._componentsToRenderOnResize = null;

Modified: trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -96,10 +96,17 @@
         AnalysisTask.fetchByPlatformAndMetric(this._platformId, this._metricId, noCache).then(function (tasks) {
             self._tasksForAnnotations = tasks;
             self._renderedAnnotations = false;
-            self.updateRendering();
+            self.enqueueToRender();
         });
     }
 
+    // FIXME: We should have a mechanism to get notified whenever the set of annotations change.
+    didUpdateAnnotations()
+    {
+        this._renderedAnnotations = false;
+        this.enqueueToRender();
+    }
+
     platformId() { return this._platformId; }
     metricId() { return this._metricId; }
 
@@ -132,7 +139,7 @@
     {
         this._overviewChart.setSelection(selection, this);
         this._mainChart.setSelection(null);
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _indicatorDidChange(indicatorID, isLocked)
@@ -148,7 +155,7 @@
     _updateStatus()
     {
         var range = this._mainChartStatus.updateRevisionList();
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         this._commitLogViewer.view(range.repository, range.from, range.to).then(updateRendering);
         updateRendering();
     }
@@ -165,7 +172,7 @@
     _requestOpeningCommitViewer(repository, from, to)
     {
         this._mainChartStatus.setCurrentRepository(repository);
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         this._commitLogViewer.view(repository, from, to).then(updateRendering);
         updateRendering();
     }
@@ -191,7 +198,7 @@
             return;
         }
 
-        this.updateRendering();
+        this.enqueueToRender();
 
         event.preventDefault();
         event.stopPropagation();
@@ -217,12 +224,12 @@
         this._renderAnnotations();
 
         if (this._mainChartStatus)
-            this._mainChartStatus.updateRendering();
+            this._mainChartStatus.enqueueToRender();
 
         var body = this.content().querySelector('.chart-pane-body');
         if (this._commitLogViewer && this._commitLogViewer.currentRepository()) {
             body.classList.add('has-second-sidebar');
-            this._commitLogViewer.updateRendering();
+            this._commitLogViewer.enqueueToRender();
         } else
             body.classList.remove('has-second-sidebar');
 

Modified: trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -31,7 +31,7 @@
 
         if (!to) {
             this._fetchingPromise = null;
-            this.updateRendering();
+            this.enqueueToRender();
             return Promise.resolve(null);
         }
 
@@ -41,7 +41,7 @@
 
         var self = this;
         var spinnerTimer = setTimeout(function () {
-            self.updateRendering();
+            self.enqueueToRender();
         }, 300);
 
         this._fetchingPromise.then(function (commits) {

Modified: trunk/Websites/perf.webkit.org/public/v3/components/customizable-test-group-form.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/customizable-test-group-form.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/customizable-test-group-form.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -8,7 +8,7 @@
         this._renderedRepositorylist = null;
         this._customized = false;
         this._nameControl = this.content().querySelector('.name');
-        this._nameControl._oninput_ = () => { this.updateRendering(); }
+        this._nameControl._oninput_ = () => { this.enqueueToRender(); }
         this.content().querySelector('a')._onclick_ = this._customize.bind(this);
     }
 
@@ -28,7 +28,7 @@
     {
         event.preventDefault();
         this._customized = true;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _computeRootSetMap()

Modified: trunk/Websites/perf.webkit.org/public/v3/components/editable-text.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/editable-text.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/editable-text.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -73,7 +73,7 @@
     {
         this._inEditingMode = false;
         this._updatingPromise = null;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     static htmlTemplate()

Modified: trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/interactive-time-series-chart.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -481,3 +481,5 @@
         Instrumentation.endMeasuringTime('InteractiveTimeSeriesChart', 'renderChartContent');
     }
 }
+
+ComponentBase.defineElement('interactive-time-series-chart', InteractiveTimeSeriesChart);

Modified: trunk/Websites/perf.webkit.org/public/v3/components/pane-selector.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/pane-selector.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/pane-selector.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -167,7 +167,7 @@
             if (data instanceof Metric && data.test().onlyContainsSingleMetric())
                 this._currentPath.splice(this._currentPath.length - 2, 1);
         }
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     setCallback(callback)

Modified: trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/components/time-series-chart.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -2,7 +2,7 @@
 class TimeSeriesChart extends ComponentBase {
     constructor(sourceList, options)
     {
-        super('time-series-chart');
+        super();
         this.element().style.display = 'block';
         this.element().style.position = 'relative';
         this._canvas = null;
@@ -22,12 +22,6 @@
         this._contextScaleX = 1;
         this._contextScaleY = 1;
         this._rem = null;
-
-        if (!TimeSeriesChart._chartList) {
-            TimeSeriesChart._chartList = [];
-            window.addEventListener('resize', TimeSeriesChart._updateAllCharts.bind(TimeSeriesChart));
-        }
-        TimeSeriesChart._chartList.push(this);
     }
 
     _ensureCanvas()
@@ -46,6 +40,7 @@
     }
 
     static cssTemplate() { return ''; }
+    static get enqueueToRenderOnResize() { return true; }
 
     _createCanvas()
     {
@@ -57,22 +52,6 @@
         TimeSeriesChart._chartList.map(function (chart) { chart.render(); });
     }
 
-    enqueueToRender()
-    {
-        if (!TimeSeriesChart._chartQueue) {
-            TimeSeriesChart._chartQueue = new Set;
-            window.requestAnimationFrame(TimeSeriesChart._renderEnqueuedCharts.bind(TimeSeriesChart));
-        }
-        TimeSeriesChart._chartQueue.add(this);
-    }
-
-    static _renderEnqueuedCharts()
-    {
-        for (var chart of TimeSeriesChart._chartQueue)
-            chart.updateRendering();
-        TimeSeriesChart._chartQueue = null;
-    }
-
     setDomain(startTime, endTime)
     {
         console.assert(startTime < endTime, 'startTime must be before endTime');
@@ -804,3 +783,5 @@
         return gridValues;
     }
 }
+
+ComponentBase.defineElement('time-series-chart', TimeSeriesChart);

Modified: trunk/Websites/perf.webkit.org/public/v3/instrumentation.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/instrumentation.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/instrumentation.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -37,13 +37,13 @@
         if (!this._statistics)
             return;
         var maxKeyLength = 0;
-        for (var key in this._statistics)
+        for (let key in this._statistics)
             maxKeyLength = Math.max(key.length, maxKeyLength);
 
-        for (var key in this._statistics) {
-            var item = this._statistics[key];
-            var keySuffix = ' '.repeat(maxKeyLength - key.length);
-            var log = `${key}${keySuffix}: `;
+        for (let key of Object.keys(this._statistics).sort()) {
+            const item = this._statistics[key];
+            const keySuffix = ' '.repeat(maxKeyLength - key.length);
+            let log = `${key}${keySuffix}: `;
             log += ` mean = ${item.mean.toFixed(2)} ${item.unit}`;
             if (item.unit == 'ms')
                 log += ` total = ${item.value.toFixed(2)} ${item.unit}`;

Modified: trunk/Websites/perf.webkit.org/public/v3/models/time-series.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/models/time-series.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/models/time-series.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -8,6 +8,7 @@
         this._data = [];
     }
 
+    values() { return this._data.map((point) => point.value); }
     length() { return this._data.length; }
 
     append(item)

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/analysis-category-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/analysis-category-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/analysis-category-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -23,10 +23,10 @@
         var self = this;
         AnalysisTask.fetchAll().then(function () {
             self._fetched = true;
-            self.updateRendering();
+            self.enqueueToRender();
         }, function (error) {
             self._errorMessage = 'Failed to fetch the list of analysis tasks: ' + error;
-            self.updateRendering();
+            self.enqueueToRender();
         });
         super.open(state);
     }
@@ -59,12 +59,12 @@
             this._categoryToolbar.setFilter(state.filter);
 
         if (!isOpen)
-            this.updateRendering();
+            this.enqueueToRender();
     }
 
     filterDidChange(shouldUpdateState)
     {
-        this.updateRendering();
+        this.enqueueToRender();
         if (shouldUpdateState)
             this.scheduleUrlStateUpdate();
     }
@@ -74,7 +74,7 @@
         Instrumentation.startMeasuringTime('AnalysisCategoryPage', 'render');
 
         super.render();
-        this._categoryToolbar.updateRendering();
+        this._categoryToolbar.enqueueToRender();
 
         if (this._errorMessage) {
             console.assert(!this._fetched);

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -19,7 +19,7 @@
     _updateStatus()
     {
         super._updateStatus();
-        this._page.updateRendering();
+        this._page.enqueueToRender();
     }
 
     selectedPoints()
@@ -101,7 +101,7 @@
             var taskId = parseInt(state.remainingRoute);
             AnalysisTask.fetchById(taskId).then(this._didFetchTask.bind(this), function (error) {
                 self._errorMessage = `Failed to fetch the analysis task ${state.remainingRoute}: ${error}`;
-                self.updateRendering();
+                self.enqueueToRender();
             });
             this._fetchRelatedInfoForTaskId(taskId);
         } else if (state.buildRequest) {
@@ -110,7 +110,7 @@
                 self._fetchRelatedInfoForTaskId(task.id());
             }, function (error) {
                 self._errorMessage = `Failed to fetch the analysis task for the build request ${buildRequestId}: ${error}`;
-                self.updateRendering();
+                self.enqueueToRender();
             });
         }
     }
@@ -146,7 +146,7 @@
         this._chartPane.setOverviewDomain(domain[0], domain[1]);
         this._chartPane.setMainDomain(domain[0], domain[1]);
 
-        this.updateRendering();
+        this.enqueueToRender();
 
         return task;
     }
@@ -154,7 +154,7 @@
     _didFetchRelatedAnalysisTasks(relatedTasks)
     {
         this._relatedTasks = relatedTasks;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _didFetchMeasurement()
@@ -171,7 +171,7 @@
 
         this._startPoint = startPoint;
         this._endPoint = endPoint;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _didFetchTestGroups(testGroups)
@@ -179,7 +179,7 @@
         this._testGroups = testGroups.sort(function (a, b) { return +a.createdAt() - b.createdAt(); });
         this._didUpdateTestGroupHiddenState();
         this._assignTestResultsIfPossible();
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _showAllTestGroups()
@@ -186,7 +186,7 @@
     {
         this._showHiddenTestGroups = true;
         this._didUpdateTestGroupHiddenState();
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _didUpdateTestGroupHiddenState()
@@ -206,7 +206,7 @@
     {
         this._analysisResults = results;
         if (this._assignTestResultsIfPossible())
-            this.updateRendering();
+            this.enqueueToRender();
     }
 
     _assignTestResultsIfPossible()
@@ -233,7 +233,7 @@
 
         this.content().querySelector('.error-message').textContent = this._errorMessage || '';
 
-        this._chartPane.updateRendering();
+        this._chartPane.enqueueToRender();
 
         var element = ComponentBase.createElement;
         var link = ComponentBase.createLink;
@@ -264,18 +264,18 @@
         } else
             repositoryList = Repository.sortByNamePreferringOnesWithURL(Repository.all());
 
-        this._bugList.updateRendering();
+        this._bugList.enqueueToRender();
 
         this._causeList.setKindList(repositoryList);
-        this._causeList.updateRendering();
+        this._causeList.enqueueToRender();
 
         this._fixList.setKindList(repositoryList);
-        this._fixList.updateRendering();
+        this._fixList.enqueueToRender();
 
         this.content().querySelector('.analysis-task-status').style.display = this._task ? null : 'none';
         this.content().querySelector('.overview-chart').style.display = this._task ? null : 'none';
         this.content().querySelector('.test-group-view').style.display = this._task && this._testGroups && this._testGroups.length ? null : 'none';
-        this._taskNameLabel.updateRendering();
+        this._taskNameLabel.enqueueToRender();
 
         if (this._relatedTasks && this._task) {
             var router = this.router();
@@ -296,7 +296,7 @@
         var a = selectedRange['A'];
         var b = selectedRange['B'];
         this._newTestGroupFormForViewer.setRootSetMap(a && b ? {'A': a.rootSet(), 'B': b.rootSet()} : null);
-        this._newTestGroupFormForViewer.updateRendering();
+        this._newTestGroupFormForViewer.enqueueToRender();
         this._newTestGroupFormForViewer.element().style.display = this._triggerable ? null : 'none';
 
         this._renderTestGroupList();
@@ -308,13 +308,13 @@
         var points = this._chartPane.selectedPoints();
         this._newTestGroupFormForChart.setRootSetMap(points && points.length >= 2 ?
                 {'A': points[0].rootSet(), 'B': points[points.length - 1].rootSet()} : null);
-        this._newTestGroupFormForChart.updateRendering();
+        this._newTestGroupFormForChart.enqueueToRender();
         this._newTestGroupFormForChart.element().style.display = this._triggerable ? null : 'none';
 
         this._analysisResultsViewer.setCurrentTestGroup(this._currentTestGroup);
-        this._analysisResultsViewer.updateRendering();
+        this._analysisResultsViewer.enqueueToRender();
 
-        this._testGroupResultsTable.updateRendering();
+        this._testGroupResultsTable.enqueueToRender();
 
         Instrumentation.endMeasuringTime('AnalysisTaskPage', 'render');
     }
@@ -356,7 +356,7 @@
             for (var testGroup of this._filteredTestGroups) {
                 var label = this._testGroupLabelMap.get(testGroup);
                 label.setText(testGroup.label());
-                label.updateRendering();
+                label.enqueueToRender();
             }
         }
     }
@@ -364,7 +364,7 @@
     _createTestGroupListItem(group)
     {
         var text = new EditableText(group.label());
-        text.setStartedEditingCallback(() => { return text.updateRendering(); });
+        text.setStartedEditingCallback(() => { return text.enqueueToRender(); });
         text.setUpdateCallback(this._updateTestGroupName.bind(this, group));
 
         this._testGroupLabelMap.set(group, text);
@@ -408,7 +408,7 @@
 
             this._renderedCurrentTestGroup = this._currentTestGroup;
         }
-        this._retryForm.updateRendering();
+        this._retryForm.enqueueToRender();
     }
 
     _showTestGroup(testGroup)
@@ -415,23 +415,23 @@
     {
         this._currentTestGroup = testGroup;        
         this._testGroupResultsTable.setTestGroup(this._currentTestGroup);
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _didStartEditingTaskName()
     {
-        this._taskNameLabel.updateRendering();
+        this._taskNameLabel.enqueueToRender();
     }
 
     _updateTaskName()
     {
         console.assert(this._task);
-        this._taskNameLabel.updateRendering();
+        this._taskNameLabel.enqueueToRender();
 
         return this._task.updateName(this._taskNameLabel.editedText()).then(() => {
-            this.updateRendering();
+            this.enqueueToRender();
         }, (error) => {
-            this.updateRendering();
+            this.enqueueToRender();
             alert('Failed to update the name: ' + error);
         });
     }
@@ -439,12 +439,12 @@
     _updateTestGroupName(testGroup)
     {
         var label = this._testGroupLabelMap.get(testGroup);
-        label.updateRendering();
+        label.enqueueToRender();
 
         return testGroup.updateName(label.editedText()).then(() => {
-            this.updateRendering();
+            this.enqueueToRender();
         }, (error) => {
-            this.updateRendering();
+            this.enqueueToRender();
             alert('Failed to hide the test name: ' + error);
         });
     }
@@ -454,10 +454,10 @@
         console.assert(this._currentTestGroup);
         return this._currentTestGroup.updateHiddenFlag(!this._currentTestGroup.isHidden()).then(() => {
             this._didUpdateTestGroupHiddenState();
-            this.updateRendering();
+            this.enqueueToRender();
         }, function (error) {
             this._mayHaveMutatedTestGroupHiddenState();
-            this.updateRendering();
+            this.enqueueToRender();
             alert('Failed to update the group: ' + error);
         });
     }
@@ -471,7 +471,10 @@
         if (newChangeType == 'unconfirmed')
             newChangeType = null;
 
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => {
+            this._chartPane.didUpdateAnnotations();
+            this.enqueueToRender();
+        };
         return this._task.updateChangeType(newChangeType).then(updateRendering, (error) => {
             updateRendering();
             alert('Failed to update the status: ' + error);
@@ -483,7 +486,7 @@
         console.assert(tracker instanceof BugTracker);
         bugNumber = parseInt(bugNumber);
 
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         return this._task.associateBug(tracker, bugNumber).then(updateRendering, (error) => {
             updateRendering();
             alert('Failed to associate the bug: ' + error);
@@ -492,7 +495,7 @@
 
     _dissociateBug(bug)
     {
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         return this._task.dissociateBug(bug).then(updateRendering, (error) => {
             updateRendering();
             alert('Failed to dissociate the bug: ' + error);
@@ -501,7 +504,7 @@
 
     _associateCommit(kind, repository, revision)
     {
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         return this._task.associateCommit(kind, repository, revision).then(updateRendering, (error) => {
             updateRendering();
             if (error == 'AmbiguousRevision')
@@ -515,7 +518,7 @@
 
     _dissociateCommit(commit)
     {
-        const updateRendering = () => { this.updateRendering(); };
+        const updateRendering = () => { this.enqueueToRender(); };
         return this._task.dissociateCommit(commit).then(updateRendering, (error) => {
             updateRendering();
             alert('Failed to dissociate the commit: ' + error);
@@ -539,7 +542,7 @@
     _chartSelectionDidChange()
     {
         this._selectionWasModifiedByUser = true;
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _createNewTestGroupFromChart(name, repetitionCount, rootSetMap)
@@ -549,7 +552,7 @@
 
     _selectedRowInAnalysisResultsViewer()
     {
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _createNewTestGroupFromViewer(name, repetitionCount, rootSetMap)

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/build-request-queue-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/build-request-queue-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/build-request-queue-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -18,16 +18,16 @@
 
                 BuildRequest.fetchForTriggerable(entry.name).then(function (requests) {
                     triggerable.buildRequests = requests;
-                    self.updateRendering();
+                    self.enqueueToRender();
                 });
 
                 return triggerable;
             });
-            self.updateRendering();
+            self.enqueueToRender();
         });
 
         AnalysisTask.fetchAll().then(function () {
-            self.updateRendering();
+            self.enqueueToRender();
         });
 
         super.open(state);

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -6,7 +6,7 @@
         if (!values.length)
             return Promise.resolve(null);
 
-        var averageValues = callback.call(null, values, parameters[0], parameters[1]);
+        var averageValues = callback.call(null, values, ...parameters);
         if (!averageValues)
             return Promise.resolve(null);
 
@@ -186,8 +186,8 @@
     {
         if (repository != this._commitLogViewer.currentRepository()) {
             var range = this._mainChartStatus.setCurrentRepository(repository);
-            this._commitLogViewer.view(repository, range.from, range.to).then(() => { this.updateRendering(); });
-            this.updateRendering();
+            this._commitLogViewer.view(repository, range.from, range.to).then(() => { this.enqueueToRender(); });
+            this.enqueueToRender();
         }
     }
 
@@ -402,15 +402,15 @@
         var link = ComponentBase.createLink;
         var self = this;
 
-        if (this._trendLineType == null) {
-            this.renderReplace(this.content().querySelector('.trend-line-types'), [
+        const trendLineTypesContainer = this.content().querySelector('.trend-line-types');
+        if (!trendLineTypesContainer.querySelector('select')) {
+            this.renderReplace(trendLineTypesContainer, [
                 element('select', {onchange: this._trendLineTypeDidChange.bind(this)},
-                    ChartTrendLineTypes.map(function (type) {
-                        return element('option', type == self._trendLineType ? {value: type.id, selected: true} : {value: type.id}, type.label);
-                    }))
+                    ChartTrendLineTypes.map((type) => { return element('option', {value: type.id}, type.label); }))
             ]);
-        } else
-            this.content().querySelector('.trend-line-types select').value = this._trendLineType.id;
+        }
+        if (this._trendLineType)
+            trendLineTypesContainer.querySelector('select').value = this._trendLineType.id;
 
         if (this._renderedTrendLineOptions)
             return;
@@ -448,7 +448,7 @@
 
         this._updateTrendLine();
         this._chartsPage.graphOptionsDidChange();
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     _defaultParametersForTrendLine(type)
@@ -493,7 +493,7 @@
 
         if (!currentTrendLineType.execute) {
             this._mainChart.clearTrendLines();
-            this.updateRendering();
+            this.enqueueToRender();
         } else {
             // Wait for all trendlines to be ready. Otherwise we might see FOC when the domain is expanded.
             Promise.all(sourceList.map(function (source, sourceIndex) {
@@ -502,7 +502,7 @@
                         self._mainChart.setTrendLine(sourceIndex, trendlineSeries);
                 });
             })).then(function () {
-                self.updateRendering();
+                self.enqueueToRender();
             });
         }
     }

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/charts-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/charts-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/charts-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -79,7 +79,7 @@
         if (newPaneList) {
             this._paneList = newPaneList;
             this._paneListChanged = true;
-            this.updateRendering();
+            this.enqueueToRender();
         }
 
         this._updateDomainsFromSerializedState(state);
@@ -109,7 +109,7 @@
             since = zoom[0] - (zoom[1] - zoom[0]) / 2;
 
         this.toolbar().setStartTime(since);
-        this.toolbar().updateRendering();
+        this.toolbar().enqueueToRender();
 
         this._mainDomain = zoom || null;
 
@@ -151,7 +151,7 @@
     setNumberOfDaysFromToolbar(numberOfDays, shouldUpdateState)
     {
         this.toolbar().setNumberOfDays(numberOfDays, true);
-        this.toolbar().updateRendering();
+        this.toolbar().enqueueToRender();
         this._updateOverviewDomain();
         this._updateMainDomain();
         if (shouldUpdateState)
@@ -298,7 +298,7 @@
             this._updateOverviewDomain();
             this._updateMainDomain();
         }
-        this.updateRendering();
+        this.enqueueToRender();
         this.scheduleUrlStateUpdate();
     }
 
@@ -310,7 +310,7 @@
             this.renderReplace(this.content().querySelector('.pane-list'), this._paneList);
 
         for (var pane of this._paneList)
-            pane.updateRendering();
+            pane.enqueueToRender();
 
         this._paneListChanged = false;
     }

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/charts-toolbar.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/charts-toolbar.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/charts-toolbar.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -34,7 +34,7 @@
     render()
     {
         super.render();
-        this._paneSelector.updateRendering();
+        this._paneSelector.enqueueToRender();
         this._labelSpan.textContent = this._numberOfDays;
         this._setInputElementValue(this._numberOfDays);
     }

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/create-analysis-task-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/create-analysis-task-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/create-analysis-task-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -16,7 +16,7 @@
 
         this._errorMessage = state.error;
         if (!isOpen)
-            this.updateRendering();
+            this.enqueueToRender();
     }
 
     render()

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/dashboard-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/dashboard-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/dashboard-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -35,7 +35,7 @@
 
         this._needsTableConstruction = true;
         if (!isOpen)
-            this.updateRendering();
+            this.enqueueToRender();
     }
 
     open(state)
@@ -114,7 +114,7 @@
 
         if (this._needsStatusUpdate) {
             for (var statusView of this._statusViews)
-                statusView.updateRendering();
+                statusView.enqueueToRender();
             this._needsStatusUpdate = false;
         }
     }
@@ -155,7 +155,7 @@
             return;
 
         this._needsStatusUpdate = true;
-        setTimeout(() => { this.updateRendering(); }, 10);
+        setTimeout(() => { this.enqueueToRender(); }, 10);
     }
 
     static htmlTemplate()

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/heading.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/heading.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/heading.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -50,7 +50,7 @@
         }
 
         if (this._toolbar)
-            this._toolbar.updateRendering();
+            this._toolbar.enqueueToRender();
 
         var currentPage = this._router.currentPage();
         if (this._renderedCurrentPage == currentPage)

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/page-with-heading.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/page-with-heading.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/page-with-heading.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -32,7 +32,7 @@
             document.body.insertBefore(this.heading().element(), document.body.firstChild);
 
         super.render();
-        this.heading().updateRendering();
+        this.heading().enqueueToRender();
     }
 
     static htmlTemplate()

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -19,7 +19,7 @@
         if (this._router)
             this._router.pageDidOpen(this);
         this.updateFromSerializedState(state, true);
-        this.updateRendering();
+        this.enqueueToRender();
     }
 
     render()

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js (210879 => 210880)


--- trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js	2017-01-18 20:43:44 UTC (rev 210879)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js	2017-01-18 21:22:39 UTC (rev 210880)
@@ -36,7 +36,7 @@
         var current = Date.now();
         var timeRange = [current - 24 * 3600 * 1000, current];
         for (var group of this._configGroups)
-            group.fetchAndComputeSummary(timeRange).then(() => { this.updateRendering(); });
+            group.fetchAndComputeSummary(timeRange).then(() => { this.enqueueToRender(); });
     }
 
     render()
@@ -106,7 +106,7 @@
         var ratioGraph = new RatioBarGraph();
 
         if (configurationList.length == 0) {
-            this._renderQueue.push(() => { ratioGraph.updateRendering(); });
+            this._renderQueue.push(() => { ratioGraph.enqueueToRender(); });
             return element('td', ratioGraph);
         }
 
@@ -121,7 +121,7 @@
 
     _renderCell(cell, spinner, anchor, ratioGraph, configurationGroup)
     {
-        spinner.updateRendering();
+        spinner.enqueueToRender();
 
         if (configurationGroup.isFetching())
             cell.classList.add('fetching');
@@ -131,7 +131,7 @@
         var warningText = this._warningTextForGroup(configurationGroup);
         anchor.title = warningText || 'Open charts';
         ratioGraph.update(configurationGroup.ratio(), configurationGroup.label(), !!warningText);
-        ratioGraph.updateRendering();
+        ratioGraph.enqueueToRender();
     }
 
     _warningTextForGroup(configurationGroup)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to