Changeset 51339 in webkit


Ignore:
Timestamp:
Nov 24, 2009 9:15:01 AM (14 years ago)
Author:
pfeldman@chromium.org
Message:

2009-11-23 Pavel Feldman <pfeldman@chromium.org>

Reviewed by Timothy Hatcher.

Web Inspector: Implement expandable compartments on timeline panel.

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

  • inspector/front-end/TimelineOverviewPane.js: (WebInspector.TimelineOverviewPane.prototype._setWindowPosition):
  • inspector/front-end/TimelinePanel.js: (WebInspector.TimelinePanel): (WebInspector.TimelinePanel.prototype.addRecordToTimeline): (WebInspector.TimelinePanel.prototype._innerAddRecordToTimeline): (WebInspector.TimelinePanel.prototype._formatRecord): (WebInspector.TimelinePanel.prototype._refreshRecords): (WebInspector.TimelinePanel.prototype._addToRecordsWindow): (WebInspector.TimelineRecordListRow): (WebInspector.TimelineRecordListRow.prototype.update): (WebInspector.TimelineRecordListRow.prototype.dispose): (WebInspector.TimelineRecordGraphRow): (WebInspector.TimelineRecordGraphRow.prototype.update): (WebInspector.TimelineRecordGraphRow.prototype._onClick): (WebInspector.TimelineRecordGraphRow.prototype.dispose):
  • inspector/front-end/inspector.css:
Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/inspector/timeline-test.js

    r51296 r51339  
    119119function frontend_getTimelineResults() {
    120120    var result = [];
    121     var records = WebInspector.panels.timeline._records;
    122     for (var i = 0; i < records.length; ++i) {
    123         result.push(records[i].record);
     121    function addRecords(records)
     122    {
     123        if (!records)
     124            return;
     125        for (var i = 0; i < records.length; ++i) {
     126            result.push(records[i].record);
     127            addRecords(records[i].children);
     128        }
    124129    }
     130    addRecords(WebInspector.panels.timeline._records);
    125131    return result;
    126132}
  • trunk/WebCore/ChangeLog

    r51337 r51339  
     12009-11-23  Pavel Feldman  <pfeldman@chromium.org>
     2
     3        Reviewed by Timothy Hatcher.
     4
     5        Web Inspector: Implement expandable compartments on timeline panel.
     6
     7        https://bugs.webkit.org/show_bug.cgi?id=31796
     8
     9        * inspector/front-end/TimelineOverviewPane.js:
     10        (WebInspector.TimelineOverviewPane.prototype._setWindowPosition):
     11        * inspector/front-end/TimelinePanel.js:
     12        (WebInspector.TimelinePanel):
     13        (WebInspector.TimelinePanel.prototype.addRecordToTimeline):
     14        (WebInspector.TimelinePanel.prototype._innerAddRecordToTimeline):
     15        (WebInspector.TimelinePanel.prototype._formatRecord):
     16        (WebInspector.TimelinePanel.prototype._refreshRecords):
     17        (WebInspector.TimelinePanel.prototype._addToRecordsWindow):
     18        (WebInspector.TimelineRecordListRow):
     19        (WebInspector.TimelineRecordListRow.prototype.update):
     20        (WebInspector.TimelineRecordListRow.prototype.dispose):
     21        (WebInspector.TimelineRecordGraphRow):
     22        (WebInspector.TimelineRecordGraphRow.prototype.update):
     23        (WebInspector.TimelineRecordGraphRow.prototype._onClick):
     24        (WebInspector.TimelineRecordGraphRow.prototype.dispose):
     25        * inspector/front-end/inspector.css:
     26
    1272009-11-24  Mark Rowe  <mrowe@apple.com>
    228
  • trunk/WebCore/inspector/front-end/TimelineGrid.js

    r51296 r51339  
    5656    },
    5757
    58     updateDividers: function(force, calculator)
     58    updateDividers: function(force, calculator, paddingLeft)
    5959    {
    6060        var dividerCount = Math.round(this._dividersElement.offsetWidth / 64);
     
    6363            return false;
    6464
     65        if (!(typeof paddingLeft === "number"))
     66            paddingLeft = 0;
    6567        this._currentDividerSlice = slice;
    6668
    67         this._dividersElement.removeChildren();
    6869        this._eventDividersElement.removeChildren();
    69         this._dividersLabelBarElement.removeChildren();
     70        // Reuse divider elements and labels.
     71        var divider = this._dividersElement.firstChild;
     72        var dividerLabelBar = this._dividersLabelBarElement.firstChild;
    7073
    71         for (var i = 1; i <= dividerCount; ++i) {
    72             var divider = document.createElement("div");
    73             divider.className = "resources-divider";
     74        var clientWidth = this._dividersLabelBarElement.clientWidth - paddingLeft;
     75        for (var i = paddingLeft ? 0 : 1; i <= dividerCount; ++i) {
     76            if (!divider) {
     77                divider = document.createElement("div");
     78                divider.className = "resources-divider";
     79                this._dividersElement.appendChild(divider);
     80
     81                dividerLabelBar = document.createElement("div");
     82                dividerLabelBar.className = "resources-divider";
     83                var label = document.createElement("div");
     84                label.className = "resources-divider-label";
     85                dividerLabelBar._labelElement = label;
     86                dividerLabelBar.appendChild(label);
     87                this._dividersLabelBarElement.appendChild(dividerLabelBar);
     88            }
     89
    7490            if (i === dividerCount)
    7591                divider.addStyleClass("last");
    76             divider.style.left = ((i / dividerCount) * 100) + "%";
     92            else
     93                divider.removeStyleClass("last");
    7794
    78             this._dividersElement.appendChild(divider.cloneNode());
     95            var left = paddingLeft + clientWidth * (i / dividerCount);
     96            var percentLeft = 100 * left / this._dividersLabelBarElement.clientWidth + "%";
     97            divider.style.left = percentLeft;
     98            dividerLabelBar.style.left = percentLeft;
    7999
    80             var label = document.createElement("div");
    81             label.className = "resources-divider-label";
    82100            if (!isNaN(slice))
    83                 label.textContent = calculator.formatValue(slice * i);
    84             divider.appendChild(label);
     101                dividerLabelBar._labelElement.textContent = calculator.formatValue(slice * i);
    85102
    86             this._dividersLabelBarElement.appendChild(divider);
     103            divider = divider.nextSibling;
     104            dividerLabelBar = dividerLabelBar.nextSibling;
     105        }
     106
     107        // Remove extras.
     108        while (divider) {
     109            var nextDivider = divider.nextSibling;
     110            this._dividersElement.removeChild(divider);
     111            divider = nextDivider;
     112        }
     113        while (dividerLabelBar) {
     114            var nextDivider = dividerLabelBar.nextSibling;
     115            this._dividersLabelBarElement.removeChild(dividerLabelBar);
     116            dividerLabelBar = nextDivider;
    87117        }
    88118        return true;
  • trunk/WebCore/inspector/front-end/TimelineOverviewPane.js

    r51296 r51339  
    113113        }
    114114
     115        function forAllRecords(recordsArray, callback)
     116        {
     117            if (!recordsArray)
     118                return;
     119            for (var i = 0; i < recordsArray.length; ++i) {
     120                callback(recordsArray[i]);
     121                forAllRecords(recordsArray[i].children, callback);
     122            }
     123        }
     124
    115125        // Create sparse arrays with 101 cells each to fill with chunks for a given category.
    116126        this._overviewCalculator.reset();
    117 
    118         for (var i = 1; i < records.length; ++i)
    119             this._overviewCalculator.updateBoundaries(records[i]);
    120 
    121         for (var i = 0; i < records.length; ++i) {
    122             var record = records[i];
     127        forAllRecords(records, this._overviewCalculator.updateBoundaries.bind(this._overviewCalculator));
     128
     129        function markTimeline(record)
     130        {
    123131            var percentages = this._overviewCalculator.computeBarGraphPercentages(record);
    124            
     132
    125133            var end = Math.round(percentages.end);
    126134            var categoryName = record.category.name;
     
    128136                timelines[categoryName][j] = true;
    129137        }
     138        forAllRecords(records, markTimeline.bind(this));
    130139
    131140        // Convert sparse arrays to continuous segments, render graphs for each.
     
    231240    {
    232241        if (typeof start === "number") {
    233             if (start > this._rightResizeElement.offsetLeft - 25)
    234                 start = this._rightResizeElement.offsetLeft - 25;
     242            if (start > this._rightResizeElement.offsetLeft - 4)
     243                start = this._rightResizeElement.offsetLeft - 4;
    235244
    236245            this.windowLeft = start / this._overviewGrid.element.clientWidth;
     
    239248        }
    240249        if (typeof end === "number") {
    241             if (end < this._leftResizeElement.offsetLeft + 30)
    242                 end = this._leftResizeElement.offsetLeft + 30;
     250            if (end < this._leftResizeElement.offsetLeft + 12)
     251                end = this._leftResizeElement.offsetLeft + 12;
    243252
    244253            this.windowRight = end / this._overviewGrid.element.clientWidth;
  • trunk/WebCore/inspector/front-end/TimelinePanel.js

    r51297 r51339  
    6161
    6262    this._timelineGrid = new WebInspector.TimelineGrid();
    63     var itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
    64     itemsGraphsElement.id = "timeline-graphs";
     63    this._itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
     64    this._itemsGraphsElement.id = "timeline-graphs";
    6565    this._containerContentElement.appendChild(this._timelineGrid.element);
    6666
    6767    this._topGapElement = document.createElement("div");
    6868    this._topGapElement.className = "timeline-gap";
    69     itemsGraphsElement.appendChild(this._topGapElement);
     69    this._itemsGraphsElement.appendChild(this._topGapElement);
    7070
    7171    this._graphRowsElement = document.createElement("div");
    72     itemsGraphsElement.appendChild(this._graphRowsElement);
     72    this._itemsGraphsElement.appendChild(this._graphRowsElement);
    7373
    7474    this._bottomGapElement = document.createElement("div");
    7575    this._bottomGapElement.className = "timeline-gap";
    76     itemsGraphsElement.appendChild(this._bottomGapElement);
     76    this._itemsGraphsElement.appendChild(this._bottomGapElement);
    7777
    7878    this._createStatusbarButtons();
     
    137137    addRecordToTimeline: function(record)
    138138    {
     139        this._innerAddRecordToTimeline(record, this._records);
     140        this._scheduleRefresh();
     141    },
     142
     143    _innerAddRecordToTimeline: function(record, collection)
     144    {
    139145        var formattedRecord = this._formatRecord(record);
     146
    140147        // Glue subsequent records with same category and title together if they are closer than 100ms to each other.
    141148        if (this._lastRecord && (!record.children || !record.children.length) &&
     
    147154            this._lastRecord.count++;
    148155        } else {
    149             this._records.push(formattedRecord);
    150 
    151             for (var i = 0; record.children && i < record.children.length; ++i)
    152                 this.addRecordToTimeline(record.children[i]);
     156            collection.push(formattedRecord);
     157            for (var i = 0; record.children && i < record.children.length; ++i) {
     158                if (!formattedRecord.children)
     159                    formattedRecord.children = [];
     160                var formattedChild = this._innerAddRecordToTimeline(record.children[i], formattedRecord.children);
     161                formattedChild.parent = formattedRecord;
     162            }
    153163            this._lastRecord = record.children && record.children.length ? null : formattedRecord;
    154164        }
    155         this._scheduleRefresh();
     165        return formattedRecord;
    156166    },
    157167
     
    197207        } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceReceiveResponse) {
    198208            var sendRequestRecord = this._sendRequestRecords[record.data.identifier];
    199             sendRequestRecord._responseReceivedFormattedTime = formattedRecord.startTime;
    200             formattedRecord.startTime = sendRequestRecord.startTime;
    201             sendRequestRecord.details = this._getRecordDetails(record);
     209            if (sendRequestRecord) { // False if we started instrumentation in the middle of request.
     210                sendRequestRecord._responseReceivedFormattedTime = formattedRecord.startTime;
     211                formattedRecord.startTime = sendRequestRecord.startTime;
     212                sendRequestRecord.details = this._getRecordDetails(record);
     213            }
    202214        } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceFinish) {
    203215            var sendRequestRecord = this._sendRequestRecords[record.data.identifier];
     
    315327            var percentages = this._calculator.computeBarGraphPercentages(record);
    316328            if (percentages.start < 100 && percentages.end >= 0 && !record.category.hidden)
    317                 recordsInWindow.push(record);
     329                this._addToRecordsWindow(record, recordsInWindow);
    318330        }
    319331
     
    321333        var visibleTop = this._containerElement.scrollTop;
    322334        var visibleBottom = visibleTop + this._containerElement.clientHeight;
     335
     336        // Define row height, should be in sync with styles for timeline graphs.
    323337        const rowHeight = 18;
    324 
    325         // Convert visible area to visible indexes.
     338        const expandOffset = 15;
     339
     340        // Convert visible area to visible indexes. Always include top-level record for a visible nested record.
    326341        var startIndex = Math.max(0, Math.floor(visibleTop / rowHeight) - 1);
     342        while (startIndex > 0 && recordsInWindow[startIndex].parent)
     343            startIndex--;
    327344        var endIndex = Math.min(recordsInWindow.length, Math.ceil(visibleBottom / rowHeight));
    328 
     345        while (endIndex < recordsInWindow.length - 1 && recordsInWindow[startIndex].parent)
     346            endIndex++;
     347
     348        // Resize gaps first.
     349        const top = (startIndex * rowHeight) + "px";
     350        this._topGapElement.style.height = top;
     351        this.sidebarElement.style.top = top;
     352        this.sidebarResizeElement.style.top = top;
     353        this._bottomGapElement.style.height = (recordsInWindow.length - endIndex) * rowHeight + "px";
     354
     355        // Update visible rows.
    329356        var listRowElement = this._sidebarListElement.firstChild;
    330357        var graphRowElement = this._graphRowsElement.firstChild;
     358        var width = this._graphRowsElement.offsetWidth;
     359        var scheduleRefreshCallback = this._scheduleRefresh.bind(this, true);
    331360        for (var i = startIndex; i < endIndex; ++i) {
    332361            var record = recordsInWindow[i];
     
    338367            }
    339368            if (!graphRowElement) {
    340                 graphRowElement = new WebInspector.TimelineRecordGraphRow().element;
     369                graphRowElement = new WebInspector.TimelineRecordGraphRow(this._itemsGraphsElement, scheduleRefreshCallback, rowHeight).element;
    341370                this._graphRowsElement.appendChild(graphRowElement);
    342371            }
    343372
    344373            listRowElement.listRow.update(record, isEven);
    345             graphRowElement.graphRow.update(record, isEven, this._calculator);
     374            graphRowElement.graphRow.update(record, isEven, this._calculator, width, expandOffset);
    346375
    347376            listRowElement = listRowElement.nextSibling;
     
    349378        }
    350379
     380        // Remove extra rows.
    351381        while (listRowElement) {
    352382            var nextElement = listRowElement.nextSibling;
    353             listRowElement.parentElement.removeChild(listRowElement);
     383            listRowElement.listRow.dispose();
    354384            listRowElement = nextElement;
    355385        }
    356 
    357386        while (graphRowElement) {
    358387            var nextElement = graphRowElement.nextSibling;
    359             graphRowElement.parentElement.removeChild(graphRowElement);
     388            graphRowElement.graphRow.dispose();
    360389            graphRowElement = nextElement;
    361390        }
    362391
    363         this._timelineGrid.updateDividers(true, this._calculator);
    364 
    365         const top = (startIndex * rowHeight) + "px";
    366         this._topGapElement.style.height = top;
    367         this.sidebarElement.style.top = top;
    368         this.sidebarResizeElement.style.top = top;
    369         this._bottomGapElement.style.height = (recordsInWindow.length - endIndex) * rowHeight + "px";
     392        // Reserve some room for expand / collapse controls to the left for records that start at 0ms.
     393        var timelinePaddingLeft = this._calculator.windowLeft === 0 ? expandOffset : 0;
     394        this._timelineGrid.updateDividers(true, this._calculator, timelinePaddingLeft);
    370395        this._adjustScrollPosition((recordsInWindow.length + 1) * rowHeight);
     396    },
     397
     398    _addToRecordsWindow: function(record, recordsWindow)
     399    {
     400        recordsWindow.push(record);
     401        if (!record.collapsed) {
     402            var index = recordsWindow.length;
     403            for (var i = 0; record.children && i < record.children.length; ++i)
     404                this._addToRecordsWindow(record.children[i], recordsWindow);
     405            record.visibleChildrenCount = recordsWindow.length - index;
     406        }
    371407    },
    372408
     
    475511    this.element = document.createElement("div");
    476512    this.element.listRow = this;
    477 
    478513    var iconElement = document.createElement("span");
    479514    iconElement.className = "timeline-tree-icon";
     
    517552        else
    518553            this._repeatCountElement.textContent = "";
     554    },
     555
     556    dispose: function()
     557    {
     558        this.element.parentElement.removeChild(this.element);
    519559    }
    520560}
    521561
    522562
    523 WebInspector.TimelineRecordGraphRow = function()
     563WebInspector.TimelineRecordGraphRow = function(graphContainer, refreshCallback, rowHeight)
    524564{
    525565    this.element = document.createElement("div");
     
    533573    this._barElement.className = "timeline-graph-bar";
    534574    this._barAreaElement.appendChild(this._barElement);
     575
     576    this._expandElement = document.createElement("div");
     577    this._expandElement.className = "timeline-expandable";
     578    graphContainer.appendChild(this._expandElement);
     579
     580    var leftBorder = document.createElement("div");
     581    leftBorder.className = "timeline-expandable-left";
     582    this._expandElement.appendChild(leftBorder);
     583
     584    this._expandElement.addEventListener("click", this._onClick.bind(this));
     585    this._refreshCallback = refreshCallback;
     586    this._rowHeight = rowHeight;
    535587}
    536588
    537589WebInspector.TimelineRecordGraphRow.prototype = {
    538     update: function(record, isEven, calculator)
    539     {
     590    update: function(record, isEven, calculator, clientWidth, expandOffset)
     591    {
     592        this._record = record;
    540593        this.element.className = "timeline-graph-side timeline-category-" + record.category.name + (isEven ? " even" : "");
    541594        var percentages = calculator.computeBarGraphPercentages(record);
    542         this._barElement.style.setProperty("left", percentages.start + "%");
    543         this._barElement.style.setProperty("right", (100 - percentages.end) + "%");
     595        var left = percentages.start / 100 * clientWidth;
     596        var width = (percentages.end - percentages.start) / 100 * clientWidth;
     597        this._barElement.style.left = (left + expandOffset) + "px";
     598        this._barElement.style.width = width + "px";
     599
     600        if (record.visibleChildrenCount) {
     601            this._expandElement.style.top = this.element.offsetTop + "px";
     602            this._expandElement.style.left = left + "px";
     603            this._expandElement.style.width = Math.max(12, width + 25) + "px";
     604            if (!record.collapsed) {
     605                this._expandElement.style.height = (record.visibleChildrenCount + 1) * this._rowHeight + "px";
     606                this._expandElement.addStyleClass("timeline-expandable-expanded");
     607                this._expandElement.removeStyleClass("timeline-expandable-collapsed");
     608            } else {
     609                this._expandElement.style.height = this._rowHeight + "px";
     610                this._expandElement.addStyleClass("timeline-expandable-collapsed");
     611                this._expandElement.removeStyleClass("timeline-expandable-expanded");
     612            }
     613            this._expandElement.removeStyleClass("hidden");
     614        } else {
     615            this._expandElement.addStyleClass("hidden");
     616        }
     617    },
     618
     619    _onClick: function(event)
     620    {
     621        this._record.collapsed = !this._record.collapsed;
     622        this._refreshCallback();
     623    },
     624
     625    dispose: function()
     626    {
     627        this.element.parentElement.removeChild(this.element);
     628        this._expandElement.parentElement.removeChild(this._expandElement);
    544629    }
    545630}
  • trunk/WebCore/inspector/front-end/inspector.css

    r51297 r51339  
    32893289}
    32903290
     3291.timeline .sidebar {
     3292    overflow-y: hidden;
     3293    z-index: 100;
     3294}
     3295
    32913296#timeline-overview-separator {
    32923297    position: absolute;
     
    34123417    height: 18px;
    34133418    line-height: 15px;
     3419    padding-right: 5px;
    34143420    padding-left: 10px;
    34153421    padding-top: 2px;
     
    34173423    text-overflow: ellipsis;
    34183424    overflow: hidden;
     3425}
     3426
     3427.timeline-expandable {
     3428    position: absolute;
     3429    border-left: 1px solid rgb(163, 163, 163);
     3430}
     3431
     3432.timeline-expandable-left {
     3433    position: absolute;
     3434    top: 0;
     3435    bottom: 0;
     3436    left: 0;
     3437    width: 3px;
     3438    border-top: 1px solid rgb(163, 163, 163);
     3439    border-bottom: 1px solid rgb(163, 163, 163);
     3440}
     3441
     3442.timeline-expandable-collapsed {
     3443    background-image: url(Images/disclosureTriangleSmallRightBlack.png);
     3444    background-position-x: 1px;
     3445    background-position-y: 2px;
     3446    background-repeat: no-repeat;
     3447}
     3448
     3449.timeline-expandable-expanded {
     3450    background-image: url(Images/disclosureTriangleSmallDownBlack.png);
     3451    background-position-x: 1px;
     3452    background-position-y: 3px;
     3453    background-repeat: no-repeat;
    34193454}
    34203455
     
    34853520    top: 0;
    34863521    bottom: 0;
    3487     right: 8px;
    3488     left: 9px;
     3522    right: 0;
     3523    left: 3px;
    34893524    pointer-events: none;
    34903525}
  • trunk/WebCore/inspector/front-end/utilities.js

    r50909 r51339  
    551551        formatterFunction = String.sprintf;
    552552
     553    if (seconds === 0)
     554        return "0";
     555
    553556    var ms = seconds * 1000;
    554557    if (higherResolution && ms < 1000)
Note: See TracChangeset for help on using the changeset viewer.