2009-11-22 Pavel Feldman <pfeldman@chromium.org>
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Nov 2009 18:25:03 +0000 (18:25 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Nov 2009 18:25:03 +0000 (18:25 +0000)
        Reviewed by Timothy Hatcher.

        Web Inspector: Reimplement TimelinePanel to make it fast:
        - Extract grid and overview into separate files
        - Make timeline create only divs for visible rows

        https://bugs.webkit.org/show_bug.cgi?id=31784

        * WebCore.gypi:
        * WebCore.vcproj/WebCore.vcproj:
        * inspector/front-end/AbstractTimelinePanel.js:
        (WebInspector.AbstractTimelinePanel.prototype.createInterface):
        (WebInspector.AbstractTimelinePanel.prototype.refresh):
        (WebInspector.AbstractTimelinePanel.prototype.set calculator):
        * inspector/front-end/TimelineGrid.js: Added.
        (WebInspector.TimelineGrid):
        (WebInspector.TimelineGrid.prototype.get itemsGraphsElement):
        (WebInspector.TimelineGrid.prototype.updateDividers):
        (WebInspector.TimelineGrid.prototype.addEventDivider):
        (WebInspector.TimelineGrid.prototype.setScrollAndDividerTop):
        * inspector/front-end/TimelineOverviewPane.js: Added.
        (WebInspector.TimelineOverviewPane):
        (WebInspector.TimelineOverviewPane.prototype._onCheckboxClicked):
        (WebInspector.TimelineOverviewPane.prototype.update):
        (WebInspector.TimelineOverviewPane.prototype.setSidebarWidth):
        (WebInspector.TimelineOverviewPane.prototype.updateMainViewWidth):
        (WebInspector.TimelineOverviewPane.prototype.reset):
        (WebInspector.TimelineOverviewPane.prototype._resizeWindow):
        (WebInspector.TimelineOverviewPane.prototype._windowResizeDragging):
        (WebInspector.TimelineOverviewPane.prototype._dragWindow):
        (WebInspector.TimelineOverviewPane.prototype._windowDragging):
        (WebInspector.TimelineOverviewPane.prototype._resizeWindowLeft):
        (WebInspector.TimelineOverviewPane.prototype._resizeWindowRight):
        (WebInspector.TimelineOverviewPane.prototype._setWindowPosition):
        (WebInspector.TimelineOverviewPane.prototype._endWindowDragging):
        (WebInspector.TimelineOverviewCalculator):
        (WebInspector.TimelineOverviewCalculator.prototype.computeBarGraphPercentages):
        (WebInspector.TimelineOverviewCalculator.prototype.reset):
        (WebInspector.TimelineOverviewCalculator.prototype.updateBoundaries):
        (WebInspector.TimelineOverviewCalculator.prototype.get boundarySpan):
        (WebInspector.TimelineOverviewCalculator.prototype.formatValue):
        (WebInspector.TimelineCategoryTreeElement):
        (WebInspector.TimelineCategoryTreeElement.prototype.onattach):
        (WebInspector.TimelineCategoryGraph):
        (WebInspector.TimelineCategoryGraph.prototype.get graphElement):
        (WebInspector.TimelineCategoryGraph.prototype.addChunk):
        (WebInspector.TimelineCategoryGraph.prototype.clearChunks):
        (WebInspector.TimelineCategoryGraph.prototype.set dimmed):
        * inspector/front-end/TimelinePanel.js:
        (WebInspector.TimelinePanel):
        (WebInspector.TimelinePanel.prototype._toggleTimelineButtonClicked):
        (WebInspector.TimelinePanel.prototype.addRecordToTimeline):
        (WebInspector.TimelinePanel.prototype._formatRecord):
        (WebInspector.TimelinePanel.prototype.setSidebarWidth):
        (WebInspector.TimelinePanel.prototype.updateMainViewWidth):
        (WebInspector.TimelinePanel.prototype.resize):
        (WebInspector.TimelinePanel.prototype.reset):
        (WebInspector.TimelinePanel.prototype.show):
        (WebInspector.TimelinePanel.prototype._onScroll):
        (WebInspector.TimelinePanel.prototype._scheduleRefresh):
        (WebInspector.TimelinePanel.prototype._refresh):
        (WebInspector.TimelinePanel.prototype._refreshRecords):
        (WebInspector.TimelinePanel.prototype._adjustScrollPosition):
        (WebInspector.TimelineCategory):
        (WebInspector.TimelineCalculator):
        (WebInspector.TimelineCalculator.prototype.get boundarySpan):
        (WebInspector.TimelineRecordListRow):
        (WebInspector.TimelineRecordListRow.prototype.update):
        (WebInspector.TimelineRecordGraphRow):
        (WebInspector.TimelineRecordGraphRow.prototype.update):
        * inspector/front-end/WebKit.qrc:
        * inspector/front-end/inspector.css:
        * inspector/front-end/inspector.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@51296 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/inspector/timeline-test.js
WebCore/ChangeLog
WebCore/WebCore.gypi
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/inspector/front-end/AbstractTimelinePanel.js
WebCore/inspector/front-end/TimelineGrid.js [new file with mode: 0644]
WebCore/inspector/front-end/TimelineOverviewPane.js [new file with mode: 0644]
WebCore/inspector/front-end/TimelinePanel.js
WebCore/inspector/front-end/WebKit.qrc
WebCore/inspector/front-end/inspector.css
WebCore/inspector/front-end/inspector.html

index c32fbfd..d019d5f 100644 (file)
@@ -118,9 +118,9 @@ function timelineAgentTypeToString(numericType)
 // Injected into Inspector window
 function frontend_getTimelineResults() {
     var result = [];
-    var items = WebInspector.panels.timeline._items;
-    for (var i = 0; i < items.length; ++i) {
-        result.push(items[i].record);
+    var records = WebInspector.panels.timeline._records;
+    for (var i = 0; i < records.length; ++i) {
+        result.push(records[i].record);
     }
     return result;
 }
index b742d93..8eb5362 100644 (file)
@@ -1,3 +1,79 @@
+2009-11-22  Pavel Feldman  <pfeldman@chromium.org>
+
+        Reviewed by Timothy Hatcher.
+
+        Web Inspector: Reimplement TimelinePanel to make it fast:
+        - Extract grid and overview into separate files
+        - Make timeline create only divs for visible rows
+
+        https://bugs.webkit.org/show_bug.cgi?id=31784
+
+        * WebCore.gypi:
+        * WebCore.vcproj/WebCore.vcproj:
+        * inspector/front-end/AbstractTimelinePanel.js:
+        (WebInspector.AbstractTimelinePanel.prototype.createInterface):
+        (WebInspector.AbstractTimelinePanel.prototype.refresh):
+        (WebInspector.AbstractTimelinePanel.prototype.set calculator):
+        * inspector/front-end/TimelineGrid.js: Added.
+        (WebInspector.TimelineGrid):
+        (WebInspector.TimelineGrid.prototype.get itemsGraphsElement):
+        (WebInspector.TimelineGrid.prototype.updateDividers):
+        (WebInspector.TimelineGrid.prototype.addEventDivider):
+        (WebInspector.TimelineGrid.prototype.setScrollAndDividerTop):
+        * inspector/front-end/TimelineOverviewPane.js: Added.
+        (WebInspector.TimelineOverviewPane):
+        (WebInspector.TimelineOverviewPane.prototype._onCheckboxClicked):
+        (WebInspector.TimelineOverviewPane.prototype.update):
+        (WebInspector.TimelineOverviewPane.prototype.setSidebarWidth):
+        (WebInspector.TimelineOverviewPane.prototype.updateMainViewWidth):
+        (WebInspector.TimelineOverviewPane.prototype.reset):
+        (WebInspector.TimelineOverviewPane.prototype._resizeWindow):
+        (WebInspector.TimelineOverviewPane.prototype._windowResizeDragging):
+        (WebInspector.TimelineOverviewPane.prototype._dragWindow):
+        (WebInspector.TimelineOverviewPane.prototype._windowDragging):
+        (WebInspector.TimelineOverviewPane.prototype._resizeWindowLeft):
+        (WebInspector.TimelineOverviewPane.prototype._resizeWindowRight):
+        (WebInspector.TimelineOverviewPane.prototype._setWindowPosition):
+        (WebInspector.TimelineOverviewPane.prototype._endWindowDragging):
+        (WebInspector.TimelineOverviewCalculator):
+        (WebInspector.TimelineOverviewCalculator.prototype.computeBarGraphPercentages):
+        (WebInspector.TimelineOverviewCalculator.prototype.reset):
+        (WebInspector.TimelineOverviewCalculator.prototype.updateBoundaries):
+        (WebInspector.TimelineOverviewCalculator.prototype.get boundarySpan):
+        (WebInspector.TimelineOverviewCalculator.prototype.formatValue):
+        (WebInspector.TimelineCategoryTreeElement):
+        (WebInspector.TimelineCategoryTreeElement.prototype.onattach):
+        (WebInspector.TimelineCategoryGraph):
+        (WebInspector.TimelineCategoryGraph.prototype.get graphElement):
+        (WebInspector.TimelineCategoryGraph.prototype.addChunk):
+        (WebInspector.TimelineCategoryGraph.prototype.clearChunks):
+        (WebInspector.TimelineCategoryGraph.prototype.set dimmed):
+        * inspector/front-end/TimelinePanel.js:
+        (WebInspector.TimelinePanel):
+        (WebInspector.TimelinePanel.prototype._toggleTimelineButtonClicked):
+        (WebInspector.TimelinePanel.prototype.addRecordToTimeline):
+        (WebInspector.TimelinePanel.prototype._formatRecord):
+        (WebInspector.TimelinePanel.prototype.setSidebarWidth):
+        (WebInspector.TimelinePanel.prototype.updateMainViewWidth):
+        (WebInspector.TimelinePanel.prototype.resize):
+        (WebInspector.TimelinePanel.prototype.reset):
+        (WebInspector.TimelinePanel.prototype.show):
+        (WebInspector.TimelinePanel.prototype._onScroll):
+        (WebInspector.TimelinePanel.prototype._scheduleRefresh):
+        (WebInspector.TimelinePanel.prototype._refresh):
+        (WebInspector.TimelinePanel.prototype._refreshRecords):
+        (WebInspector.TimelinePanel.prototype._adjustScrollPosition):
+        (WebInspector.TimelineCategory):
+        (WebInspector.TimelineCalculator):
+        (WebInspector.TimelineCalculator.prototype.get boundarySpan):
+        (WebInspector.TimelineRecordListRow):
+        (WebInspector.TimelineRecordListRow.prototype.update):
+        (WebInspector.TimelineRecordGraphRow):
+        (WebInspector.TimelineRecordGraphRow.prototype.update):
+        * inspector/front-end/WebKit.qrc:
+        * inspector/front-end/inspector.css:
+        * inspector/front-end/inspector.html:
+
 2009-11-22  Chris Evans  <cevans@chromium.org>
 
         Reviewed by Adam Barth.
index 6c49885..362bce7 100644 (file)
             'inspector/front-end/TestController.js',
             'inspector/front-end/TextPrompt.js',
             'inspector/front-end/TimelineAgent.js',
+            'inspector/front-end/TimelineOverviewPane.js',
+            'inspector/front-end/TimelineGrid.js',
             'inspector/front-end/TimelinePanel.js',
             'inspector/front-end/TopDownProfileDataGridTree.js',
             'inspector/front-end/treeoutline.js',
index 11bd19c..b24a402 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\inspector\front-end\TimelineOverviewPane.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath="..\inspector\front-end\TimelineGrid.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\inspector\front-end\TimelinePanel.js"\r
                                        >\r
                                </File>\r
index 3d6cd66..c5180f6 100644 (file)
@@ -81,7 +81,8 @@ WebInspector.AbstractTimelinePanel.prototype = {
         this.summaryBar.element.id = "resources-summary";
         this._containerContentElement.appendChild(this.summaryBar.element);
 
-        this._timelineGrid = new WebInspector.TimelineGrid(this._containerContentElement);
+        this._timelineGrid = new WebInspector.TimelineGrid();
+        this._containerContentElement.appendChild(this._timelineGrid.element);
         this.itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
     },
 
@@ -300,7 +301,7 @@ WebInspector.AbstractTimelinePanel.prototype = {
 
         if (boundariesChanged) {
             // The boundaries changed, so all item graphs are stale.
-            this._staleItems = this._items;
+            this._staleItems = this._items.slice();
             staleItemsLength = this._staleItems.length;
         }
 
@@ -349,7 +350,7 @@ WebInspector.AbstractTimelinePanel.prototype = {
         this._calculator = x;
         this._calculator.reset();
 
-        this._staleItems = this._items;
+        this._staleItems = this._items.slice();
         this.refresh();
     },
 
@@ -506,74 +507,3 @@ WebInspector.AbstractTimelineCategory.prototype = {
         return this.title;
     }
 }
-
-WebInspector.TimelineGrid = function(container)
-{
-    this._itemsGraphsElement = document.createElement("div");
-    this._itemsGraphsElement.id = "resources-graphs";
-    container.appendChild(this._itemsGraphsElement);
-
-    this._dividersElement = document.createElement("div");
-    this._dividersElement.id = "resources-dividers";
-    container.appendChild(this._dividersElement);
-
-    this._eventDividersElement = document.createElement("div");
-    this._eventDividersElement.id = "resources-event-dividers";
-    container.appendChild(this._eventDividersElement);
-
-    this._dividersLabelBarElement = document.createElement("div");
-    this._dividersLabelBarElement.id = "resources-dividers-label-bar";
-    container.appendChild(this._dividersLabelBarElement);
-}
-
-WebInspector.TimelineGrid.prototype = {
-    get itemsGraphsElement()
-    {
-        return this._itemsGraphsElement;
-    },
-
-    updateDividers: function(force, calculator)
-    {
-        var dividerCount = Math.round(this._dividersElement.offsetWidth / 64);
-        var slice = calculator.boundarySpan / dividerCount;
-        if (!force && this._currentDividerSlice === slice)
-            return false;
-
-        this._currentDividerSlice = slice;
-
-        this._dividersElement.removeChildren();
-        this._eventDividersElement.removeChildren();
-        this._dividersLabelBarElement.removeChildren();
-
-        for (var i = 1; i <= dividerCount; ++i) {
-            var divider = document.createElement("div");
-            divider.className = "resources-divider";
-            if (i === dividerCount)
-                divider.addStyleClass("last");
-            divider.style.left = ((i / dividerCount) * 100) + "%";
-
-            this._dividersElement.appendChild(divider.cloneNode());
-
-            var label = document.createElement("div");
-            label.className = "resources-divider-label";
-            if (!isNaN(slice))
-                label.textContent = calculator.formatValue(slice * i);
-            divider.appendChild(label);
-
-            this._dividersLabelBarElement.appendChild(divider);
-        }
-        return true;
-    },
-
-    addEventDivider: function(divider)
-    {
-        this._eventDividersElement.appendChild(divider);
-    },
-
-    setScrollAndDividerTop: function(scrollTop, dividersTop)
-    {
-        this._dividersElement.style.top = scrollTop + "px";
-        this._eventDividersElement.style.top = scrollTop + "px";
-        this._dividersLabelBarElement.style.top = dividersTop + "px";
-    }
-}
diff --git a/WebCore/inspector/front-end/TimelineGrid.js b/WebCore/inspector/front-end/TimelineGrid.js
new file mode 100644 (file)
index 0000000..7ed94b6
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineGrid = function()
+{
+    this.element = document.createElement("div");
+
+    this._itemsGraphsElement = document.createElement("div");
+    this._itemsGraphsElement.id = "resources-graphs";
+    this.element.appendChild(this._itemsGraphsElement);
+
+    this._dividersElement = document.createElement("div");
+    this._dividersElement.id = "resources-dividers";
+    this.element.appendChild(this._dividersElement);
+
+    this._eventDividersElement = document.createElement("div");
+    this._eventDividersElement.id = "resources-event-dividers";
+    this.element.appendChild(this._eventDividersElement);
+
+    this._dividersLabelBarElement = document.createElement("div");
+    this._dividersLabelBarElement.id = "resources-dividers-label-bar";
+    this.element.appendChild(this._dividersLabelBarElement);
+}
+
+WebInspector.TimelineGrid.prototype = {
+    get itemsGraphsElement()
+    {
+        return this._itemsGraphsElement;
+    },
+
+    updateDividers: function(force, calculator)
+    {
+        var dividerCount = Math.round(this._dividersElement.offsetWidth / 64);
+        var slice = calculator.boundarySpan / dividerCount;
+        if (!force && this._currentDividerSlice === slice)
+            return false;
+
+        this._currentDividerSlice = slice;
+
+        this._dividersElement.removeChildren();
+        this._eventDividersElement.removeChildren();
+        this._dividersLabelBarElement.removeChildren();
+
+        for (var i = 1; i <= dividerCount; ++i) {
+            var divider = document.createElement("div");
+            divider.className = "resources-divider";
+            if (i === dividerCount)
+                divider.addStyleClass("last");
+            divider.style.left = ((i / dividerCount) * 100) + "%";
+
+            this._dividersElement.appendChild(divider.cloneNode());
+
+            var label = document.createElement("div");
+            label.className = "resources-divider-label";
+            if (!isNaN(slice))
+                label.textContent = calculator.formatValue(slice * i);
+            divider.appendChild(label);
+
+            this._dividersLabelBarElement.appendChild(divider);
+        }
+        return true;
+    },
+
+    addEventDivider: function(divider)
+    {
+        this._eventDividersElement.appendChild(divider);
+    },
+
+    setScrollAndDividerTop: function(scrollTop, dividersTop)
+    {
+        this._dividersElement.style.top = scrollTop + "px";
+        this._eventDividersElement.style.top = scrollTop + "px";
+        this._dividersLabelBarElement.style.top = dividersTop + "px";
+    }
+}
diff --git a/WebCore/inspector/front-end/TimelineOverviewPane.js b/WebCore/inspector/front-end/TimelineOverviewPane.js
new file mode 100644 (file)
index 0000000..3479d4c
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineOverviewPane = function(categories)
+{
+    this.element = document.createElement("div");
+    this.element.id = "timeline-overview-panel";
+
+    this._categories = categories;
+    this._overviewSidebarElement = document.createElement("div");
+    this._overviewSidebarElement.id = "timeline-overview-sidebar";
+    this.element.appendChild(this._overviewSidebarElement);
+
+    var overviewTreeElement = document.createElement("ol");
+    overviewTreeElement.className = "sidebar-tree";
+    this._overviewSidebarElement.appendChild(overviewTreeElement);
+    var sidebarTree = new TreeOutline(overviewTreeElement);
+
+    var categoriesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("TIMELINES"), {}, true);
+    categoriesTreeElement.expanded = true;
+    sidebarTree.appendChild(categoriesTreeElement);
+    for (var categoryName in this._categories) {
+        var category = this._categories[categoryName];
+        categoriesTreeElement.appendChild(new WebInspector.TimelineCategoryTreeElement(category, this._onCheckboxClicked.bind(this, category)));
+    }
+
+    this._overviewGrid = new WebInspector.TimelineGrid();
+    this._overviewGrid.element.id = "timeline-overview-grid";
+    this._overviewGrid.itemsGraphsElement.id = "timeline-overview-graphs";
+    this.element.appendChild(this._overviewGrid.element);
+
+    this._categoryGraphs = {};
+    var i = 0;
+    for (var category in this._categories) {
+        var categoryGraph = new WebInspector.TimelineCategoryGraph(this._categories[category], i++ % 2);
+        this._categoryGraphs[category] = categoryGraph;
+        this._overviewGrid.itemsGraphsElement.appendChild(categoryGraph.graphElement);
+    }
+    this._overviewGrid.setScrollAndDividerTop(0, 0);
+
+    this._overviewWindowElement = document.createElement("div");
+    this._overviewWindowElement.id = "timeline-overview-window";
+    this._overviewWindowElement.addEventListener("mousedown", this._dragWindow.bind(this), false);
+    this._overviewGrid.element.appendChild(this._overviewWindowElement);
+
+    this._leftResizeElement = document.createElement("div");
+    this._leftResizeElement.className = "timeline-window-resizer";
+    this._leftResizeElement.style.left = 0;
+    this._overviewGrid.element.appendChild(this._leftResizeElement);
+    this._leftResizeElement.addEventListener("mousedown", this._resizeWindow.bind(this, this._leftResizeElement), false);
+
+    this._rightResizeElement = document.createElement("div");
+    this._rightResizeElement.className = "timeline-window-resizer timeline-window-resizer-right";
+    this._rightResizeElement.style.right = 0;
+    this._overviewGrid.element.appendChild(this._rightResizeElement);
+    this._rightResizeElement.addEventListener("mousedown", this._resizeWindow.bind(this, this._rightResizeElement), false);
+
+    this._overviewCalculator = new WebInspector.TimelineOverviewCalculator();
+
+    var separatorElement = document.createElement("div");
+    separatorElement.id = "timeline-overview-separator";
+    this.element.appendChild(separatorElement);
+
+    this.windowLeft = 0.0;
+    this.windowRight = 1.0;
+}
+
+
+WebInspector.TimelineOverviewPane.prototype = {
+    _onCheckboxClicked: function (category, event) {
+        if (event.target.checked)
+            category.hidden = false;
+        else
+            category.hidden = true;
+        this._categoryGraphs[category.name].dimmed = !event.target.checked;
+        this.dispatchEventToListeners("filter changed");
+    },
+
+    update: function(records)
+    {
+        // Clear summary bars.
+        var timelines = {};
+        for (var category in this._categories) {
+            timelines[category] = [];
+            this._categoryGraphs[category].clearChunks();
+        }
+
+        // Create sparse arrays with 101 cells each to fill with chunks for a given category.
+        this._overviewCalculator.reset();
+
+        for (var i = 1; i < records.length; ++i)
+            this._overviewCalculator.updateBoundaries(records[i]);
+
+        for (var i = 0; i < records.length; ++i) {
+            var record = records[i];
+            var percentages = this._overviewCalculator.computeBarGraphPercentages(record);
+            
+            var end = Math.round(percentages.end);
+            var categoryName = record.category.name;
+            for (var j = Math.round(percentages.start); j <= end; ++j)
+                timelines[categoryName][j] = true;
+        }
+
+        // Convert sparse arrays to continuous segments, render graphs for each.
+        for (var category in this._categories) {
+            var timeline = timelines[category];
+            window.timelineSaved = timeline;
+            var chunkStart = -1;
+            for (var j = 0; j < 101; ++j) {
+                if (timeline[j]) {
+                    if (chunkStart === -1)
+                        chunkStart = j;
+                } else {
+                    if (chunkStart !== -1) {
+                        this._categoryGraphs[category].addChunk(chunkStart, j);
+                        chunkStart = -1;
+                    }
+                }
+            }
+            if (chunkStart !== -1) {
+                this._categoryGraphs[category].addChunk(chunkStart, 100);
+                chunkStart = -1;
+            }
+        }
+        this._overviewGrid.updateDividers(true, this._overviewCalculator);
+    },
+
+    setSidebarWidth: function(width)
+    {
+        this._overviewSidebarElement.style.width = width + "px";
+    },
+
+    updateMainViewWidth: function(width)
+    {
+        this._overviewGrid.element.style.left = width + "px";
+    },
+
+    reset: function()
+    {
+        this._overviewCalculator.reset();
+        this._overviewGrid.updateDividers(true, this._overviewCalculator);
+        this.windowLeft = 0.0;
+        this.windowRight = 1.0;
+    },
+
+    _resizeWindow: function(resizeElement, event)
+    {
+        WebInspector.elementDragStart(resizeElement, this._windowResizeDragging.bind(this, resizeElement), this._endWindowDragging.bind(this), event, "col-resize");
+    },
+
+    _windowResizeDragging: function(resizeElement, event)
+    {
+        if (resizeElement === this._leftResizeElement)
+            this._resizeWindowLeft(event.pageX - this._overviewGrid.element.offsetLeft);
+        else
+            this._resizeWindowRight(event.pageX - this._overviewGrid.element.offsetLeft);
+        event.preventDefault();
+    },
+
+    _dragWindow: function(event)
+    {
+        WebInspector.elementDragStart(this._overviewWindowElement, this._windowDragging.bind(this, event.pageX,
+            this._leftResizeElement.offsetLeft, this._rightResizeElement.offsetLeft), this._endWindowDragging.bind(this), event, "ew-resize");
+    },
+
+    _windowDragging: function(startX, windowLeft, windowRight, event)
+    {
+        var delta = event.pageX - startX;
+        var start = windowLeft + delta;
+        var end = windowRight + delta;
+        var windowSize = windowRight - windowLeft;
+
+        if (start < 0) {
+            start = 0;
+            end = windowSize;
+        }
+
+        if (end > this._overviewGrid.element.clientWidth) {
+            end = this._overviewGrid.element.clientWidth;
+            start = end - windowSize;
+        }
+        this._setWindowPosition(start, end);
+
+        event.preventDefault();
+    },
+
+    _resizeWindowLeft: function(start)
+    {
+        // Glue to edge.
+        if (start < 10)
+            start = 0;
+        this._setWindowPosition(start, null);
+    },
+
+    _resizeWindowRight: function(end)
+    {
+        // Glue to edge.
+        if (end > this._overviewGrid.element.clientWidth - 10)
+            end = this._overviewGrid.element.clientWidth;
+        this._setWindowPosition(null, end);
+    },
+
+    _setWindowPosition: function(start, end)
+    {
+        if (typeof start === "number") {
+            if (start > this._rightResizeElement.offsetLeft - 25)
+                start = this._rightResizeElement.offsetLeft - 25;
+
+            this.windowLeft = start / this._overviewGrid.element.clientWidth;
+            this._leftResizeElement.style.left = this.windowLeft * 100 + "%";
+            this._overviewWindowElement.style.left = this.windowLeft * 100 + "%";
+        }
+        if (typeof end === "number") {
+            if (end < this._leftResizeElement.offsetLeft + 30)
+                end = this._leftResizeElement.offsetLeft + 30;
+
+            this.windowRight = end / this._overviewGrid.element.clientWidth;
+            this._rightResizeElement.style.left = this.windowRight * 100 + "%";
+        }
+        this._overviewWindowElement.style.width = (this.windowRight - this.windowLeft) * 100 + "%";
+        this.dispatchEventToListeners("window changed");
+    },
+
+    _endWindowDragging: function(event)
+    {
+        WebInspector.elementDragEnd(event);
+    }
+}
+
+WebInspector.TimelineOverviewPane.prototype.__proto__ = WebInspector.Object.prototype;
+
+
+WebInspector.TimelineOverviewCalculator = function()
+{
+    this._uiString = WebInspector.UIString.bind(WebInspector);
+}
+
+WebInspector.TimelineOverviewCalculator.prototype = {
+    computeBarGraphPercentages: function(record)
+    {
+        var start = (record.startTime - this.minimumBoundary) / this.boundarySpan * 100;
+        var end = (record.endTime - this.minimumBoundary) / this.boundarySpan * 100;
+        return {start: start, end: end};
+    },
+
+    reset: function()
+    {
+        delete this.minimumBoundary;
+        delete this.maximumBoundary;
+    },
+
+    updateBoundaries: function(record)
+    {
+        if (typeof this.minimumBoundary === "undefined" || record.startTime < this.minimumBoundary) {
+            this.minimumBoundary = record.startTime;
+            return true;
+        }
+        if (typeof this.maximumBoundary === "undefined" || record.endTime > this.maximumBoundary) {
+            this.maximumBoundary = record.endTime;
+            return true;
+        }
+        return false;
+    },
+
+    get boundarySpan()
+    {
+        return this.maximumBoundary - this.minimumBoundary;
+    },
+
+    formatValue: function(value)
+    {
+        return Number.secondsToString(value, this._uiString);
+    }
+}
+
+
+WebInspector.TimelineCategoryTreeElement = function(category, onCheckboxClicked)
+{
+    this._category = category;
+    this._onCheckboxClicked = onCheckboxClicked;
+    // Pass an empty title, the title gets made later in onattach.
+    TreeElement.call(this, "", null, false);
+}
+
+WebInspector.TimelineCategoryTreeElement.prototype = {
+    onattach: function()
+    {
+        this.listItemElement.removeChildren();
+        this.listItemElement.addStyleClass("timeline-category-tree-item");
+        this.listItemElement.addStyleClass("timeline-category-" + this._category.name);
+
+        var label = document.createElement("label");
+
+        var checkElement = document.createElement("input");
+        checkElement.type = "checkbox";
+        checkElement.className = "timeline-category-checkbox";
+        checkElement.checked = true;
+        checkElement.addEventListener("click", this._onCheckboxClicked);
+        label.appendChild(checkElement);
+
+        var typeElement = document.createElement("span");
+        typeElement.className = "type";
+        typeElement.textContent = this._category.title;
+        label.appendChild(typeElement);
+
+        this.listItemElement.appendChild(label);
+    }
+}
+
+WebInspector.TimelineCategoryTreeElement.prototype.__proto__ = TreeElement.prototype;
+
+WebInspector.TimelineCategoryGraph = function(category, isEven)
+{
+    this._category = category;
+
+    this._graphElement = document.createElement("div");
+    this._graphElement.className = "timeline-graph-side timeline-overview-graph-side" + (isEven ? " even" : "");
+
+    this._barAreaElement = document.createElement("div");
+    this._barAreaElement.className = "timeline-graph-bar-area timeline-category-" + category.name;
+    this._graphElement.appendChild(this._barAreaElement);
+}
+
+WebInspector.TimelineCategoryGraph.prototype = {
+    get graphElement()
+    {
+        return this._graphElement;
+    },
+
+    addChunk: function(start, end)
+    {
+        var chunk = document.createElement("div");
+        chunk.className = "timeline-graph-bar";
+        this._barAreaElement.appendChild(chunk);
+        chunk.style.setProperty("left", start + "%");
+        chunk.style.setProperty("width", (end - start) + "%");
+    },
+
+    clearChunks: function()
+    {
+        this._barAreaElement.removeChildren();
+    },
+
+    set dimmed(dimmed)
+    {
+        if (dimmed)
+            this._barAreaElement.removeStyleClass("timeline-category-" + this._category.name);
+        else
+            this._barAreaElement.addStyleClass("timeline-category-" + this._category.name);
+    }
+}
index 3770ebc..f6761b8 100644 (file)
 
 WebInspector.TimelinePanel = function()
 {
-    WebInspector.AbstractTimelinePanel.call(this);
-
+    WebInspector.Panel.call(this);
     this.element.addStyleClass("timeline");
 
-    this._createOverview();
-    this.createInterface();
-    this.containerElement.id = "timeline-container";
-    this.summaryBar.element.id = "timeline-summary";
-    this.itemsGraphsElement.id = "timeline-graphs";
+    this._overviewPane = new WebInspector.TimelineOverviewPane(this.categories);
+    this._overviewPane.addEventListener("window changed", this._scheduleRefresh, this);
+    this._overviewPane.addEventListener("filter changed", this._refresh, this);
+    this.element.appendChild(this._overviewPane.element);
+
+    this._containerElement = document.createElement("div");
+    this._containerElement.id = "timeline-container";
+    this._containerElement.addEventListener("scroll", this._onScroll.bind(this), false);
+    this.element.appendChild(this._containerElement);
+
+    this.createSidebar(this._containerElement, this._containerElement);
+    this.sidebarElement.id = "timeline-sidebar";
+    this.itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RECORDS"), {}, true);
+    this.itemsTreeElement.expanded = true;
+    this.sidebarTree.appendChild(this.itemsTreeElement);
+
+    this._sidebarListElement = document.createElement("div");
+    this.sidebarElement.appendChild(this._sidebarListElement);
+
+    this._containerContentElement = document.createElement("div");
+    this._containerContentElement.id = "resources-container-content";
+    this._containerElement.appendChild(this._containerContentElement);
+
+    this._timelineGrid = new WebInspector.TimelineGrid();
+    this._itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
+    this._itemsGraphsElement.id = "timeline-graphs";
+    this._containerContentElement.appendChild(this._timelineGrid.element);
+
+    this._topGapElement = document.createElement("div");
+    this._topGapElement.className = "timeline-gap";
+    this._itemsGraphsElement.appendChild(this._topGapElement);
+
+    this._graphRowsElement = document.createElement("div");
+    this._itemsGraphsElement.appendChild(this._graphRowsElement);
+
+    this._bottomGapElement = document.createElement("div");
+    this._bottomGapElement.className = "timeline-gap";
+    this._itemsGraphsElement.appendChild(this._bottomGapElement);
 
     this._createStatusbarButtons();
 
-    this.calculator = new WebInspector.TimelineCalculator();
-    for (category in this.categories)
-        this.showCategory(category);
+    this._records = [];
     this._sendRequestRecords = {};
+    this._calculator = new WebInspector.TimelineCalculator();
 }
 
 WebInspector.TimelinePanel.prototype = {
@@ -73,13 +104,6 @@ WebInspector.TimelinePanel.prototype = {
         return this._categories;
     },
 
-    populateSidebar: function()
-    {
-        this.itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RECORDS"), {}, true);
-        this.itemsTreeElement.expanded = true;
-        this.sidebarTree.appendChild(this.itemsTreeElement);
-    },
-
     _createStatusbarButtons: function()
     {
         this.toggleTimelineButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
@@ -89,6 +113,14 @@ WebInspector.TimelinePanel.prototype = {
         this.clearButton.addEventListener("click", this.reset.bind(this), false);
     },
 
+    _toggleTimelineButtonClicked: function()
+    {
+        if (this.toggleTimelineButton.toggled)
+            InspectorController.stopTimelineProfiler();
+        else
+            InspectorController.startTimelineProfiler();
+    },
+
     timelineWasStarted: function()
     {
         this.toggleTimelineButton.toggled = true;
@@ -110,32 +142,14 @@ WebInspector.TimelinePanel.prototype = {
                 formattedRecord.startTime - this._lastRecord.endTime < 0.1) {
             this._lastRecord.endTime = formattedRecord.endTime;
             this._lastRecord.count++;
-            this.refreshItem(this._lastRecord);
         } else {
-            this.addItem(formattedRecord);
+            this._records.push(formattedRecord);
 
             for (var i = 0; record.children && i < record.children.length; ++i)
                 this.addRecordToTimeline(record.children[i]);
             this._lastRecord = record.children && record.children.length ? null : formattedRecord;
         }
-    },
-
-    createItemTreeElement: function(item)
-    {
-        return new WebInspector.TimelineRecordTreeElement(item);
-    },
-
-    createItemGraph: function(item)
-    {
-        return new WebInspector.TimelineGraph(item);
-    },
-
-    _toggleTimelineButtonClicked: function()
-    {
-        if (this.toggleTimelineButton.toggled)
-            InspectorController.stopTimelineProfiler();
-        else
-            InspectorController.startTimelineProfiler();
+        this._scheduleRefresh();
     },
 
     _formatRecord: function(record)
@@ -182,7 +196,6 @@ WebInspector.TimelinePanel.prototype = {
             sendRequestRecord._responseReceivedFormattedTime = formattedRecord.startTime;
             formattedRecord.startTime = sendRequestRecord.startTime;
             sendRequestRecord.details = this._getRecordDetails(record);
-            this.refreshItem(sendRequestRecord);
         } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceFinish) {
             var sendRequestRecord = this._sendRequestRecords[record.data.identifier];
             if (sendRequestRecord) // False for main resource.
@@ -220,361 +233,160 @@ WebInspector.TimelinePanel.prototype = {
         }
     },
 
-    reset: function()
-    {
-        WebInspector.AbstractTimelinePanel.prototype.reset.call(this);
-        this._lastRecord = null;
-        this._overviewCalculator.reset();
-        for (var category in this.categories)
-            this._categoryGraphs[category].clearChunks();
-        this._setWindowPosition(0, this._overviewGridElement.clientWidth);
-        this._sendRequestRecords = {};
-    },
-
-    _createOverview: function()
-    {
-        var overviewPanelElement = document.createElement("div");
-        overviewPanelElement.id = "timeline-overview-panel";
-        this.element.appendChild(overviewPanelElement);
-
-        this._overviewSidebarElement = document.createElement("div");
-        this._overviewSidebarElement.id = "timeline-overview-sidebar";
-        overviewPanelElement.appendChild(this._overviewSidebarElement);
-
-        var overviewTreeElement = document.createElement("ol");
-        overviewTreeElement.className = "sidebar-tree";
-        this._overviewSidebarElement.appendChild(overviewTreeElement);
-        var sidebarTree = new TreeOutline(overviewTreeElement);
-
-        var categoriesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("TIMELINES"), {}, true);
-        categoriesTreeElement.expanded = true;
-        sidebarTree.appendChild(categoriesTreeElement);
-
-        for (var category in this.categories)
-            categoriesTreeElement.appendChild(new WebInspector.TimelineCategoryTreeElement(this.categories[category]));
-
-        this._overviewGridElement = document.createElement("div");
-        this._overviewGridElement.id = "timeline-overview-grid";
-        overviewPanelElement.appendChild(this._overviewGridElement);
-        this._overviewGrid = new WebInspector.TimelineGrid(this._overviewGridElement);
-        this._overviewGrid.itemsGraphsElement.id = "timeline-overview-graphs";
-
-        this._categoryGraphs = {};
-        for (var category in this.categories) {
-            var categoryGraph = new WebInspector.TimelineCategoryGraph(this.categories[category]);
-            this._categoryGraphs[category] = categoryGraph;
-            this._overviewGrid.itemsGraphsElement.appendChild(categoryGraph.graphElement);
-        }
-        this._overviewGrid.setScrollAndDividerTop(0, 0);
-
-        this._overviewWindowElement = document.createElement("div");
-        this._overviewWindowElement.id = "timeline-overview-window";
-        this._overviewWindowElement.addEventListener("mousedown", this._dragWindow.bind(this), false);
-        this._overviewGridElement.appendChild(this._overviewWindowElement);
-
-        this._leftResizeElement = document.createElement("div");
-        this._leftResizeElement.className = "timeline-window-resizer";
-        this._leftResizeElement.style.left = 0;
-        this._overviewGridElement.appendChild(this._leftResizeElement);
-        this._leftResizeElement.addEventListener("mousedown", this._resizeWindow.bind(this, this._leftResizeElement), false);
-
-        this._rightResizeElement = document.createElement("div");
-        this._rightResizeElement.className = "timeline-window-resizer timeline-window-resizer-right";
-        this._rightResizeElement.style.right = 0;
-        this._overviewGridElement.appendChild(this._rightResizeElement);
-        this._rightResizeElement.addEventListener("mousedown", this._resizeWindow.bind(this, this._rightResizeElement), false);
-
-        this._overviewCalculator = new WebInspector.TimelineCalculator();
-
-        var separatorElement = document.createElement("div");
-        separatorElement.id = "timeline-overview-separator";
-        this.element.appendChild(separatorElement);
-    },
-
     setSidebarWidth: function(width)
     {
-        WebInspector.AbstractTimelinePanel.prototype.setSidebarWidth.call(this, width);
-        this._overviewSidebarElement.style.width = width + "px";
+        WebInspector.Panel.prototype.setSidebarWidth.call(this, width);
+        this._overviewPane.setSidebarWidth(width);
     },
 
     updateMainViewWidth: function(width)
     {
-        WebInspector.AbstractTimelinePanel.prototype.updateMainViewWidth.call(this, width);
-        this._overviewGridElement.style.left = width + "px";
+        this._containerContentElement.style.left = width + "px";
+        this._scheduleRefresh();
+        this._overviewPane.updateMainViewWidth(width);
     },
 
-    updateGraphDividersIfNeeded: function()
-    {
-        WebInspector.AbstractTimelinePanel.prototype.updateGraphDividersIfNeeded.call(this);
-        this._overviewGrid.updateDividers(true, this._overviewCalculator);
+    resize: function() {
+        this._scheduleRefresh();
     },
 
-    refresh: function()
-    {
-        WebInspector.AbstractTimelinePanel.prototype.refresh.call(this);
-        this.adjustScrollPosition();
-
-        // Clear summary bars.
-        var timelines = {};
-        for (var category in this.categories) {
-            timelines[category] = [];
-            this._categoryGraphs[category].clearChunks();
-        }
-
-        // Create sparse arrays with 101 cells each to fill with chunks for a given category.
-        for (var i = 0; i < this.items.length; ++i) {
-            var record = this.items[i];
-            this._overviewCalculator.updateBoundaries(record);
-            var percentages = this._overviewCalculator.computeBarGraphPercentages(record);
-            var end = Math.round(percentages.end);
-            var categoryName = record.category.name;
-            for (var j = Math.round(percentages.start); j <= end; ++j)
-                timelines[categoryName][j] = true;
-        }
-
-        // Convert sparse arrays to continuous segments, render graphs for each.
-        for (var category in this.categories) {
-            var timeline = timelines[category];
-            window.timelineSaved = timeline;
-            var chunkStart = -1;
-            for (var j = 0; j < 101; ++j) {
-                if (timeline[j]) {
-                    if (chunkStart === -1)
-                        chunkStart = j;
-                } else {
-                    if (chunkStart !== -1) {
-                        this._categoryGraphs[category].addChunk(chunkStart, j);
-                        chunkStart = -1;
-                    }
-                }
-            }
-            if (chunkStart !== -1) {
-                this._categoryGraphs[category].addChunk(chunkStart, 100);
-                chunkStart = -1;
-            }
-        }
-        this._overviewGrid.updateDividers(true, this._overviewCalculator);
-    },
-
-    _resizeWindow: function(resizeElement, event)
+    reset: function()
     {
-        WebInspector.elementDragStart(resizeElement, this._windowResizeDragging.bind(this, resizeElement), this._endWindowDragging.bind(this), event, "col-resize");
+        this._lastRecord = null;
+        this._sendRequestRecords = {};
+        this._overviewPane.reset();
+        this._records = [];
+        this._refresh();
     },
 
-    _windowResizeDragging: function(resizeElement, event)
+    show: function()
     {
-        if (resizeElement === this._leftResizeElement)
-            this._resizeWindowLeft(event.pageX - this._overviewGridElement.offsetLeft);
-        else
-            this._resizeWindowRight(event.pageX - this._overviewGridElement.offsetLeft);
-        event.preventDefault();
-    },
+        WebInspector.Panel.prototype.show.call(this);
 
-    _dragWindow: function(event)
-    {
-        WebInspector.elementDragStart(this._overviewWindowElement, this._windowDragging.bind(this, event.pageX,
-            this._leftResizeElement.offsetLeft, this._rightResizeElement.offsetLeft), this._endWindowDragging.bind(this), event, "ew-resize");
+        if (this._needsRefresh)
+            this._refresh();
     },
 
-    _windowDragging: function(startX, windowLeft, windowRight, event)
+    _onScroll: function(event)
     {
-        var delta = event.pageX - startX;
-        var start = windowLeft + delta;
-        var end = windowRight + delta;
-        var windowSize = windowRight - windowLeft;
-
-        if (start < 0) {
-            start = 0;
-            end = windowSize;
-        }
-
-        if (end > this._overviewGridElement.clientWidth) {
-            end = this._overviewGridElement.clientWidth;
-            start = end - windowSize;
-        }
-        this._setWindowPosition(start, end);
-
-        event.preventDefault();
+        var scrollTop = this._containerElement.scrollTop;
+        var dividersTop = Math.max(0, scrollTop);
+        this._timelineGrid.setScrollAndDividerTop(scrollTop, dividersTop);
+        this._scheduleRefresh();
     },
 
-    _resizeWindowLeft: function(start)
+    _scheduleRefresh: function()
     {
-        // Glue to edge.
-        if (start < 10)
-            start = 0;
-        this._setWindowPosition(start, null);
-    },
+        if (this._needsRefresh)
+            return;
+        this._needsRefresh = true;
 
-    _resizeWindowRight: function(end)
-    {
-        // Glue to edge.
-        if (end > this._overviewGridElement.clientWidth - 10)
-            end = this._overviewGridElement.clientWidth;
-        this._setWindowPosition(null, end);
+        if (this.visible && !("_refreshTimeout" in this))
+            this._refreshTimeout = setTimeout(this._refresh.bind(this), 100);
     },
 
-    _setWindowPosition: function(start, end)
+    _refresh: function()
     {
-        this.calculator.reset();
-        this.invalidateAllItems();
-        if (typeof start === "number") {
-            if (start > this._rightResizeElement.offsetLeft - 25)
-                start = this._rightResizeElement.offsetLeft - 25;
-
-            var windowLeft = start / this._overviewGridElement.clientWidth;
-            this.calculator.windowLeft = windowLeft;
-            this._leftResizeElement.style.left = windowLeft * 100 + "%";
-            this._overviewWindowElement.style.left = windowLeft * 100 + "%";
-        }
-        if (typeof end === "number") {
-            if (end < this._leftResizeElement.offsetLeft + 30)
-                end = this._leftResizeElement.offsetLeft + 30;
-
-            var windowRight = end / this._overviewGridElement.clientWidth;
-            this.calculator.windowRight = windowRight;
-            this._rightResizeElement.style.left = windowRight * 100 + "%";
+        this._needsRefresh = false;
+        if ("_refreshTimeout" in this) {
+            clearTimeout(this._refreshTimeout);
+            delete this._refreshTimeout;
         }
-        this._overviewWindowElement.style.width = (this.calculator.windowRight - this.calculator.windowLeft) * 100 + "%";
-        this.needsRefresh = true;
+        this._overviewPane.update(this._records);
+        this._refreshRecords();
     },
 
-    _endWindowDragging: function(event)
+    _refreshRecords: function()
     {
-        WebInspector.elementDragEnd(event);
-    }
-}
+        this._calculator.windowLeft = this._overviewPane.windowLeft;
+        this._calculator.windowRight = this._overviewPane.windowRight;
+        this._calculator.reset();
 
-WebInspector.TimelinePanel.prototype.__proto__ = WebInspector.AbstractTimelinePanel.prototype;
+        for (var i = 0; i < this._records.length; ++i)
+            this._calculator.updateBoundaries(this._records[i]);
 
+        var recordsInWindow = [];
+        for (var i = 0; i < this._records.length; ++i) {
+            var record = this._records[i];
+            var percentages = this._calculator.computeBarGraphPercentages(record);
+            if (percentages.start < 100 && percentages.end >= 0 && !record.category.hidden)
+                recordsInWindow.push(record);
+        }
 
-WebInspector.TimelineCategory = function(name, title, color)
-{
-    WebInspector.AbstractTimelineCategory.call(this, name, title, color);
-}
-
-WebInspector.TimelineCategory.prototype = {
-}
-
-WebInspector.TimelineCategory.prototype.__proto__ = WebInspector.AbstractTimelineCategory.prototype;
+        // Calculate the visible area.
+        var visibleTop = this._containerElement.scrollTop;
+        var visibleBottom = visibleTop + this._containerElement.clientHeight;
+        const rowHeight = 18;
 
+        // Convert visible area to visible indexes.
+        var startIndex = Math.max(0, Math.floor(visibleTop / rowHeight) - 1);
+        var endIndex = Math.min(recordsInWindow.length, Math.ceil(visibleBottom / rowHeight));
 
+        var listRowElement = this._sidebarListElement.firstChild;
+        var graphRowElement = this._graphRowsElement.firstChild;
+        for (var i = startIndex; i < endIndex; ++i) {
+            var record = recordsInWindow[i];
+            var isEven = !(i % 2);
 
-WebInspector.TimelineCategoryTreeElement = function(category)
-{
-    this._category = category;
+            if (!listRowElement) {
+                listRowElement = new WebInspector.TimelineRecordListRow().element;
+                this._sidebarListElement.appendChild(listRowElement);
+            }
+            if (!graphRowElement) {
+                graphRowElement = new WebInspector.TimelineRecordGraphRow().element;
+                this._graphRowsElement.appendChild(graphRowElement);
+            }
 
-    // Pass an empty title, the title gets made later in onattach.
-    TreeElement.call(this, "", null, false);
-}
+            listRowElement.listRow.update(record, isEven);
+            graphRowElement.graphRow.update(record, isEven, this._calculator);
 
-WebInspector.TimelineCategoryTreeElement.prototype = {
-    onattach: function()
-    {
-        this.listItemElement.removeChildren();
-        this.listItemElement.addStyleClass("timeline-category-tree-item");
-        this.listItemElement.addStyleClass("timeline-category-" + this._category.name);
+            listRowElement = listRowElement.nextSibling;
+            graphRowElement = graphRowElement.nextSibling;
+        }
 
-        var label = document.createElement("label");
+        while (listRowElement) {
+            var nextElement = listRowElement.nextSibling;
+            listRowElement.parentElement.removeChild(listRowElement);
+            listRowElement = nextElement;
+        }
 
-        var checkElement = document.createElement("input");
-        checkElement.type = "checkbox";
-        checkElement.className = "timeline-category-checkbox";
-        checkElement.checked = true;
-        checkElement.addEventListener("click", this._onCheckboxClicked.bind(this));
-        label.appendChild(checkElement);
+        while (graphRowElement) {
+            var nextElement = graphRowElement.nextSibling;
+            graphRowElement.parentElement.removeChild(graphRowElement);
+            graphRowElement = nextElement;
+        }
 
-        var typeElement = document.createElement("span");
-        typeElement.className = "type";
-        typeElement.textContent = this._category.title;
-        label.appendChild(typeElement);
+        this._timelineGrid.updateDividers(true, this._calculator);
 
-        this.listItemElement.appendChild(label);
+        const top = (startIndex * rowHeight) + "px";
+        this._topGapElement.style.height = top;
+        this.sidebarElement.style.top = top;
+        this.sidebarResizeElement.style.top = top;
+        this._bottomGapElement.style.height = (recordsInWindow.length - endIndex) * rowHeight + "px";
+        this._adjustScrollPosition((recordsInWindow.length + 1) * rowHeight);
     },
 
-    _onCheckboxClicked: function (event) {
-        if (event.target.checked)
-            WebInspector.panels.timeline.showCategory(this._category.name);
-        else {
-            WebInspector.panels.timeline.hideCategory(this._category.name);
-            WebInspector.panels.timeline.adjustScrollPosition();
-        }
-        WebInspector.panels.timeline._categoryGraphs[this._category.name].dimmed = !event.target.checked;
+    _adjustScrollPosition: function(totalHeight)
+    {
+        // Prevent the container from being scrolled off the end.
+        if ((this._containerElement.scrollTop + this._containerElement.offsetHeight) > totalHeight + 1)
+            this._containerElement.scrollTop = (totalHeight - this._containerElement.offsetHeight);
     }
 }
 
-WebInspector.TimelineCategoryTreeElement.prototype.__proto__ = TreeElement.prototype;
+WebInspector.TimelinePanel.prototype.__proto__ = WebInspector.Panel.prototype;
 
 
-WebInspector.TimelineRecordTreeElement = function(record)
+WebInspector.TimelineCategory = function(name, title, color)
 {
-    this._record = record;
-
-    // Pass an empty title, the title gets made later in onattach.
-    TreeElement.call(this, "", null, false);
+    this.name = name;
+    this.title = title;
+    this.color = color;
 }
 
-WebInspector.TimelineRecordTreeElement.prototype = {
-    onattach: function()
-    {
-        this.listItemElement.removeChildren();
-        this.listItemElement.addStyleClass("timeline-tree-item");
-        this.listItemElement.addStyleClass("timeline-category-" + this._record.category.name);
-
-        var iconElement = document.createElement("span");
-        iconElement.className = "timeline-tree-icon";
-        this.listItemElement.appendChild(iconElement);
-
-        this.typeElement = document.createElement("span");
-        this.typeElement.className = "type";
-        this.typeElement.textContent = this._record.title;
-        this.listItemElement.appendChild(this.typeElement);
-
-        if (this._record.details) {
-            var separatorElement = document.createElement("span");
-            separatorElement.className = "separator";
-            separatorElement.textContent = " ";
-
-            this.dataElement = document.createElement("span");
-            this.dataElement.className = "data dimmed";
-            this._updateDetails();
-
-            this.listItemElement.appendChild(separatorElement);
-            this.listItemElement.appendChild(this.dataElement);
-        }
-    },
-
-    _updateDetails: function()
-    {
-        if (this.dataElement && this._record.details !== this._details) {
-            this._details = this._record.details;
-            this.dataElement.textContent = "(" + this._details + ")";
-            this.dataElement.title = this._details;
-        }
-    },
-
-    refresh: function()
-    {
-        this._updateDetails();
-
-        if (this._record.count <= 1)
-            return;
-
-        if (!this.repeatCountElement) {
-            this.repeatCountElement = document.createElement("span");
-            this.repeatCountElement.className = "count";
-            this.listItemElement.appendChild(this.repeatCountElement);
-        }
-
-        this.repeatCountElement.textContent = "\u2009\u00d7\u2009" + this._record.count;
-    }
-}
-
-WebInspector.TimelineRecordTreeElement.prototype.__proto__ = TreeElement.prototype;
-
 
 WebInspector.TimelineCalculator = function()
 {
-    WebInspector.AbstractTimelineCalculator.call(this);
     this.windowLeft = 0.0;
     this.windowRight = 1.0;
     this._uiString = WebInspector.UIString.bind(WebInspector);
@@ -588,16 +400,6 @@ WebInspector.TimelineCalculator.prototype = {
         return {start: start, end: end};
     },
 
-    computePercentageFromEventTime: function(eventTime)
-    {
-        return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100;
-    },
-
-    computeBarGraphLabels: function(record)
-    {
-        return {tooltip: record.title};
-    },
-
     get minimumBoundary()
     {
         if (typeof this._minimumBoundary === "number")
@@ -652,109 +454,88 @@ WebInspector.TimelineCalculator.prototype = {
         return didChange;
     },
 
+    get boundarySpan()
+    {
+        return this.maximumBoundary - this.minimumBoundary;
+    },
+
     formatValue: function(value)
     {
         return Number.secondsToString(value + this.minimumBoundary - this._absoluteMinimumBoundary, this._uiString);
     }
 }
 
-WebInspector.TimelineCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype;
 
-
-WebInspector.TimelineCategoryGraph = function(category)
+WebInspector.TimelineRecordListRow = function()
 {
-    this._category = category;
+    this.element = document.createElement("div");
+    this.element.listRow = this;
 
-    this._graphElement = document.createElement("div");
-    this._graphElement.className = "timeline-graph-side timeline-overview-graph-side";
+    var iconElement = document.createElement("span");
+    iconElement.className = "timeline-tree-icon";
+    this.element.appendChild(iconElement);
 
-    this._barAreaElement = document.createElement("div");
-    this._barAreaElement.className = "timeline-graph-bar-area timeline-category-" + category.name;
-    this._graphElement.appendChild(this._barAreaElement);
-}
+    this._typeElement = document.createElement("span");
+    this._typeElement.className = "type";
+    this.element.appendChild(this._typeElement);
 
-WebInspector.TimelineCategoryGraph.prototype = {
-    get graphElement()
-    {
-        return this._graphElement;
-    },
+    var separatorElement = document.createElement("span");
+    separatorElement.className = "separator";
+    separatorElement.textContent = " ";
 
-    addChunk: function(start, end)
-    {
-        var chunk = document.createElement("div");
-        chunk.className = "timeline-graph-bar";
-        this._barAreaElement.appendChild(chunk);
-        chunk.style.setProperty("left", start + "%");
-        chunk.style.setProperty("width", (end - start) + "%");
-    },
+    this._dataElement = document.createElement("span");
+    this._dataElement.className = "data dimmed";
 
-    clearChunks: function()
-    {
-        this._barAreaElement.removeChildren();
-    },
+    this._repeatCountElement = document.createElement("span");
+    this._repeatCountElement.className = "count";
+
+    this.element.appendChild(separatorElement);
+    this.element.appendChild(this._dataElement);
+    this.element.appendChild(this._repeatCountElement);
+}
 
-    set dimmed(dimmed)
+WebInspector.TimelineRecordListRow.prototype = {
+    update: function(record, isEven)
     {
-        if (dimmed)
-            this._barAreaElement.removeStyleClass("timeline-category-" + this._category.name);
+        this.element.className = "timeline-tree-item timeline-category-" + record.category.name + (isEven ? " even" : "");
+        this._typeElement.textContent = record.title;
+
+        if (record.details) {
+            this._dataElement.textContent = "(" + record.details + ")";
+            this._dataElement.title = record.details;
+        } else {
+            this._dataElement.textContent = "";
+            this._dataElement.title = "";
+        }
+
+        if (record.count > 1)
+            this._repeatCountElement.textContent = "\u2009\u00d7\u2009" + record.count;
         else
-            this._barAreaElement.addStyleClass("timeline-category-" + this._category.name);
+            this._repeatCountElement.textContent = "";
     }
 }
 
 
-WebInspector.TimelineGraph = function(record)
+WebInspector.TimelineRecordGraphRow = function()
 {
-    this.record = record;
-
-    this._graphElement = document.createElement("div");
-    this._graphElement.className = "timeline-graph-side";
+    this.element = document.createElement("div");
+    this.element.graphRow = this;
 
     this._barAreaElement = document.createElement("div");
     this._barAreaElement.className = "timeline-graph-bar-area";
-    this._graphElement.appendChild(this._barAreaElement);
+    this.element.appendChild(this._barAreaElement);
 
     this._barElement = document.createElement("div");
     this._barElement.className = "timeline-graph-bar";
     this._barAreaElement.appendChild(this._barElement);
-
-    this._graphElement.addStyleClass("timeline-category-" + record.category.name);
-    this._hidden = false;
 }
 
-WebInspector.TimelineGraph.prototype = {
-    get graphElement()
-    {
-        return this._graphElement;
-    },
-
-    refreshLabelPositions: function()
+WebInspector.TimelineRecordGraphRow.prototype = {
+    update: function(record, isEven, calculator)
     {
-    },
-
-    refresh: function(calculator)
-    {
-        var percentages = calculator.computeBarGraphPercentages(this.record);
-        var labels = calculator.computeBarGraphLabels(this.record);
-
-        this._percentages = percentages;
-
-        if (percentages.start > 100 || percentages.end < 0) {
-            if (!this._hidden) {
-                this._graphElement.addStyleClass("hidden");
-                this.record._itemsTreeElement.listItemElement.addStyleClass("hidden");
-                this._hidden = true;
-            }
-        } else {
-            this._barElement.style.setProperty("left", percentages.start + "%");
-            this._barElement.style.setProperty("right", (100 - percentages.end) + "%");
-            if (this._hidden) {
-                this._graphElement.removeStyleClass("hidden");
-                this.record._itemsTreeElement.listItemElement.removeStyleClass("hidden");
-                this._hidden = false;
-            }
-        }
-        var tooltip = (labels.tooltip || "");
-        this._barElement.title = tooltip;
+        this.element.className = "timeline-graph-side timeline-category-" + record.category.name + (isEven ? " even" : "");
+        var percentages = calculator.computeBarGraphPercentages(record);
+        this._barElement.style.setProperty("left", percentages.start + "%");
+        this._barElement.style.setProperty("right", (100 - percentages.end) + "%");
     }
 }
index dbf406c..170d935 100644 (file)
@@ -62,6 +62,8 @@
     <file>TestController.js</file>
     <file>TextPrompt.js</file>
     <file>TimelineAgent.js</file>
+    <file>TimelineGrid.js</file>
+    <file>TimelineOverviewPane.js</file>
     <file>TimelinePanel.js</file>
     <file>TopDownProfileDataGridTree.js</file>
     <file>treeoutline.js</file>
index 1f753a1..cbc66a1 100644 (file)
@@ -1436,7 +1436,7 @@ body.inactive .placard.selected {
 }
 
 .event-bars .event-bar .header .subtitle {
-    color: rgba(90, 90, 90, 0.742188);
+    color: rgba(90, 90, 90, 0.75);
 }
 
 .event-bars .event-bar .header::before {
@@ -1781,8 +1781,8 @@ li.editing .swatch, li.editing .enabled-button,  li.editing-sub-part .delete-but
 .sidebar {
     position: absolute;
     top: 0;
+    min-height: 100%;
     left: 0;
-    bottom: 0;
     width: 200px;
     overflow-y: auto;
     overflow-x: hidden;
@@ -2594,7 +2594,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph {
 
 #resources-dividers-label-bar {
     position: absolute;
-    top: 93px;
+    top: 0;
     left: 0px;
     right: 0;
     background-color: rgba(255, 255, 255, 0.8);
@@ -2866,7 +2866,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph {
 .sidebar-resizer-vertical {
     position: absolute;
     top: 0;
-    bottom: 0;
+    min-height: 100%;
     width: 5px;
     z-index: 500;
     cursor: col-resize;
@@ -3346,10 +3346,6 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
     -webkit-box-shadow: white 1px 0 0, white -1px 0 0, white 0 1px 0, white 0 -1px 0;
 }
 
-.timeline .sidebar-resizer-vertical {
-    top: 90px;
-}
-
 #timeline-overview-grid #resources-graphs {
     position: absolute;
     top: 0;
@@ -3375,8 +3371,8 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
 
 .timeline-category-tree-item {
     height: 20px;
+    line-height: 20px;
     padding-left: 6px;
-    padding-top: 3px;
     white-space: nowrap;
     text-overflow: ellipsis;
     overflow: hidden;
@@ -3417,6 +3413,7 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
 
 .timeline-tree-item {
     height: 18px;
+    line-height: 15px;
     padding-left: 10px;
     padding-top: 2px;
     white-space: nowrap;
@@ -3441,7 +3438,7 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
     position: absolute;
 }
 
-.timeline-tree-item:nth-of-type(2n) {
+.timeline-tree-item.even {
     background-color: rgba(0, 0, 0, 0.05);
 }
 
@@ -3449,16 +3446,6 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
     color: rgba(0, 0, 0, 0.7);
 }
 
-#timeline-container .timeline-category-loading, #timeline-container .timeline-category-scripting, #timeline-container .timeline-category-rendering {
-    display: none;
-}
-
-#timeline-container .filter-all .timeline-category-loading, #timeline-container .filter-loading .timeline-category-loading,
-#timeline-container .filter-all .timeline-category-scripting, #timeline-container .filter-scripting .timeline-category-scripting,
-#timeline-container .filter-all .timeline-category-rendering, #timeline-container .filter-rendering .timeline-category-rendering {
-    display: list-item;
-}
-
 #timeline-overview-graphs {
     position: absolute;
     left: 0;
@@ -3518,7 +3505,7 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches {
     pointer-events: none;
 }
 
-.timeline-graph-side:nth-of-type(2n) {
+.timeline-graph-side.even {
     background-color: rgba(0, 0, 0, 0.05);
 }
 
index 489996c..34df32d 100644 (file)
@@ -46,6 +46,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="ChangesView.js"></script>
     <script type="text/javascript" src="ConsoleView.js"></script>
     <script type="text/javascript" src="Panel.js"></script>
+    <script type="text/javascript" src="TimelineGrid.js"></script>    
     <script type="text/javascript" src="AbstractTimelinePanel.js"></script>
     <script type="text/javascript" src="Resource.js"></script>
     <script type="text/javascript" src="ResourceCategory.js"></script>
@@ -97,6 +98,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="InjectedScriptAccess.js"></script>
     <script type="text/javascript" src="TimelineAgent.js"></script>
     <script type="text/javascript" src="TimelinePanel.js"></script>
+    <script type="text/javascript" src="TimelineOverviewPane.js"></script>
     <script type="text/javascript" src="TestController.js"></script>
 </head>
 <body class="detached">