Changeset 242739 in webkit


Ignore:
Timestamp:
Mar 11, 2019 2:26:47 PM (5 years ago)
Author:
Joseph Pecoraro
Message:

Web Inspector: CPU Usage Timeline - Add legend and graph hover effects
https://bugs.webkit.org/show_bug.cgi?id=195390

Reviewed by Devin Rousso.

  • Localizations/en.lproj/localizedStrings.js:

New strings for the legends.

  • UserInterface/Main.html:

Combined files.

  • UserInterface/Views/Variables.css:

(:root):
(@media (prefers-color-scheme: dark)):
Tweaked colors, including individual stroke and fill colors for each CPU section.

  • UserInterface/Views/CPUTimelineOverviewGraph.css:

(.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.total-usage):
(.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage):
(.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage):
Updated colors.

  • UserInterface/Views/CPUUsageCombinedView.css: Renamed from Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css.

(.cpu-usage-combined-view > .details > .legend-container):
(.cpu-usage-combined-view > .details > .legend-container > .row):
(.cpu-usage-combined-view > .details > .legend-container > .row + .row):
(.cpu-usage-combined-view > .details > .legend-container > .row > .swatch):

  • UserInterface/Views/CPUUsageCombinedView.js: Renamed from Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js.

(WI.CPUUsageCombinedView.appendLegendRow):
(WI.CPUUsageCombinedView):
(WI.CPUUsageCombinedView.prototype.get graphElement):
(WI.CPUUsageCombinedView.prototype.get chart):
(WI.CPUUsageCombinedView.prototype.get rangeChart):
(WI.CPUUsageCombinedView.prototype.clear):
(WI.CPUUsageCombinedView.prototype.updateChart):
(WI.CPUUsageCombinedView.prototype.updateMainThreadIndicator):
(WI.CPUUsageCombinedView.prototype.clearLegend):
(WI.CPUUsageCombinedView.prototype.updateLegend):
(WI.CPUUsageCombinedView.prototype._updateDetails):

  • UserInterface/Views/CPUUsageIndicatorView.css: Removed.
  • UserInterface/Views/CPUUsageIndicatorView.js: Removed.

Combined the Indicator and StackedAreaChart into a single view
that share a left details section.

  • UserInterface/Views/CPUUsageView.js:

(WI.CPUUsageView):
(WI.CPUUsageView.prototype.get graphElement):
(WI.CPUUsageView.prototype.clear):
(WI.CPUUsageView.prototype.updateChart):
(WI.CPUUsageView.prototype.clearLegend):
(WI.CPUUsageView.prototype.updateLegend):
(WI.CPUUsageView.prototype._updateDetails):
Include a legend in the left details section.

  • UserInterface/Views/AreaChart.js:

(WI.AreaChart):
(WI.AreaChart.prototype.addPointMarker):
(WI.AreaChart.prototype.clearPointMarkers):
(WI.AreaChart.prototype.clear):
(WI.AreaChart.prototype.layout):

  • UserInterface/Views/StackedAreaChart.js:

(WI.StackedAreaChart):
(WI.StackedAreaChart.prototype.addPointMarker):
(WI.StackedAreaChart.prototype.clearPointMarkers):
(WI.StackedAreaChart.prototype.clear):
(WI.StackedAreaChart.prototype.layout):
Add point markers for the area charts.

  • UserInterface/Views/CPUTimelineView.css:
  • UserInterface/Views/CPUTimelineView.js:

(WI.CPUTimelineView):
(WI.CPUTimelineView.prototype.get cpuUsageViewHeight):
(WI.CPUTimelineView.prototype.clear):
(WI.CPUTimelineView.prototype.initialLayout.appendLegendRow):
(WI.CPUTimelineView.prototype.initialLayout):
(WI.CPUTimelineView.prototype.layout):
(WI.CPUTimelineView.prototype._graphPositionForMouseEvent):
(WI.CPUTimelineView.prototype._handleMouseClick):
(WI.CPUTimelineView.prototype._handleGraphMouseMove):
(WI.CPUTimelineView.prototype._showGraphOverlayNearTo):
(WI.CPUTimelineView.prototype._updateGraphOverlay):
(WI.CPUTimelineView.prototype._showGraphOverlay.xScale):
(WI.CPUTimelineView.prototype._showGraphOverlay.yScale):
(WI.CPUTimelineView.prototype._showGraphOverlay.addOverlayPoint):
(WI.CPUTimelineView.prototype._showGraphOverlay):
(WI.CPUTimelineView.prototype._clearOverlayMarkers.clearGraphOverlayElement):
(WI.CPUTimelineView.prototype._clearOverlayMarkers):
(WI.CPUTimelineView.prototype._hideGraphOverlay):
Include graph overlay markers.

Location:
trunk/Source/WebInspectorUI
Files:
1 added
3 deleted
10 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r242737 r242739  
     12019-03-11  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: CPU Usage Timeline - Add legend and graph hover effects
     4        https://bugs.webkit.org/show_bug.cgi?id=195390
     5
     6        Reviewed by Devin Rousso.
     7
     8        * Localizations/en.lproj/localizedStrings.js:
     9        New strings for the legends.
     10
     11        * UserInterface/Main.html:
     12        Combined files.
     13
     14        * UserInterface/Views/Variables.css:
     15        (:root):
     16        (@media (prefers-color-scheme: dark)):
     17        Tweaked colors, including individual stroke and fill colors for each CPU section.
     18
     19        * UserInterface/Views/CPUTimelineOverviewGraph.css:
     20        (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.total-usage):
     21        (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage):
     22        (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage):
     23        Updated colors.
     24
     25        * UserInterface/Views/CPUUsageCombinedView.css: Renamed from Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css.
     26        (.cpu-usage-combined-view > .details > .legend-container):
     27        (.cpu-usage-combined-view > .details > .legend-container > .row):
     28        (.cpu-usage-combined-view > .details > .legend-container > .row + .row):
     29        (.cpu-usage-combined-view > .details > .legend-container > .row > .swatch):
     30        * UserInterface/Views/CPUUsageCombinedView.js: Renamed from Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js.
     31        (WI.CPUUsageCombinedView.appendLegendRow):
     32        (WI.CPUUsageCombinedView):
     33        (WI.CPUUsageCombinedView.prototype.get graphElement):
     34        (WI.CPUUsageCombinedView.prototype.get chart):
     35        (WI.CPUUsageCombinedView.prototype.get rangeChart):
     36        (WI.CPUUsageCombinedView.prototype.clear):
     37        (WI.CPUUsageCombinedView.prototype.updateChart):
     38        (WI.CPUUsageCombinedView.prototype.updateMainThreadIndicator):
     39        (WI.CPUUsageCombinedView.prototype.clearLegend):
     40        (WI.CPUUsageCombinedView.prototype.updateLegend):
     41        (WI.CPUUsageCombinedView.prototype._updateDetails):
     42        * UserInterface/Views/CPUUsageIndicatorView.css: Removed.
     43        * UserInterface/Views/CPUUsageIndicatorView.js: Removed.
     44        Combined the Indicator and StackedAreaChart into a single view
     45        that share a left details section.
     46
     47        * UserInterface/Views/CPUUsageView.js:
     48        (WI.CPUUsageView):
     49        (WI.CPUUsageView.prototype.get graphElement):
     50        (WI.CPUUsageView.prototype.clear):
     51        (WI.CPUUsageView.prototype.updateChart):
     52        (WI.CPUUsageView.prototype.clearLegend):
     53        (WI.CPUUsageView.prototype.updateLegend):
     54        (WI.CPUUsageView.prototype._updateDetails):
     55        Include a legend in the left details section.
     56
     57        * UserInterface/Views/AreaChart.js:
     58        (WI.AreaChart):
     59        (WI.AreaChart.prototype.addPointMarker):
     60        (WI.AreaChart.prototype.clearPointMarkers):
     61        (WI.AreaChart.prototype.clear):
     62        (WI.AreaChart.prototype.layout):
     63        * UserInterface/Views/StackedAreaChart.js:
     64        (WI.StackedAreaChart):
     65        (WI.StackedAreaChart.prototype.addPointMarker):
     66        (WI.StackedAreaChart.prototype.clearPointMarkers):
     67        (WI.StackedAreaChart.prototype.clear):
     68        (WI.StackedAreaChart.prototype.layout):
     69        Add point markers for the area charts.
     70
     71        * UserInterface/Views/CPUTimelineView.css:
     72        * UserInterface/Views/CPUTimelineView.js:
     73        (WI.CPUTimelineView):
     74        (WI.CPUTimelineView.prototype.get cpuUsageViewHeight):
     75        (WI.CPUTimelineView.prototype.clear):
     76        (WI.CPUTimelineView.prototype.initialLayout.appendLegendRow):
     77        (WI.CPUTimelineView.prototype.initialLayout):
     78        (WI.CPUTimelineView.prototype.layout):
     79        (WI.CPUTimelineView.prototype._graphPositionForMouseEvent):
     80        (WI.CPUTimelineView.prototype._handleMouseClick):
     81        (WI.CPUTimelineView.prototype._handleGraphMouseMove):
     82        (WI.CPUTimelineView.prototype._showGraphOverlayNearTo):
     83        (WI.CPUTimelineView.prototype._updateGraphOverlay):
     84        (WI.CPUTimelineView.prototype._showGraphOverlay.xScale):
     85        (WI.CPUTimelineView.prototype._showGraphOverlay.yScale):
     86        (WI.CPUTimelineView.prototype._showGraphOverlay.addOverlayPoint):
     87        (WI.CPUTimelineView.prototype._showGraphOverlay):
     88        (WI.CPUTimelineView.prototype._clearOverlayMarkers.clearGraphOverlayElement):
     89        (WI.CPUTimelineView.prototype._clearOverlayMarkers):
     90        (WI.CPUTimelineView.prototype._hideGraphOverlay):
     91        Include graph overlay markers.
     92
    1932019-03-11  Devin Rousso  <drousso@apple.com>
    294
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r242622 r242739  
    617617localizedStrings["MSE Logging:"] = "MSE Logging:";
    618618localizedStrings["Main Thread"] = "Main Thread";
     619localizedStrings["Main: %s"] = "Main: %s";
    619620localizedStrings["Manifest URL"] = "Manifest URL";
    620621localizedStrings["Mass"] = "Mass";
     
    711712localizedStrings["Other Issue"] = "Other Issue";
    712713localizedStrings["Other Threads"] = "Other Threads";
     714localizedStrings["Other: %s"] = "Other: %s";
    713715localizedStrings["Other\u2026"] = "Other\u2026";
    714716localizedStrings["Outgoing message"] = "Outgoing message";
     
    10571059localizedStrings["Total memory size at the end of the selected time range"] = "Total memory size at the end of the selected time range";
    10581060localizedStrings["Total time"] = "Total time";
     1061localizedStrings["Total: %s"] = "Total: %s";
    10591062localizedStrings["Totals:"] = "Totals:";
    10601063localizedStrings["Trace"] = "Trace";
     
    10831086localizedStrings["Unsupported property value"] = "Unsupported property value";
    10841087localizedStrings["Untitled"] = "Untitled";
     1088localizedStrings["Usage: %s"] = "Usage: %s";
    10851089localizedStrings["Use Default Appearance"] = "Use Default Appearance";
    10861090localizedStrings["Use Default Media Styles"] = "Use Default Media Styles";
     
    11221126localizedStrings["Worker"] = "Worker";
    11231127localizedStrings["Worker Thread"] = "Worker Thread";
     1128localizedStrings["Worker Threads"] = "Worker Threads";
    11241129localizedStrings["Worker \u2014 %s"] = "Worker \u2014 %s";
     1130localizedStrings["Worker: %s"] = "Worker: %s";
    11251131localizedStrings["Working Copy"] = "Working Copy";
    11261132localizedStrings["Wrap lines to editor width"] = "Wrap lines to editor width";
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r242300 r242739  
    4646    <link rel="stylesheet" href="Views/CPUTimelineOverviewGraph.css">
    4747    <link rel="stylesheet" href="Views/CPUTimelineView.css">
    48     <link rel="stylesheet" href="Views/CPUUsageIndicatorView.css">
    49     <link rel="stylesheet" href="Views/CPUUsageStackedView.css">
     48    <link rel="stylesheet" href="Views/CPUUsageCombinedView.css">
    5049    <link rel="stylesheet" href="Views/CPUUsageView.css">
    5150    <link rel="stylesheet" href="Views/CallFrameIcons.css">
     
    597596    <script src="Views/CPUTimelineOverviewGraph.js"></script>
    598597    <script src="Views/CPUTimelineView.js"></script>
    599     <script src="Views/CPUUsageIndicatorView.js"></script>
    600     <script src="Views/CPUUsageStackedView.js"></script>
     598    <script src="Views/CPUUsageCombinedView.js"></script>
    601599    <script src="Views/CPUUsageView.js"></script>
    602600    <script src="Views/CSSStyleSheetTreeElement.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/AreaChart.js

    r242077 r242739  
    2727//
    2828// Initialize the chart with a size. You can then include a new point
    29 // in the area chart by providing an (x, y) point via `addPoint`.
     29// in the area chart by providing an (x, y) point via `addPoint`. You
     30// can add point markers (<circle>) with an (x, y) as well.
    3031//
    3132// SVG:
     
    5152
    5253        this._pathElement = this._chartElement.appendChild(createSVGElement("path"));
     54        this._circleElements = [];
    5355
    5456        this._points = [];
     57        this._markers = [];
    5558        this._size = null;
    5659    }
     
    8083    }
    8184
     85    clearPoints()
     86    {
     87        this._points = [];
     88    }
     89
     90    addPointMarker(x, y)
     91    {
     92        this._markers.push({x, y});
     93    }
     94
     95    clearPointMarkers()
     96    {
     97        this._markers = [];
     98    }
     99
    82100    clear()
    83101    {
    84         this._points = [];
     102        this.clearPoints();
     103        this.clearPointMarkers();
    85104    }
    86105
     
    108127        let pathString = pathComponents.join(" ");
    109128        this._pathElement.setAttribute("d", pathString);
     129
     130        if (this._circleElements.length) {
     131            for (let circle of this._circleElements)
     132                circle.remove();
     133            this._circleElements = [];
     134        }
     135
     136        if (this._markers.length) {
     137            for (let {x, y} of this._markers) {
     138                let circle = this._chartElement.appendChild(createSVGElement("circle"));
     139                this._circleElements.push(circle);
     140                circle.setAttribute("cx", x);
     141                circle.setAttribute("cy", y);
     142            }
     143        }
    110144    }
    111145};
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.css

    r242567 r242739  
    6262}
    6363
    64 .timeline-overview-graph.cpu > .stacked-column-chart > svg > rect {
    65     stroke: var(--cpu-stroke-color);
    66     fill: var(--cpu-fill-color);
     64.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.total-usage {
     65    fill: var(--cpu-other-thread-fill-color);
     66    stroke: var(--cpu-other-thread-stroke-color);
    6767}
    6868
    6969.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage {
    7070    fill: var(--cpu-main-thread-fill-color);
     71    stroke: var(--cpu-main-thread-stroke-color);
    7172}
    7273
    7374.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage {
    7475    fill: var(--cpu-worker-thread-fill-color);
     76    stroke: var(--cpu-worker-thread-stroke-color);
    7577}
    7678
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css

    r242562 r242739  
    127127}
    128128
    129 .timeline-view.cpu .legend {
     129.timeline-view.cpu > .content > .overview .legend {
    130130    -webkit-padding-start: 20px;
    131131    text-align: start;
    132132}
    133133
    134 .timeline-view.cpu .legend .row {
     134.timeline-view.cpu > .content > .overview .legend .row {
    135135    display: flex;
    136136}
    137137
    138 .timeline-view.cpu .legend .row + .row {
     138.timeline-view.cpu > .content > .overview .legend .row + .row {
    139139    margin-top: 4px;
    140140}
    141141
    142 .timeline-view.cpu .legend .swatch {
     142.timeline-view.cpu > .content > .overview .legend .swatch {
    143143    width: 1em;
    144144    height: 1em;
     
    147147}
    148148
    149 .timeline-view.cpu .legend > .row > .swatch.sample-type-idle {
    150     border: 1px solid var(--cpu-idle-stroke-color);
    151     background-color: var(--cpu-idle-fill-color);
    152 }
    153 
    154 .timeline-view.cpu .legend > .row > .swatch.sample-type-script {
     149.timeline-view.cpu > .content > .overview .legend > .row > .swatch.sample-type-script {
    155150    border: 1px solid var(--cpu-script-stroke-color);
    156151    background-color: var(--cpu-script-fill-color);
    157152}
    158153
    159 .timeline-view.cpu .legend > .row > .swatch.sample-type-style {
     154.timeline-view.cpu > .content > .overview .legend > .row > .swatch.sample-type-style {
    160155    border: 1px solid var(--cpu-style-stroke-color);
    161156    background-color: var(--cpu-style-fill-color);
    162157}
    163158
    164 .timeline-view.cpu .legend > .row > .swatch.sample-type-layout {
     159.timeline-view.cpu > .content > .overview .legend > .row > .swatch.sample-type-layout {
    165160    border: 1px solid var(--cpu-layout-stroke-color);
    166161    background-color: var(--cpu-layout-fill-color);
    167162}
    168163
    169 .timeline-view.cpu .legend > .row > .swatch.sample-type-paint {
     164.timeline-view.cpu > .content > .overview .legend > .row > .swatch.sample-type-paint {
    170165    border: 1px solid var(--cpu-paint-stroke-color);
    171166    background-color: var(--cpu-paint-fill-color);
     
    198193
    199194.timeline-view.cpu :matches(.area-chart, .stacked-area-chart) svg > path {
    200     stroke: var(--cpu-stroke-color);
    201     fill: var(--cpu-fill-color);
     195    fill: var(--cpu-other-thread-fill-color);
     196    stroke: var(--cpu-other-thread-stroke-color);
    202197}
    203198
     
    205200.timeline-view.cpu svg > path.main-thread-usage {
    206201    fill: var(--cpu-main-thread-fill-color);
     202    stroke: var(--cpu-main-thread-stroke-color);
    207203}
    208204
     
    210206.timeline-view.cpu svg > path.worker-thread-usage {
    211207    fill: var(--cpu-worker-thread-fill-color);
    212 }
    213 
    214 .timeline-view.cpu .cpu-usage-view.empty {
    215     display: none;
     208    stroke: var(--cpu-worker-thread-stroke-color);
    216209}
    217210
     
    248241}
    249242
    250 .timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart rect {
    251     stroke-opacity: 0.25;
    252 }
    253 
    254 .timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-script {
    255     stroke: var(--cpu-script-stroke-color);
    256     fill: var(--cpu-script-fill-color);
    257 }
    258 
    259 .timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-style {
    260     stroke: var(--cpu-style-stroke-color);
    261     fill: var(--cpu-style-fill-color);
    262 }
    263 
    264 .timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-layout {
    265     stroke: var(--cpu-layout-stroke-color);
    266     fill: var(--cpu-layout-fill-color);
    267 }
    268 
    269 .timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-paint {
    270     stroke: var(--cpu-paint-stroke-color);
    271     fill: var(--cpu-paint-fill-color);
     243.timeline-view.cpu :matches(.area-chart, .stacked-area-chart) circle {
     244    r: 3;
     245    fill: var(--cpu-overlay-color);
     246}
     247
     248.timeline-view.cpu .timeline-ruler > .markers > .marker.timestamp {
     249    color: var(--cpu-overlay-color);
     250    opacity: 0.8;
     251    pointer-events: none;
     252}
     253
     254.timeline-view.cpu .timeline-ruler > .markers > .marker.timestamp::after {
     255    display: none;
    272256}
    273257
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js

    r242562 r242739  
    3636        this.element.classList.add("cpu");
    3737
     38        this._sectionLimit = CPUTimelineView.defaultSectionLimit;
     39
    3840        this._statisticsData = null;
    39         this._sectionLimit = CPUTimelineView.defaultSectionLimit;
     41        this._secondsPerPixelInLayout = undefined;
     42        this._visibleRecordsInLayout = [];
     43        this._discontinuitiesInLayout = [];
     44
     45        this._stickingOverlay = false;
     46        this._overlayRecord = null;
     47        this._overlayTime = NaN;
    4048
    4149        timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this);
     
    5967    }
    6068
    61     static get cpuUsageViewHeight() { return 150; }
     69    static get cpuUsageViewHeight() { return 135; }
    6270    static get threadCPUUsageViewHeight() { return 65; }
    6371    static get indicatorViewHeight() { return 15; }
     
    125133        this._removeWorkerThreadViews();
    126134
    127         this._mainThreadWorkIndicatorView.clear();
     135        this._sectionLimit = CPUTimelineView.defaultSectionLimit;
    128136
    129137        this._statisticsData = null;
    130         this._sectionLimit = CPUTimelineView.defaultSectionLimit;
     138        this._secondsPerPixelInLayout = undefined;
     139        this._visibleRecordsInLayout = [];
     140        this._discontinuitiesInLayout = [];
     141
     142        this._stickingOverlay = false;
     143        this._hideGraphOverlay();
    131144    }
    132145
     
    142155    initialLayout()
    143156    {
    144         this.element.style.setProperty("--cpu-usage-stacked-view-height", CPUTimelineView.cpuUsageViewHeight + "px");
     157        this.element.style.setProperty("--cpu-usage-combined-view-height", CPUTimelineView.cpuUsageViewHeight + "px");
    145158        this.element.style.setProperty("--cpu-usage-view-height", CPUTimelineView.threadCPUUsageViewHeight + "px");
    146159        this.element.style.setProperty("--cpu-usage-indicator-view-height", CPUTimelineView.indicatorViewHeight + "px");
     
    175188
    176189            let valueContainer = rowElement.appendChild(document.createElement("div"));
    177             valueContainer.classList.add("value");
    178190
    179191            let labelElement = valueContainer.appendChild(document.createElement("div"));
     
    288300        detailsSubtitleElement.textContent = WI.UIString("CPU Usage");
    289301
    290         this._cpuUsageView = new WI.CPUUsageStackedView(WI.UIString("Total"));
     302        this._cpuUsageView = new WI.CPUUsageCombinedView(WI.UIString("Total"));
    291303        this.addSubview(this._cpuUsageView);
    292304        detailsContainerElement.appendChild(this._cpuUsageView.element);
    293305
    294         this._mainThreadWorkIndicatorView = new WI.CPUUsageIndicatorView;
    295         this.addSubview(this._mainThreadWorkIndicatorView);
    296         detailsContainerElement.appendChild(this._mainThreadWorkIndicatorView.element);
    297 
    298         this._mainThreadWorkIndicatorView.chart.element.addEventListener("click", this._handleIndicatorClick.bind(this));
     306        this._cpuUsageView.rangeChart.element.addEventListener("click", this._handleIndicatorClick.bind(this));
    299307
    300308        this._threadsDetailsElement = detailsContainerElement.appendChild(document.createElement("details"));
     
    316324
    317325        this._webkitThreadUsageView = new WI.CPUUsageView(WI.UIString("WebKit Threads"));
    318         this._webkitThreadUsageView.element.classList.add("non-main-thread");
    319326        this.addSubview(this._webkitThreadUsageView);
    320327        this._threadsDetailsElement.appendChild(this._webkitThreadUsageView.element);
    321328
    322329        this._unknownThreadUsageView = new WI.CPUUsageView(WI.UIString("Other Threads"));
    323         this._unknownThreadUsageView.element.classList.add("non-main-thread");
    324330        this.addSubview(this._unknownThreadUsageView);
    325331        this._threadsDetailsElement.appendChild(this._unknownThreadUsageView.element);
     
    399405        this._clearSources();
    400406
     407        this.element.addEventListener("click", this._handleGraphClick.bind(this));
    401408        this.element.addEventListener("mousemove", this._handleGraphMouseMove.bind(this));
     409
     410        this._overlayMarker = new WI.TimelineMarker(-1, WI.TimelineMarker.Type.TimeStamp);
     411        this._timelineRuler.addMarker(this._overlayMarker);
    402412    }
    403413
     
    434444            return;
    435445        }
     446
     447        this._secondsPerPixelInLayout = secondsPerPixel;
     448        this._visibleRecordsInLayout = visibleRecords;
     449        this._discontinuitiesInLayout = discontinuities.slice();
    436450
    437451        this._statisticsData = this._computeStatisticsData(graphStartTime, visibleEndTime);
     
    611625        // the maximum total CPU usage across all threads.
    612626        let layoutMax = max;
     627        this._layoutMax = max;
    613628
    614629        function layoutView(view, property, graphHeight, {dataPoints, min, max, average}) {
     
    685700                let workerView = new WI.CPUUsageView(displayName);
    686701                workerView.element.classList.add("worker-thread");
     702                workerView.__workerId = workerId;
    687703                this.addSubview(workerView);
    688704                this._threadsDetailsElement.insertBefore(workerView.element, this._webkitThreadUsageView.element);
     
    699715        let graphWidth = (graphEndTime - graphStartTime) / secondsPerPixel;
    700716        let size = new WI.Size(graphWidth, CPUTimelineView.indicatorViewHeight);
    701         this._mainThreadWorkIndicatorView.updateChart(this._statisticsData.samples, size, visibleEndTime, xScaleIndicatorRange);
     717        this._cpuUsageView.updateMainThreadIndicator(this._statisticsData.samples, size, visibleEndTime, xScaleIndicatorRange);
    702718
    703719        this._layoutEnergyChart(average, visibleDuration);
     720
     721        this._updateGraphOverlay();
    704722    }
    705723
     
    14891507            return NaN;
    14901508
    1491         let chartRect = chartElement.getBoundingClientRect();
    1492         let position = event.pageX - chartRect.left;
     1509        let rect = chartElement.getBoundingClientRect();
     1510        let position = event.pageX - rect.left;
    14931511
    14941512        if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL)
    1495             return chartRect.width - position;
     1513            return rect.width - position;
    14961514        return position;
    14971515    }
     
    15921610    }
    15931611
     1612    _handleGraphClick(event)
     1613    {
     1614        let mousePosition = this._graphPositionForMouseEvent(event);
     1615        if (isNaN(mousePosition))
     1616            return;
     1617
     1618        this._stickingOverlay = !this._stickingOverlay;
     1619
     1620        if (!this._stickingOverlay)
     1621            this._handleGraphMouseMove(event);
     1622    }
     1623
    15941624    _handleGraphMouseMove(event)
    15951625    {
    15961626        let mousePosition = this._graphPositionForMouseEvent(event);
    15971627        if (isNaN(mousePosition)) {
     1628            this._hideGraphOverlay();
    15981629            this.dispatchEventToListeners(WI.TimelineView.Event.ScannerHide);
    15991630            return;
     
    16031634        let time = this.startTime + (mousePosition * secondsPerPixel);
    16041635
     1636        if (!this._stickingOverlay)
     1637            this._showGraphOverlayNearTo(time);
     1638
    16051639        this.dispatchEventToListeners(WI.TimelineView.Event.ScannerShow, {time});
     1640    }
     1641
     1642    _showGraphOverlayNearTo(time)
     1643    {
     1644        let nearestRecord = null;
     1645        let nearestDistance = Infinity;
     1646
     1647        // Find the nearest record to the time.
     1648        for (let record of this._visibleRecordsInLayout) {
     1649            let distance = Math.abs(time - record.timestamp);
     1650            if (distance < nearestDistance) {
     1651                nearestRecord = record;
     1652                nearestDistance = distance;
     1653            }
     1654        }
     1655
     1656        if (!nearestRecord) {
     1657            this._hideGraphOverlay();
     1658            return;
     1659        }
     1660
     1661        let bestTime = nearestRecord.timestamp;
     1662
     1663        // Snap to a discontinuity if closer.
     1664        for (let {startTime, endTime} of this._discontinuitiesInLayout) {
     1665            let distance = Math.abs(time - startTime);
     1666            if (distance < nearestDistance) {
     1667                nearestDistance = distance;
     1668                bestTime = startTime;
     1669            }
     1670            distance = Math.abs(time - endTime);
     1671            if (distance < nearestDistance) {
     1672                nearestDistance = distance;
     1673                bestTime = endTime;
     1674            }
     1675        }
     1676
     1677        // Snap to end time if closer.
     1678        let visibleEndTime = Math.min(this.endTime, this.currentTime);
     1679        let distance = Math.abs(time - visibleEndTime);
     1680        if (distance < nearestDistance) {
     1681            nearestDistance = distance;
     1682            bestTime = visibleEndTime;
     1683        }
     1684
     1685        let graphStartTime = this.startTime;
     1686        let adjustedTime = Number.constrain(bestTime, graphStartTime, visibleEndTime);
     1687        this._showGraphOverlay(nearestRecord, adjustedTime);
     1688    }
     1689
     1690    _updateGraphOverlay()
     1691    {
     1692        if (!this._overlayRecord)
     1693            return;
     1694
     1695        this._showGraphOverlay(this._overlayRecord, this._overlayTime, true);
     1696    }
     1697
     1698    _showGraphOverlay(record, time, force)
     1699    {
     1700        if (!force && record === this._overlayRecord && time === this._overlayTime)
     1701            return;
     1702
     1703        this._overlayRecord = record;
     1704        this._overlayTime = time;
     1705
     1706        let layoutMax = this._layoutMax;
     1707        let secondsPerPixel = this._secondsPerPixelInLayout;
     1708        let graphMax = layoutMax * 1.05;
     1709        let graphStartTime = this.startTime;
     1710
     1711        this._overlayMarker.time = time + (secondsPerPixel / 2);
     1712
     1713        function xScale(time) {
     1714            return (time - graphStartTime) / secondsPerPixel;
     1715        }
     1716
     1717        let x = xScale(time);
     1718
     1719        let {mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage, workersData} = record;
     1720
     1721        function addOverlayPoint(view, graphHeight, value) {
     1722            if (!value)
     1723                return;
     1724
     1725            function yScale(value) {
     1726                return graphHeight - ((value / graphMax) * graphHeight);
     1727            }
     1728
     1729            view.chart.addPointMarker(x, yScale(value));
     1730            view.chart.needsLayout();
     1731        }
     1732
     1733        this._clearOverlayMarkers();
     1734
     1735        this._cpuUsageView.updateLegend(record);
     1736        addOverlayPoint(this._cpuUsageView, CPUTimelineView.cpuUsageViewHeight, mainThreadUsage);
     1737        addOverlayPoint(this._cpuUsageView, CPUTimelineView.cpuUsageViewHeight, mainThreadUsage + workerThreadUsage);
     1738        addOverlayPoint(this._cpuUsageView, CPUTimelineView.cpuUsageViewHeight, mainThreadUsage + workerThreadUsage + webkitThreadUsage + unknownThreadUsage);
     1739
     1740        if (this._threadsDetailsElement.open) {
     1741            this._mainThreadUsageView.updateLegend(mainThreadUsage);
     1742            addOverlayPoint(this._mainThreadUsageView, CPUTimelineView.threadCPUUsageViewHeight, mainThreadUsage);
     1743
     1744            this._webkitThreadUsageView.updateLegend(webkitThreadUsage);
     1745            addOverlayPoint(this._webkitThreadUsageView, CPUTimelineView.threadCPUUsageViewHeight, webkitThreadUsage);
     1746
     1747            this._unknownThreadUsageView.updateLegend(unknownThreadUsage);
     1748            addOverlayPoint(this._unknownThreadUsageView, CPUTimelineView.threadCPUUsageViewHeight, unknownThreadUsage);
     1749
     1750            for (let workerView of this._workerViews)
     1751                workerView.updateLegend(NaN);
     1752
     1753            if (workersData) {
     1754                for (let {targetId, usage} of workersData) {
     1755                    let workerView = this._workerViews.find((x) => x.__workerId === targetId);
     1756                    if (workerView) {
     1757                        workerView.updateLegend(usage);
     1758                        addOverlayPoint(workerView, CPUTimelineView.threadCPUUsageViewHeight, usage);
     1759                    }
     1760                }
     1761            }
     1762        }
     1763    }
     1764
     1765    _clearOverlayMarkers()
     1766    {
     1767        function clearGraphOverlayElement(view) {
     1768            view.clearLegend();
     1769            view.chart.clearPointMarkers();
     1770            view.chart.needsLayout();
     1771        }
     1772
     1773        clearGraphOverlayElement(this._cpuUsageView);
     1774        clearGraphOverlayElement(this._mainThreadUsageView);
     1775        clearGraphOverlayElement(this._webkitThreadUsageView);
     1776        clearGraphOverlayElement(this._unknownThreadUsageView);
     1777
     1778        for (let workerView of this._workerViews)
     1779            clearGraphOverlayElement(workerView);
     1780    }
     1781
     1782    _hideGraphOverlay()
     1783    {
     1784        if (this._stickingOverlay)
     1785            return;
     1786
     1787        this._overlayRecord = null;
     1788        this._overlayTime = NaN;
     1789        this._overlayMarker.time = -1;
     1790        this._clearOverlayMarkers();
    16061791    }
    16071792};
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageCombinedView.js

    r242738 r242739  
    2424 */
    2525
    26 WI.CPUUsageStackedView = class CPUUsageStackedView extends WI.View
     26WI.CPUUsageCombinedView = class CPUUsageCombinedView extends WI.View
    2727{
    2828    constructor(displayName)
     
    3030        super();
    3131
    32         this.element.classList.add("cpu-usage-stacked-view");
     32        this.element.classList.add("cpu-usage-combined-view");
    3333
    3434        this._detailsElement = this.element.appendChild(document.createElement("div"));
     
    4444        this._detailsMaxElement = this._detailsElement.appendChild(document.createElement("span"));
    4545        this._detailsElement.appendChild(document.createElement("br"));
    46         this._detailsMinElement = this._detailsElement.appendChild(document.createElement("span"));
     46        this._detailsElement.appendChild(document.createElement("br"));
    4747        this._updateDetails(NaN, NaN);
    4848
     
    5050        this._graphElement.classList.add("graph");
    5151
     52        // Combined thread usage area chart.
    5253        this._chart = new WI.StackedAreaChart;
    5354        this._chart.initializeSections(["main-thread-usage", "worker-thread-usage", "total-usage"]);
    5455        this.addSubview(this._chart);
    5556        this._graphElement.appendChild(this._chart.element);
     57
     58        // Main thread indicator strip.
     59        this._rangeChart = new WI.RangeChart;
     60        this.addSubview(this._rangeChart);
     61        this._graphElement.appendChild(this._rangeChart.element);
     62
     63        function appendLegendRow(legendElement, className) {
     64            let rowElement = legendElement.appendChild(document.createElement("div"));
     65            rowElement.classList.add("row");
     66
     67            let swatchElement = rowElement.appendChild(document.createElement("div"));
     68            swatchElement.classList.add("swatch", className);
     69
     70            let labelElement = rowElement.appendChild(document.createElement("div"));
     71            labelElement.classList.add("label");
     72
     73            return labelElement;
     74        }
     75
     76        this._legendElement = this._detailsElement.appendChild(document.createElement("div"));
     77        this._legendElement.classList.add("legend-container");
     78
     79        this._legendMainThreadElement = appendLegendRow(this._legendElement, "main-thread");
     80        this._legendWorkerThreadsElement = appendLegendRow(this._legendElement, "worker-threads");
     81        this._legendOtherThreadsElement = appendLegendRow(this._legendElement, "other-threads");
     82        this._legendTotalThreadsElement = appendLegendRow(this._legendElement, "total");
     83
     84        this.clearLegend();
    5685    }
    5786
    5887    // Public
    5988
     89    get graphElement() { return this._graphElement; }
    6090    get chart() { return this._chart; }
     91    get rangeChart() { return this._rangeChart; }
    6192
    6293    clear()
    6394    {
    6495        this._cachedAverageSize = undefined;
    65         this._cachedMinSize = undefined;
    6696        this._cachedMaxSize = undefined;
    6797        this._updateDetails(NaN, NaN);
    6898
     99        this.clearLegend();
     100
    69101        this._chart.clear();
    70102        this._chart.needsLayout();
     103
     104        this._rangeChart.clear();
     105        this._rangeChart.needsLayout();
    71106    }
    72107
     
    79114        console.assert(min <= average && average <= max);
    80115
    81         this._updateDetails(min, max, average);
    82 
    83         this._chart.clear();
     116        this._updateDetails(max, average);
     117
     118        this._chart.clearPoints();
    84119        this._chart.size = size;
    85120        this._chart.needsLayout();
     
    117152    }
    118153
     154    updateMainThreadIndicator(samples, size, visibleEndTime, xScale)
     155    {
     156        console.assert(size instanceof WI.Size);
     157
     158        this._rangeChart.clear();
     159        this._rangeChart.size = size;
     160        this._rangeChart.needsLayout();
     161
     162        if (!samples.length)
     163            return;
     164
     165        // Coalesce ranges of samples.
     166        let ranges = [];
     167        let currentRange = null;
     168        let currentSampleType = undefined;
     169        for (let i = 0; i < samples.length; ++i) {
     170            // Back to idle, close any current chunk.
     171            let type = samples[i];
     172            if (!type) {
     173                if (currentRange) {
     174                    ranges.push(currentRange);
     175                    currentRange = null;
     176                    currentSampleType = undefined;
     177                }
     178                continue;
     179            }
     180
     181            // Expand existing chunk.
     182            if (type === currentSampleType) {
     183                currentRange.endIndex = i;
     184                continue;
     185            }
     186
     187            // If type changed, close current chunk.
     188            if (currentSampleType) {
     189                ranges.push(currentRange);
     190                currentRange = null;
     191                currentSampleType = undefined;
     192            }
     193
     194            // Start a new chunk.
     195            console.assert(!currentRange);
     196            console.assert(!currentSampleType);
     197            currentRange = {type, startIndex: i, endIndex: i};
     198            currentSampleType = type;
     199        }
     200
     201        for (let {type, startIndex, endIndex} of ranges) {
     202            let startX = xScale(startIndex);
     203            let endX = xScale(endIndex + 1);
     204            let width = endX - startX;
     205            this._rangeChart.addRange(startX, width, type);
     206        }
     207    }
     208
     209    clearLegend()
     210    {
     211        this._legendMainThreadElement.textContent = WI.UIString("Main Thread");
     212        this._legendWorkerThreadsElement.textContent = WI.UIString("Worker Threads");
     213        this._legendOtherThreadsElement.textContent = WI.UIString("Other Threads");
     214        this._legendTotalThreadsElement.textContent = "";
     215    }
     216
     217    updateLegend(record)
     218    {
     219        if (!record) {
     220            this.clearLegend();
     221            return;
     222        }
     223
     224        let {usage, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage} = record;
     225
     226        this._legendMainThreadElement.textContent = WI.UIString("Main: %s").format(Number.percentageString(mainThreadUsage / 100));
     227        this._legendWorkerThreadsElement.textContent = WI.UIString("Worker: %s").format(Number.percentageString(workerThreadUsage / 100));
     228        this._legendOtherThreadsElement.textContent = WI.UIString("Other: %s").format(Number.percentageString((webkitThreadUsage + unknownThreadUsage) / 100));
     229        this._legendTotalThreadsElement.textContent = WI.UIString("Total: %s").format(Number.percentageString(usage / 100));
     230    }
     231
    119232    // Private
    120233
    121     _updateDetails(minSize, maxSize, averageSize)
    122     {
    123         if (this._cachedMinSize === minSize && this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
     234    _updateDetails(maxSize, averageSize)
     235    {
     236        if (this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
    124237            return;
    125238
    126239        this._cachedAverageSize = averageSize;
    127         this._cachedMinSize = minSize;
    128240        this._cachedMaxSize = maxSize;
    129241
    130242        this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(maxSize) ? Number.percentageString(averageSize / 100) : emDash);
    131243        this._detailsMaxElement.textContent = WI.UIString("Highest: %s").format(Number.isFinite(maxSize) ? Number.percentageString(maxSize / 100) : emDash);
    132         this._detailsMinElement.textContent = WI.UIString("Lowest: %s").format(Number.isFinite(minSize) ? Number.percentageString(minSize / 100) : emDash);
    133244    }
    134245};
     246
     247WI.CPUUsageCombinedView._cachedMainThreadIndicatorFillColor = null;
     248WI.CPUUsageCombinedView._cachedMainThreadIndicatorStrokeColor = null;
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js

    r242077 r242739  
    4444        this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span"));
    4545        this._detailsElement.appendChild(document.createElement("br"));
    46         this._detailsMaxElement = this._detailsElement.appendChild(document.createElement("span"));
    47         this._updateDetails(NaN, NaN);
     46        this._detailsUsageElement = this._detailsElement.appendChild(document.createElement("span"));
     47
     48        this.clearLegend();
     49        this._updateDetails(NaN);
    4850
    4951        this._graphElement = this.element.appendChild(document.createElement("div"));
     
    5759    // Public
    5860
     61    get graphElement() { return this._graphElement; }
    5962    get chart() { return this._chart; }
    6063
     
    6265    {
    6366        this._cachedAverageSize = undefined;
    64         this._cachedMaxSize = undefined;
    65         this._updateDetails(NaN, NaN);
     67        this._updateDetails(NaN);
     68
     69        this.clearLegend();
    6670
    6771        this._chart.clear();
     72        this._chart.needsLayout();
    6873    }
    6974
     
    7782        console.assert(property, "CPUUsageView needs a property of the dataPoints to graph");
    7883
    79         this._updateDetails(min, max, average);
     84        this._updateDetails(average);
    8085
    81         this._chart.clear();
     86        this._chart.clearPoints();
    8287        this._chart.size = size;
    8388        this._chart.needsLayout();
     
    109114    }
    110115
     116    clearLegend()
     117    {
     118        this._detailsUsageElement.hidden = true;
     119        this._detailsUsageElement.textContent = emDash;
     120    }
     121
     122    updateLegend(value)
     123    {
     124        let usage = Number.isFinite(value) ? Number.percentageString(value / 100) : emDash;
     125
     126        this._detailsUsageElement.hidden = false;
     127        this._detailsUsageElement.textContent = WI.UIString("Usage: %s").format(usage);
     128    }
     129
    111130    // Private
    112131
    113     _updateDetails(minSize, maxSize, averageSize)
     132    _updateDetails(averageSize)
    114133    {
    115         if (this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
     134        if (this._cachedAverageSize === averageSize)
    116135            return;
    117136
    118137        this._cachedAverageSize = averageSize;
    119         this._cachedMaxSize = maxSize;
    120138
    121139        this._detailsAverageElement.hidden = !Number.isFinite(averageSize);
    122         this._detailsMaxElement.hidden = !Number.isFinite(maxSize);
    123 
    124140        this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(averageSize) ? Number.percentageString(averageSize / 100) : emDash);
    125         this._detailsMaxElement.textContent = WI.UIString("Highest: %s").format(Number.isFinite(maxSize) ? Number.percentageString(maxSize / 100) : emDash);
    126141    }
    127142};
  • trunk/Source/WebInspectorUI/UserInterface/Views/StackedAreaChart.js

    r242077 r242739  
    3030// provide for the segments will allow you to style them. You can then include
    3131// a new set of (x, [y1, y2, y3]) points in the chart via `addPointSet`. The
    32 // order of `y` values must be in the same order as the sections.
     32// order of `y` values must be in the same order as the sections. You can add
     33// point markers (<circle>) with an (x, y) as well, note these are individual
     34// instead of a set.
    3335//
    3436// SVG:
     
    5759
    5860        this._pathElements = [];
     61        this._circleElements = [];
    5962
    6063        this._points = [];
     64        this._markers = [];
    6165        this._size = null;
    6266    }
     
    102106    }
    103107
     108    clearPoints()
     109    {
     110        this._points = [];
     111    }
     112
     113    addPointMarker(x, y)
     114    {
     115        this._markers.push({x, y});
     116    }
     117
     118    clearPointMarkers()
     119    {
     120        this._markers = [];
     121    }
     122
    104123    clear()
    105124    {
    106         this._points = [];
     125        this.clearPoints();
     126        this.clearPointMarkers();
    107127    }
    108128
     
    137157            this._pathElements[i].setAttribute("d", pathString);
    138158        }
     159
     160        if (this._circleElements.length) {
     161            for (let circle of this._circleElements)
     162                circle.remove();
     163            this._circleElements = [];
     164        }
     165
     166        if (this._markers.length) {
     167            for (let {x, y} of this._markers) {
     168                let circle = this._chartElement.appendChild(createSVGElement("circle"));
     169                this._circleElements.push(circle);
     170                circle.setAttribute("cx", x);
     171                circle.setAttribute("cy", y);
     172                circle.setAttribute("r", 3);
     173            }
     174        }
    139175    }
    140176};
  • trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css

    r242562 r242739  
    133133    --memory-max-comparison-stroke-color: hsl(220, 10%, 55%);
    134134
     135    /* LegacyCPUTimeline */
     136    --cpu-fill-color: hsl(81, 80%, 50%);
    135137    --cpu-stroke-color: hsl(118, 33%, 42%);
    136     --cpu-fill-color: hsl(81, 80%, 50%);
     138
     139    --cpu-other-thread-fill-color: hsl(81, 80%, 50%);
     140    --cpu-other-thread-stroke-color: hsl(81, 80%, 30%);
    137141    --cpu-main-thread-fill-color: hsl(118, 43%, 55%);
    138     --cpu-worker-thread-fill-color: hsl(45, 94.75%, 55%);
    139 
    140     --cpu-idle-fill-color: hsl(220, 10%, 75%);
    141     --cpu-idle-stroke-color: hsl(220, 10%, 55%);
     142    --cpu-main-thread-stroke-color: hsl(118, 33%, 42%);
     143    --cpu-worker-thread-fill-color: hsl(59, 79%, 62%);
     144    --cpu-worker-thread-stroke-color: hsl(59, 79%, 37%);
     145    --cpu-overlay-color: var(--cpu-main-thread-stroke-color);
     146
    142147    --cpu-script-fill-color: hsl(269, 65%, 75%);
    143148    --cpu-script-stroke-color: hsl(269, 33%, 50%);
     
    300305        --network-error-color: hsl(0, 54%, 55%);
    301306
     307        --cpu-overlay-color: hsl(36, 98%, 50%);
     308
    302309        --cpu-low-color: hsl(110, 52%, 56%);
    303310        --cpu-medium-color: hsl(46, 91%, 62%);
Note: See TracChangeset for help on using the changeset viewer.