Changeset 162681 in webkit


Ignore:
Timestamp:
Jan 23, 2014 7:41:22 PM (10 years ago)
Author:
timothy@apple.com
Message:

Refactor TimelineRecordBar combining logic into a helper function.

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

Reviewed by Joseph Pecoraro.

  • UserInterface/LayoutTimelineOverviewGraph.js:

(WebInspector.LayoutTimelineOverviewGraph.prototype.updateLayout.createBar):
(WebInspector.LayoutTimelineOverviewGraph.prototype.updateLayout):
Use TimelineRecordBar.createCombinedBars.

  • UserInterface/NetworkTimelineOverviewGraph.css:

(.timeline-overview-graph.network):
(.timeline-overview-graph.network > .graph-row > .timeline-record-bar):
Cleaned up since we are using TimelineRecordBar.

  • UserInterface/NetworkTimelineOverviewGraph.js:

(WebInspector.NetworkTimelineOverviewGraph.prototype.reset):
(WebInspector.NetworkTimelineOverviewGraph.prototype.updateLayout.createBar):
(WebInspector.NetworkTimelineOverviewGraph.prototype.updateLayout):
Use TimelineRecordBar.createCombinedBars.

  • UserInterface/ScriptTimelineOverviewGraph.js:

(WebInspector.ScriptTimelineOverviewGraph.prototype.updateLayout.createBar):
(WebInspector.ScriptTimelineOverviewGraph.prototype.updateLayout):
Use TimelineRecordBar.createCombinedBars.

  • UserInterface/TimelineDataGridNode.js:

(WebInspector.TimelineDataGridNode.prototype.refreshGraph.createBar):
(WebInspector.TimelineDataGridNode.prototype.refreshGraph):
Use TimelineRecordBar.createCombinedBars.

  • UserInterface/TimelineRecordBar.css:

(.timeline-record-bar.unfinished > .segment):
(.timeline-record-bar.has-inactive-segment > .segment:not(.inactive)):
(:focus .selected .timeline-record-bar > .segment.inactive):
Improved selected appearance and don't assume .segment.inactive exists.

  • UserInterface/TimelineRecordBar.js:

(WebInspector.TimelineRecordBar):
(WebInspector.TimelineRecordBar.createCombinedBars.compareByActiveStartTime): Added.
(WebInspector.TimelineRecordBar.createCombinedBars): Added.
(WebInspector.TimelineRecordBar.prototype.get renderMode): Added.
(WebInspector.TimelineRecordBar.prototype.set renderMode): Added.
(WebInspector.TimelineRecordBar.prototype.set records):
(WebInspector.TimelineRecordBar.prototype.refresh):
Lazy create DOM elements. Support rendering one or both segments. Doing this lets
combined inactive segments to sit behind multiple active segments.

Location:
trunk/Source/WebInspectorUI
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r162564 r162681  
     12014-01-23  Timothy Hatcher  <timothy@apple.com>
     2
     3        Refactor TimelineRecordBar combining logic into a helper function.
     4
     5        https://bugs.webkit.org/show_bug.cgi?id=127530
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        * UserInterface/LayoutTimelineOverviewGraph.js:
     10        (WebInspector.LayoutTimelineOverviewGraph.prototype.updateLayout.createBar):
     11        (WebInspector.LayoutTimelineOverviewGraph.prototype.updateLayout):
     12        Use TimelineRecordBar.createCombinedBars.
     13
     14        * UserInterface/NetworkTimelineOverviewGraph.css:
     15        (.timeline-overview-graph.network):
     16        (.timeline-overview-graph.network > .graph-row > .timeline-record-bar):
     17        Cleaned up since we are using TimelineRecordBar.
     18
     19        * UserInterface/NetworkTimelineOverviewGraph.js:
     20        (WebInspector.NetworkTimelineOverviewGraph.prototype.reset):
     21        (WebInspector.NetworkTimelineOverviewGraph.prototype.updateLayout.createBar):
     22        (WebInspector.NetworkTimelineOverviewGraph.prototype.updateLayout):
     23        Use TimelineRecordBar.createCombinedBars.
     24
     25        * UserInterface/ScriptTimelineOverviewGraph.js:
     26        (WebInspector.ScriptTimelineOverviewGraph.prototype.updateLayout.createBar):
     27        (WebInspector.ScriptTimelineOverviewGraph.prototype.updateLayout):
     28        Use TimelineRecordBar.createCombinedBars.
     29
     30        * UserInterface/TimelineDataGridNode.js:
     31        (WebInspector.TimelineDataGridNode.prototype.refreshGraph.createBar):
     32        (WebInspector.TimelineDataGridNode.prototype.refreshGraph):
     33        Use TimelineRecordBar.createCombinedBars.
     34
     35        * UserInterface/TimelineRecordBar.css:
     36        (.timeline-record-bar.unfinished > .segment):
     37        (.timeline-record-bar.has-inactive-segment > .segment:not(.inactive)):
     38        (:focus .selected .timeline-record-bar > .segment.inactive):
     39        Improved selected appearance and don't assume .segment.inactive exists.
     40
     41        * UserInterface/TimelineRecordBar.js:
     42        (WebInspector.TimelineRecordBar):
     43        (WebInspector.TimelineRecordBar.createCombinedBars.compareByActiveStartTime): Added.
     44        (WebInspector.TimelineRecordBar.createCombinedBars): Added.
     45        (WebInspector.TimelineRecordBar.prototype.get renderMode): Added.
     46        (WebInspector.TimelineRecordBar.prototype.set renderMode): Added.
     47        (WebInspector.TimelineRecordBar.prototype.set records):
     48        (WebInspector.TimelineRecordBar.prototype.refresh):
     49        Lazily create DOM elements. Support rendering one or both segments. Doing this lets
     50        combined inactive segments sit behind multiple active segments.
     51
    1522014-01-22  Timothy Hatcher  <timothy@apple.com>
    253
  • trunk/Source/WebInspectorUI/UserInterface/LayoutTimelineOverviewGraph.js

    r162419 r162681  
    5959        WebInspector.TimelineOverviewGraph.prototype.updateLayout.call(this);
    6060
    61         var startTime = this.startTime;
    62         var currentTime = this.currentTime;
    63         var endTime = this.endTime;
    64         var duration = (endTime - startTime);
     61        var visibleWidth = this.element.offsetWidth;
     62        var secondsPerPixel = (this.endTime - this.startTime) / visibleWidth;
    6563
    66         var visibleWidth = this.element.offsetWidth;
    67         var secondsPerPixel = duration / visibleWidth;
    6864        var recordBarIndex = 0;
    69         var barRecords = [];
    7065
    71         function createBar(barRecords)
     66        function createBar(records, renderMode)
    7267        {
    7368            var timelineRecordBar = this._timelineRecordBars[recordBarIndex];
    7469            if (!timelineRecordBar)
    7570                timelineRecordBar = this._timelineRecordBars[recordBarIndex] = new WebInspector.TimelineRecordBar;
    76             timelineRecordBar.records = barRecords;
     71            timelineRecordBar.renderMode = renderMode;
     72            timelineRecordBar.records = records;
    7773            timelineRecordBar.refresh(this);
    7874            if (!timelineRecordBar.element.parentNode)
     
    8177        }
    8278
    83         for (var record of this._layoutTimeline.records) {
    84             // If this bar is completely before the bounds of the graph, skip this record.
    85             if (record.endTime < startTime)
    86                 continue;
    87 
    88             // If this record is completely after the current time or end time, break out now.
    89             // Records are sorted, so all records after this will be beyond the current or end time too.
    90             if (record.startTime > currentTime || record.startTime > endTime)
    91                 break;
    92 
    93             // Check if the previous record is a different type or far enough away to create the bar.
    94             if (barRecords.length && WebInspector.TimelineRecordBar.recordsCannotBeCombined(barRecords, record, secondsPerPixel)) {
    95                 createBar.call(this, barRecords);
    96                 barRecords = [];
    97             }
    98 
    99             barRecords.push(record);
    100         }
    101 
    102         // Create the bar for the last record if needed.
    103         if (barRecords.length)
    104             createBar.call(this, barRecords);
     79        WebInspector.TimelineRecordBar.createCombinedBars(this._layoutTimeline.records, secondsPerPixel, this, createBar.bind(this));
    10580
    10681        // Remove the remaining unused TimelineRecordBars.
  • trunk/Source/WebInspectorUI/UserInterface/NetworkTimelineOverviewGraph.css

    r162419 r162681  
    2525
    2626.timeline-overview-graph.network {
    27     padding-top: 2px
     27    padding-top: 3px;
    2828}
    2929
    3030.timeline-overview-graph.network > .graph-row {
    31     position: relative;
    3231    height: 5px;
    3332}
    3433
    35 .timeline-overview-graph.network > .graph-row > .bar {
    36     position: absolute;
     34.timeline-overview-graph.network > .graph-row > .timeline-record-bar {
    3735    height: 4px;
    38     background-color: rgb(120, 176, 225);
    39     border: 1px solid rgb(61, 147, 200);
    40     border-radius: 2px;
    4136    margin-top: 1px;
    42     min-width: 3px;
    4337}
    4438
    45 .timeline-overview-graph.network > .graph-row > .bar.inactive {
    46     background-color: rgb(167, 204, 237);
    47     border-color: rgb(127, 185, 220);
    48 }
    49 
    50 .timeline-overview-graph.network > .graph-row > .bar.inactive,
    51 .timeline-overview-graph.network > .graph-row > .bar.unfinished {
    52     border-top-right-radius: 0;
    53     border-bottom-right-radius: 0;
    54     border-right: none;
    55 }
    56 
    57 .timeline-overview-graph.network > .graph-row > .bar:not(.inactive) {
    58     border-top-left-radius: 0;
    59     border-bottom-left-radius: 0;
    60     z-index: 1;
     39.timeline-overview-graph.network > .graph-row > .timeline-record-bar > .segment:not(.inactive) {
    6140    box-shadow: white 1px 0 0;
    6241}
    6342
    64 .timeline-overview-graph.network:nth-child(even) > .graph-row > .bar:not(.inactive) {
     43.timeline-overview-graph.network:nth-child(even) > .graph-row > .timeline-record-bar > .segment:not(.inactive) {
    6544    box-shadow: rgb(247, 247, 247) 1px 0 0;
    6645}
  • trunk/Source/WebInspectorUI/UserInterface/NetworkTimelineOverviewGraph.js

    r162419 r162681  
    4444WebInspector.NetworkTimelineOverviewGraph.UnfinishedStyleClassName = "unfinished";
    4545WebInspector.NetworkTimelineOverviewGraph.MaximumRowCount = 6;
    46 WebInspector.NetworkTimelineOverviewGraph.MinimumBarPaddingPixels = 5;
    4746
    4847WebInspector.NetworkTimelineOverviewGraph.prototype = {
     
    6362
    6463        this.element.removeChildren();
     64
     65        for (var rowRecords of this._timelineRecordGridRows) {
     66            rowRecords.__element = document.createElement("div");
     67            rowRecords.__element.className = WebInspector.NetworkTimelineOverviewGraph.GraphRowStyleClassName;
     68            this.element.appendChild(rowRecords.__element);
     69
     70            rowRecords.__recordBars = [];
     71        }
    6572    },
    6673
     
    6976        WebInspector.TimelineOverviewGraph.prototype.updateLayout.call(this);
    7077
    71         var startTime = this.startTime;
    72         var currentTime = this.currentTime;
    73         var endTime = this.endTime;
    74         var duration = (endTime - startTime);
     78        var visibleWidth = this.element.offsetWidth;
     79        var secondsPerPixel = (this.endTime - this.startTime) / visibleWidth;
    7580
    76         var visibleWidth = this.element.offsetWidth;
    77         var secondsPerPixel = duration / visibleWidth;
     81        var recordBarIndex = 0;
    7882
    79         function updateElementPosition(element, newPosition, property)
     83        function createBar(rowElement, rowRecordBars, records, renderMode)
    8084        {
    81             newPosition *= 100;
    82             newPosition = newPosition.toFixed(2);
    83 
    84             var currentPosition = parseFloat(element.style[property]).toFixed(2);
    85             if (currentPosition !== newPosition)
    86                 element.style[property] = newPosition + "%";
    87         }
    88 
    89         function createBar(barElementCache, rowElement, barStartTime, barEndTime, inactive)
    90         {
    91             if (barStartTime > currentTime)
    92                 return;
    93 
    94             var barElement = barElementCache.shift();
    95             if (!barElement) {
    96                 barElement = document.createElement("div");
    97                 barElement.classList.add(WebInspector.NetworkTimelineOverviewGraph.BarStyleClassName);
    98             }
    99 
    100             barElement.classList.toggle(WebInspector.NetworkTimelineOverviewGraph.InactiveBarStyleClassName, inactive);
    101 
    102             if (barEndTime >= currentTime) {
    103                 barEndTime = currentTime;
    104                 barElement.classList.add(WebInspector.NetworkTimelineOverviewGraph.UnfinishedStyleClassName);
    105             } else
    106                 barElement.classList.remove(WebInspector.NetworkTimelineOverviewGraph.UnfinishedStyleClassName);
    107 
    108             if (inactive) {
    109                 var newBarRightPosition = 1 - ((barEndTime - startTime) / duration);
    110                 updateElementPosition(barElement, newBarRightPosition, "right");
    111                 barElement.style.removeProperty("left");
    112             } else {
    113                 var newBarLeftPosition = (barStartTime - startTime) / duration;
    114                 updateElementPosition(barElement, newBarLeftPosition, "left");
    115                 barElement.style.removeProperty("right");
    116             }
    117 
    118             var newBarWidth = ((barEndTime - barStartTime) / duration);
    119             updateElementPosition(barElement, newBarWidth, "width");
    120 
    121             if (!barElement.parendNode)
    122                 rowElement.appendChild(barElement);
     85            var timelineRecordBar = rowRecordBars[recordBarIndex];
     86            if (!timelineRecordBar)
     87                timelineRecordBar = rowRecordBars[recordBarIndex] = new WebInspector.TimelineRecordBar;
     88            timelineRecordBar.renderMode = renderMode;
     89            timelineRecordBar.records = records;
     90            timelineRecordBar.refresh(this);
     91            if (!timelineRecordBar.element.parentNode)
     92                rowElement.appendChild(timelineRecordBar.element);
     93            ++recordBarIndex;
    12394        }
    12495
    12596        for (var rowRecords of this._timelineRecordGridRows) {
    126             var rowElement = rowRecords.__rowElement;
    127             if (!rowElement) {
    128                 rowElement = rowRecords.__rowElement = document.createElement("div");
    129                 rowElement.className = WebInspector.NetworkTimelineOverviewGraph.GraphRowStyleClassName;
    130                 this.element.appendChild(rowElement);
     97            var rowElement = rowRecords.__element;
     98            var rowRecordBars = rowRecords.__recordBars;
     99
     100            recordBarIndex = 0;
     101
     102            WebInspector.TimelineRecordBar.createCombinedBars(rowRecords, secondsPerPixel, this, createBar.bind(this, rowElement, rowRecordBars));
     103
     104            // Remove the remaining unused TimelineRecordBars.
     105            for (; recordBarIndex < rowRecordBars.length; ++recordBarIndex) {
     106                rowRecordBars[recordBarIndex].records = null;
     107                rowRecordBars[recordBarIndex].element.remove();
    131108            }
    132 
    133             if (!rowRecords.length)
    134                 continue;
    135 
    136             // Save the current bar elements to reuse.
    137             var barElementCache = Array.prototype.slice.call(rowElement.childNodes);
    138 
    139             var inactiveStartTime = NaN;
    140             var inactiveEndTime = NaN;
    141             var activeStartTime = NaN;
    142             var activeEndTime = NaN;
    143 
    144             for (var record of rowRecords) {
    145                 if (isNaN(record.startTime))
    146                     continue;
    147 
    148                 // If this bar is completely before the bounds of the graph, skip this record.
    149                 if (record.endTime < startTime)
    150                     continue;
    151 
    152                 // If this record is completely after the current time or end time, break out now.
    153                 // Records are sorted, so all records after this will be beyond the current or end time too.
    154                 if (record.startTime > currentTime || record.startTime > endTime)
    155                     break;
    156 
    157                 // Check if the previous record is far enough away to create the inactive bar.
    158                 if (!isNaN(inactiveStartTime) && inactiveEndTime + (secondsPerPixel * WebInspector.NetworkTimelineOverviewGraph.MinimumBarPaddingPixels) <= record.startTime) {
    159                     createBar.call(this, barElementCache, rowElement, inactiveStartTime, inactiveEndTime, true);
    160                     inactiveStartTime = NaN;
    161                     inactiveEndTime = NaN;
    162                 }
    163 
    164                 // If this is a new bar, peg the start time.
    165                 if (isNaN(inactiveStartTime))
    166                     inactiveStartTime = record.startTime;
    167 
    168                 // Update the end time to be the maximum we encounter. inactiveEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
    169                 inactiveEndTime = Math.max(inactiveEndTime || 0, record.activeStartTime);
    170 
    171                 // Check if the previous record is far enough away to create the active bar. We also create it now if the current record has no active state time.
    172                 if (!isNaN(activeStartTime) && (activeEndTime + (secondsPerPixel * WebInspector.NetworkTimelineOverviewGraph.MinimumBarPaddingPixels) <= record.activeStartTime || isNaN(record.activeStartTime))) {
    173                     if (!isNaN(activeEndTime)) {
    174                         createBar.call(this, barElementCache, rowElement, activeStartTime, activeEndTime);
    175                         activeStartTime = NaN;
    176                         activeEndTime = NaN;
    177                     }
    178                 }
    179 
    180                 if (isNaN(record.activeStartTime))
    181                     continue;
    182 
    183                 // If this is a new bar, peg the start time.
    184                 if (isNaN(activeStartTime))
    185                     activeStartTime = record.activeStartTime;
    186 
    187                 // Update the end time to be the maximum we encounter. activeEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
    188                 if (!isNaN(record.endTime))
    189                     activeEndTime = Math.max(activeEndTime || 0, record.endTime);
    190             }
    191 
    192             // Create the inactive bar for the last record if needed.
    193             if (!isNaN(inactiveStartTime))
    194                 createBar.call(this, barElementCache, rowElement, inactiveStartTime, inactiveEndTime || currentTime, true);
    195 
    196             // Create the active bar for the last record if needed.
    197             if (!isNaN(activeStartTime))
    198                 createBar.call(this, barElementCache, rowElement, activeStartTime, activeEndTime || currentTime);
    199 
    200             // Remove any unused bar elements.
    201             for (var barElement of barElementCache)
    202                 barElement.remove();
    203109        }
    204110    },
  • trunk/Source/WebInspectorUI/UserInterface/ScriptTimelineOverviewGraph.js

    r162419 r162681  
    5959        WebInspector.TimelineOverviewGraph.prototype.updateLayout.call(this);
    6060
    61         var startTime = this.startTime;
    62         var currentTime = this.currentTime;
    63         var endTime = this.endTime;
    64         var duration = (endTime - startTime);
     61        var visibleWidth = this.element.offsetWidth;
     62        var secondsPerPixel = (this.endTime - this.startTime) / visibleWidth;
    6563
    66         var visibleWidth = this.element.offsetWidth;
    67         var secondsPerPixel = duration / visibleWidth;
    6864        var recordBarIndex = 0;
    69         var barRecords = [];
    7065
    71         function createBar(barRecords)
     66        function createBar(records, renderMode)
    7267        {
    7368            var timelineRecordBar = this._timelineRecordBars[recordBarIndex];
    7469            if (!timelineRecordBar)
    7570                timelineRecordBar = this._timelineRecordBars[recordBarIndex] = new WebInspector.TimelineRecordBar;
    76             timelineRecordBar.records = barRecords;
     71            timelineRecordBar.renderMode = renderMode;
     72            timelineRecordBar.records = records;
    7773            timelineRecordBar.refresh(this);
    7874            if (!timelineRecordBar.element.parentNode)
     
    8177        }
    8278
    83         for (var record of this._scriptTimeline.records) {
    84             // If this bar is completely before the bounds of the graph, skip this record.
    85             if (record.endTime < startTime)
    86                 continue;
    87 
    88             // If this record is completely after the current time or end time, break out now.
    89             // Records are sorted, so all records after this will be beyond the current or end time too.
    90             if (record.startTime > currentTime || record.startTime > endTime)
    91                 break;
    92 
    93             // Check if the previous record is a different type or far enough away to create the bar.
    94             if (barRecords.length && WebInspector.TimelineRecordBar.recordsCannotBeCombined(barRecords, record, secondsPerPixel)) {
    95                 createBar.call(this, barRecords);
    96                 barRecords = [];
    97             }
    98 
    99             barRecords.push(record);
    100         }
    101 
    102         // Create the bar for the last record if needed.
    103         if (barRecords.length)
    104             createBar.call(this, barRecords);
     79        WebInspector.TimelineRecordBar.createCombinedBars(this._scriptTimeline.records, secondsPerPixel, this, createBar.bind(this));
    10580
    10681        // Remove the remaining unused TimelineRecordBars.
  • trunk/Source/WebInspectorUI/UserInterface/TimelineDataGridNode.js

    r162564 r162681  
    243243        var recordBarIndex = 0;
    244244
    245         function createBar(barRecords)
     245        function createBar(records, renderMode)
    246246        {
    247247            var timelineRecordBar = this._timelineRecordBars[recordBarIndex];
    248248            if (!timelineRecordBar)
    249249                timelineRecordBar = this._timelineRecordBars[recordBarIndex] = new WebInspector.TimelineRecordBar;
    250             timelineRecordBar.records = barRecords;
     250            timelineRecordBar.renderMode = renderMode;
     251            timelineRecordBar.records = records;
    251252            timelineRecordBar.refresh(this._graphDataSource);
    252253            if (!timelineRecordBar.element.parentNode)
     
    255256        }
    256257
    257         function createBarsForRecords(records)
    258         {
    259             var barRecords = [];
    260 
    261             for (var record of records) {
    262                 if (isNaN(record.startTime))
    263                     continue;
    264 
    265                 // If this bar is completely before the bounds of the graph, skip this record.
    266                 if (record.endTime < startTime)
    267                     continue;
    268 
    269                 // If this record is completely after the current time or end time, break out now.
    270                 // Records are sorted, so all records after this will be beyond the current or end time too.
    271                 if (record.startTime > currentTime || record.startTime > endTime)
    272                     break;
    273 
    274                 // Check if the previous record can be combined with the current record, if not make a new bar.
    275                 if (barRecords.length && WebInspector.TimelineRecordBar.recordsCannotBeCombined(barRecords, record, secondsPerPixel)) {
    276                     createBar.call(this, barRecords);
    277                     barRecords = [];
    278                 }
    279 
    280                 barRecords.push(record);
    281             }
    282 
    283             // Create the bar for the last record if needed.
    284             if (barRecords.length)
    285                 createBar.call(this, barRecords);
    286         }
     258        var boundCreateBar = createBar.bind(this);
    287259
    288260        if (this.expanded) {
    289261            // When expanded just use the records for this node.
    290             createBarsForRecords.call(this, this.records);
     262            WebInspector.TimelineRecordBar.createCombinedBars(this.records, secondsPerPixel, this._graphDataSource, boundCreateBar);
    291263        } else {
    292264            // When collapsed use the records for this node and its descendants.
     
    318290
    319291            for (var records of recordTypeMap.values())
    320                 createBarsForRecords.call(this, records);
     292                WebInspector.TimelineRecordBar.createCombinedBars(records, secondsPerPixel, this._graphDataSource, boundCreateBar);
    321293        }
    322294
  • trunk/Source/WebInspectorUI/UserInterface/TimelineRecordBar.css

    r162560 r162681  
    5353    border-bottom-right-radius: 0 !important;
    5454    border-right: none;
    55     margin-right: -1px;
    5655}
    5756
    58 .timeline-record-bar > .segment.inactive + .segment {
     57.timeline-record-bar.has-inactive-segment > .segment:not(.inactive) {
    5958    border-top-left-radius: 0 !important;
    6059    border-bottom-left-radius: 0 !important;
     
    6766
    6867:focus .selected .timeline-record-bar > .segment.inactive {
    69     opacity: 0.7;
     68    background-color: rgb(196, 215, 242) !important;
    7069}
    7170
  • trunk/Source/WebInspectorUI/UserInterface/TimelineRecordBar.js

    r162560 r162681  
    2424 */
    2525
    26 WebInspector.TimelineRecordBar = function(records)
     26WebInspector.TimelineRecordBar = function(records, renderMode)
    2727{
    2828    WebInspector.Object.call(this);
     
    3131    this._element.classList.add(WebInspector.TimelineRecordBar.StyleClassName);
    3232
    33     this._activeBarElement = document.createElement("div");
    34     this._activeBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
    35     this._element.appendChild(this._activeBarElement);
    36 
     33    this.renderMode = renderMode;
    3734    this.records = records;
    3835};
     
    4845WebInspector.TimelineRecordBar.MinimumMarginPixels = 1;
    4946
    50 WebInspector.TimelineRecordBar.recordsCannotBeCombined = function(records, candidateRecord, secondsPerPixel)
     47WebInspector.TimelineRecordBar.RenderMode = {
     48    Normal: "timeline-record-bar-normal-render-mode",
     49    InactiveOnly: "timeline-record-bar-inactive-only-render-mode",
     50    ActiveOnly: "timeline-record-bar-active-only-render-mode"
     51};
     52
     53WebInspector.TimelineRecordBar.createCombinedBars = function(records, secondsPerPixel, graphDataSource, createBarCallback)
    5154{
    52     console.assert(records instanceof Array || records instanceof WebInspector.TimelineRecord);
    53     console.assert(candidateRecord instanceof WebInspector.TimelineRecord);
    54 
    55     if (records instanceof WebInspector.TimelineRecord)
    56         records = [records];
    57 
    5855    if (!records.length)
    59         return true;
    60 
    61     if (candidateRecord.usesActiveStartTime)
    62         return true;
    63 
    64     var lastRecord = records.lastValue;
    65     if (lastRecord.usesActiveStartTime)
    66         return true;
    67 
    68     if (lastRecord.type !== candidateRecord.type)
    69         return true;
    70 
    71     if (isNaN(candidateRecord.startTime))
    72         return true;
    73 
    74     var firstRecord = records[0];
    75     var totalDuration = lastRecord.endTime - firstRecord.startTime;
    76 
    77     var minimumMargin = WebInspector.TimelineRecordBar.MinimumMarginPixels * secondsPerPixel;
    78     var minimumDuration = WebInspector.TimelineRecordBar.MinimumWidthPixels * secondsPerPixel;
    79 
    80     return firstRecord.startTime + Math.max(minimumDuration, totalDuration) + minimumMargin <= candidateRecord.startTime;
     56        return;
     57
     58    var startTime = graphDataSource.startTime;
     59    var currentTime = graphDataSource.currentTime;
     60    var endTime = graphDataSource.endTime;
     61
     62    var visibleRecords = [];
     63    var usesActiveStartTime = false;
     64    var lastRecordType = null;
     65
     66    // FIXME: Do a binary search for records that fall inside start and current time.
     67
     68    for (var record of records) {
     69        if (isNaN(record.startTime))
     70            continue;
     71
     72        // If this bar is completely before the bounds of the graph, skip this record.
     73        if (record.endTime < startTime)
     74            continue;
     75
     76        // If this record is completely after the current time or end time, break out now.
     77        // Records are sorted, so all records after this will be beyond the current or end time too.
     78        if (record.startTime > currentTime || record.startTime > endTime)
     79            break;
     80
     81        if (record.usesActiveStartTime)
     82            usesActiveStartTime = true;
     83
     84        // If one record uses active time the rest are assumed to use it.
     85        console.assert(record.usesActiveStartTime === usesActiveStartTime);
     86
     87        // Only a single record type is supported right now.
     88        console.assert(!lastRecordType || record.type === lastRecordType);
     89
     90        visibleRecords.push(record);
     91
     92        lastRecordType = record.type;
     93    }
     94
     95    if (!visibleRecords.length)
     96        return;
     97
     98    if (visibleRecords.length === 1) {
     99        createBarCallback(visibleRecords, WebInspector.TimelineRecordBar.RenderMode.Normal);
     100        return;
     101    }
     102
     103    function compareByActiveStartTime(a, b)
     104    {
     105        return a.activeStartTime - b.activeStartTime;
     106    }
     107
     108    var minimumDuration = secondsPerPixel * WebInspector.TimelineRecordBar.MinimumWidthPixels;
     109    var minimumMargin = secondsPerPixel * WebInspector.TimelineRecordBar.MinimumMarginPixels;
     110
     111    if (usesActiveStartTime) {
     112        var inactiveStartTime = NaN;
     113        var inactiveEndTime = NaN;
     114        var inactiveRecords = [];
     115
     116        for (var record of visibleRecords) {
     117            // Check if the previous record is far enough away to create the inactive bar.
     118            if (!isNaN(inactiveStartTime) && inactiveStartTime + Math.max(inactiveEndTime - inactiveStartTime, minimumDuration) + minimumMargin <= record.startTime) {
     119                createBarCallback(inactiveRecords, WebInspector.TimelineRecordBar.RenderMode.InactiveOnly);
     120                inactiveRecords = [];
     121                inactiveStartTime = NaN;
     122                inactiveEndTime = NaN;
     123            }
     124
     125            // If this is a new bar, peg the start time.
     126            if (isNaN(inactiveStartTime))
     127                inactiveStartTime = record.startTime;
     128
     129            // Update the end time to be the maximum we encounter. inactiveEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
     130            inactiveEndTime = Math.max(inactiveEndTime || 0, record.activeStartTime);
     131
     132            inactiveRecords.push(record);
     133        }
     134
     135        // Create the inactive bar for the last record if needed.
     136        if (!isNaN(inactiveStartTime))
     137            createBarCallback(inactiveRecords, WebInspector.TimelineRecordBar.RenderMode.InactiveOnly);
     138
     139        visibleRecords.sort(compareByActiveStartTime);
     140    }
     141
     142    lastRecordType = null;
     143
     144    var activeStartTime = NaN;
     145    var activeEndTime = NaN;
     146    var activeRecords = [];
     147
     148    var startTimeProperty = usesActiveStartTime ? "activeStartTime" : "startTime";
     149
     150    for (var record of visibleRecords) {
     151        // Check if the previous record is far enough away to create the active bar. We also create it now if the current record has no active state time.
     152        if (!isNaN(activeStartTime) && (activeStartTime + Math.max(activeEndTime - activeStartTime, minimumDuration) + minimumMargin <= record[startTimeProperty]
     153            || (isNaN(record[startTimeProperty]) && !isNaN(activeEndTime)))) {
     154            createBarCallback(activeRecords, WebInspector.TimelineRecordBar.RenderMode.ActiveOnly);
     155            activeRecords = [];
     156            activeStartTime = NaN;
     157            activeEndTime = NaN;
     158        }
     159
     160        if (isNaN(record[startTimeProperty]))
     161            continue;
     162
     163        // If this is a new bar, peg the start time.
     164        if (isNaN(activeStartTime))
     165            activeStartTime = record[startTimeProperty];
     166
     167        // Update the end time to be the maximum we encounter. activeEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
     168        if (!isNaN(record.endTime))
     169            activeEndTime = Math.max(activeEndTime || 0, record.endTime);
     170
     171        activeRecords.push(record);
     172    }
     173
     174    // Create the active bar for the last record if needed.
     175    if (!isNaN(activeStartTime))
     176        createBarCallback(activeRecords, WebInspector.TimelineRecordBar.RenderMode.ActiveOnly);
    81177};
    82178
     
    92188    },
    93189
     190    get renderMode()
     191    {
     192        return this._renderMode;
     193    },
     194
     195    set renderMode(renderMode)
     196    {
     197        this._renderMode = renderMode || WebInspector.TimelineRecordBar.RenderMode.Normal;
     198    },
     199
    94200    get records()
    95201    {
     
    109215        this._records = records;
    110216
    111         // Combining multiple record bars is not supported with records that have inactive time.
    112         console.assert(this._records.length <= 1 || !this._records[0].usesActiveStartTime);
    113 
    114         // Inactive time is only supported with one record.
    115         if (this._records.length === 1 && this._records[0].usesActiveStartTime) {
     217        // Assume all records are the same type.
     218        if (this._records.length)
     219            this._element.classList.add(this._records[0].type);
     220    },
     221
     222    refresh: function(graphDataSource)
     223    {
     224        if (!this._records || !this._records.length)
     225            return false;
     226
     227        var firstRecord = this._records[0];
     228        var barStartTime = firstRecord.startTime;
     229
     230        // If this bar has no time info, return early.
     231        if (isNaN(barStartTime))
     232            return false;
     233
     234        var graphStartTime = graphDataSource.startTime;
     235        var graphEndTime = graphDataSource.endTime;
     236        var graphCurrentTime = graphDataSource.currentTime;
     237
     238        var barEndTime = this._records.reduce(function(previousValue, currentValue) { return Math.max(previousValue, currentValue.endTime); }, 0);
     239
     240        // If this bar is completely after the current time, return early.
     241        if (barStartTime > graphCurrentTime)
     242            return false;
     243
     244        // If this bar is completely before or after the bounds of the graph, return early.
     245        if (barEndTime < graphStartTime || barStartTime > graphEndTime)
     246            return false;
     247
     248        var barUnfinished = isNaN(barEndTime) || barEndTime >= graphCurrentTime;
     249        if (barUnfinished)
     250            barEndTime = graphCurrentTime;
     251
     252        var graphDuration = graphEndTime - graphStartTime;
     253
     254        this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, barUnfinished);
     255
     256        var newBarLeftPosition = (barStartTime - graphStartTime) / graphDuration;
     257        this._updateElementPosition(this._element, newBarLeftPosition, "left");
     258
     259        var newBarWidth = ((barEndTime - graphStartTime) / graphDuration) - newBarLeftPosition;
     260        this._updateElementPosition(this._element, newBarWidth, "width");
     261
     262        if (!this._activeBarElement && this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
     263            this._activeBarElement = document.createElement("div");
     264            this._activeBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
     265        }
     266
     267        if (!firstRecord.usesActiveStartTime) {
     268            this._element.classList.remove(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
     269
     270            if (this._inactiveBarElement)
     271                this._inactiveBarElement.remove();
     272
     273            if (this._renderMode === WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
     274                if (this._activeBarElement)
     275                    this._activeBarElement.remove();
     276
     277                return false;
     278            }
     279
     280            // If this TimelineRecordBar is reused and had an inactive bar previously, clean it up.
     281            this._activeBarElement.style.removeProperty("left");
     282            this._activeBarElement.style.removeProperty("width");
     283
     284            if (!this._activeBarElement.parentNode)
     285                this._element.appendChild(this._activeBarElement);
     286
     287            return true;
     288        }
     289
     290        this._element.classList.add(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
     291
     292        // Find the earliest active start time for active only rendering, and the latest for the other modes.
     293        // This matches the values that TimelineRecordBar.createCombinedBars uses when combining.
     294        if (this._renderMode === WebInspector.TimelineRecordBar.RenderMode.ActiveOnly)
     295            var barActiveStartTime = this._records.reduce(function(previousValue, currentValue) { return Math.min(previousValue, currentValue.activeStartTime); }, Infinity);
     296        else
     297            var barActiveStartTime = this._records.reduce(function(previousValue, currentValue) { return Math.max(previousValue, currentValue.activeStartTime); }, 0);
     298
     299        var barDuration = barEndTime - barStartTime;
     300
     301        var inactiveUnfinished = isNaN(barActiveStartTime) || barActiveStartTime >= graphCurrentTime;
     302        this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, inactiveUnfinished);
     303
     304        if (inactiveUnfinished)
     305            barActiveStartTime = graphCurrentTime;
     306
     307        var middlePercentage = (barActiveStartTime - barStartTime) / barDuration;
     308
     309        if (this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.ActiveOnly) {
    116310            if (!this._inactiveBarElement) {
    117311                this._inactiveBarElement = document.createElement("div");
    118312                this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
    119313                this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.InactiveStyleClassName);
    120                 this._element.classList.add(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
     314            }
     315
     316            this._updateElementPosition(this._inactiveBarElement, 1 - middlePercentage, "right");
     317            this._updateElementPosition(this._inactiveBarElement, middlePercentage, "width");
     318
     319            if (!this._inactiveBarElement.parentNode)
    121320                this._element.insertBefore(this._inactiveBarElement, this._element.firstChild);
    122             }
    123         } else if (this._inactiveBarElement) {
    124             this._element.classList.remove(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
    125             this._inactiveBarElement.remove();
    126             delete this._inactiveBarElement;
    127         }
    128 
    129         // Assume all records are the same type.
    130         if (this._records.length)
    131             this._element.classList.add(this._records[0].type);
    132     },
    133 
    134     refresh: function(graphDataSource)
    135     {
    136         if (!this._records || !this._records.length)
    137             return;
    138 
    139         var firstRecord = this._records[0];
    140         var barStartTime = firstRecord.startTime;
    141 
    142         // If this bar has no time info, return early.
    143         if (isNaN(barStartTime))
    144             return false;
    145 
    146         var graphStartTime = graphDataSource.startTime;
    147         var graphEndTime = graphDataSource.endTime;
    148         var graphCurrentTime = graphDataSource.currentTime;
    149 
    150         var lastRecord = this._records.lastValue;
    151         var barEndTime = lastRecord.endTime;
    152 
    153         // If this bar is completely after the current time, return early.
    154         if (barStartTime > graphCurrentTime)
    155             return false;
    156 
    157         // If this bar is completely before or after the bounds of the graph, return early.
    158         if (barEndTime < graphStartTime || barStartTime > graphEndTime)
    159             return false;
    160 
    161         var barUnfinished = isNaN(barEndTime) || barEndTime >= graphCurrentTime;
    162         if (barUnfinished)
    163             barEndTime = graphCurrentTime;
    164 
    165         var graphDuration = graphEndTime - graphStartTime;
    166 
    167         this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, barUnfinished);
    168 
    169         var newBarLeftPosition = (barStartTime - graphStartTime) / graphDuration;
    170         this._updateElementPosition(this._element, newBarLeftPosition, "left");
    171 
    172         var newBarWidth = ((barEndTime - graphStartTime) / graphDuration) - newBarLeftPosition;
    173         this._updateElementPosition(this._element, newBarWidth, "width");
    174 
    175         if (!this._inactiveBarElement) {
    176             // If this TimelineRecordBar is reused and had an inactive bar previously,
    177             // we might need to remove some styles and add the active element back.
    178             this._activeBarElement.style.removeProperty("left");
    179             this._activeBarElement.style.removeProperty("width");
     321        }
     322
     323        if (!inactiveUnfinished && this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
     324            this._updateElementPosition(this._activeBarElement, middlePercentage, "left");
     325            this._updateElementPosition(this._activeBarElement, 1 - middlePercentage, "width");
     326
    180327            if (!this._activeBarElement.parentNode)
    181328                this._element.appendChild(this._activeBarElement);
    182             return true;
    183         }
    184 
    185         console.assert(firstRecord === lastRecord);
    186 
    187         var barActiveStartTime = Math.max(barStartTime, Math.min(firstRecord.activeStartTime, barEndTime));
    188         var barDuration = barEndTime - barStartTime;
    189 
    190         var inactiveUnfinished = isNaN(barActiveStartTime) || barActiveStartTime >= graphCurrentTime;
    191         this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, inactiveUnfinished);
    192 
    193         if (inactiveUnfinished)
    194             barActiveStartTime = graphCurrentTime;
    195 
    196         var middlePercentage = (barActiveStartTime - barStartTime) / barDuration;
    197 
    198         this._updateElementPosition(this._inactiveBarElement, 1 - middlePercentage, "right");
    199         this._updateElementPosition(this._inactiveBarElement, middlePercentage, "width");
    200 
    201         if (!inactiveUnfinished) {
    202             if (!this._activeBarElement.parentNode)
    203                 this._element.appendChild(this._activeBarElement);
    204 
    205             this._updateElementPosition(this._activeBarElement, middlePercentage, "left");
    206             this._updateElementPosition(this._activeBarElement, 1 - middlePercentage, "width");
    207         } else
     329        } else if (this._activeBarElement)
    208330            this._activeBarElement.remove();
    209331
Note: See TracChangeset for help on using the changeset viewer.