Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (242072 => 242073)
--- trunk/Source/WebInspectorUI/ChangeLog 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/ChangeLog 2019-02-26 06:36:34 UTC (rev 242073)
@@ -1,5 +1,147 @@
2019-02-25 Joseph Pecoraro <pecor...@apple.com>
+ Web Inspector: CPU Usage Timeline - Thread Breakdown
+ https://bugs.webkit.org/show_bug.cgi?id=194788
+
+ Reviewed by Devin Rousso.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Main.html:
+ New strings and files.
+
+ * UserInterface/Views/Variables.css:
+ (:root):
+ New colors for cpu threads / activity breakdown.
+
+ * UserInterface/Models/CPUTimelineRecord.js:
+ (WI.CPUTimelineRecord.prototype.get workers):
+ (WI.CPUTimelineRecord):
+ Distinguish the workers in a CPU timeline record.
+
+ * UserInterface/Views/CPUTimelineOverviewGraph.js:
+ (WI.CPUTimelineOverviewGraph):
+ (WI.CPUTimelineOverviewGraph.prototype.layout):
+ * UserInterface/Views/CPUTimelineOverviewGraph.css:
+ (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect):
+ (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage):
+ (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage):
+ (.timeline-overview-graph.cpu > .column-chart > svg > rect):
+ Stacked column chart for CPU in the overview graph.
+
+ * UserInterface/Views/CPUTimelineView.css:
+ (.timeline-view.cpu > .content > .overview):
+ (.timeline-view.cpu > .content > .details > .subtitle.threads):
+ (.timeline-view.cpu > .content > .overview > .chart):
+ (.timeline-view.cpu > .content > .overview > .chart > .subtitle):
+ (.timeline-view.cpu > .content > .overview > .chart > .container):
+ (.timeline-view.cpu > .content > .overview .samples,):
+ (.timeline-view.cpu .legend):
+ (.timeline-view.cpu .legend .row):
+ (.timeline-view.cpu .legend .row + .row):
+ (.timeline-view.cpu .legend .swatch):
+ (.timeline-view.cpu .legend > .row > .swatch.sample-type-idle):
+ (.timeline-view.cpu .legend > .row > .swatch.sample-type-script):
+ (.timeline-view.cpu .legend > .row > .swatch.sample-type-style):
+ (.timeline-view.cpu .legend > .row > .swatch.sample-type-layout):
+ (.timeline-view.cpu .legend > .row > .swatch.sample-type-paint):
+ (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-idle):
+ (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-script):
+ (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-style):
+ (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-layout):
+ (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-paint):
+ (.timeline-view.cpu svg > path):
+ (.timeline-view.cpu .main-thread svg > path,):
+ (.timeline-view.cpu .worker-thread svg > path,):
+ (.timeline-view.cpu .cpu-usage-view.empty):
+ (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers):
+ (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div):
+ (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div > .label):
+ (.timeline-view.cpu > .content): Deleted.
+ (.cpu-usage-view .line-chart > svg > path): Deleted.
+ (.timeline-view.cpu .legend > .row > .swatch.current): Deleted.
+ * UserInterface/Views/CPUTimelineView.js:
+ (WI.CPUTimelineView):
+ (WI.CPUTimelineView.displayNameForSampleType):
+ (WI.CPUTimelineView.prototype.shown):
+ (WI.CPUTimelineView.prototype.clear.clearUsageView):
+ (WI.CPUTimelineView.prototype.clear):
+ (WI.CPUTimelineView.prototype.initialLayout.createChartContainer):
+ (WI.CPUTimelineView.prototype.initialLayout.appendLegendRow):
+ (WI.CPUTimelineView.prototype.initialLayout):
+ (WI.CPUTimelineView.prototype.layout.removeGreaterThan):
+ (WI.CPUTimelineView.prototype.layout):
+ (WI.CPUTimelineView.prototype.layout.layoutView):
+ (WI.CPUTimelineView.prototype.layout.yScale):
+ (WI.CPUTimelineView.prototype._computeSamplingData.markRecordEntries):
+ (WI.CPUTimelineView.prototype._computeSamplingData):
+ (WI.CPUTimelineView.prototype._removeWorkerThreadViews):
+ (WI.CPUTimelineView.prototype._clearBreakdownLegend):
+ (WI.CPUTimelineView.prototype.layout.xScale): Deleted.
+ Line charts and Circle Chart for threads and breakdowns.
+
+ * UserInterface/Views/CPUUsageStackedView.css:
+ (.cpu-usage-stacked-view):
+ (.cpu-usage-stacked-view > .details):
+ (body[dir=ltr] .cpu-usage-stacked-view > .details):
+ (body[dir=rtl] .cpu-usage-stacked-view > .details):
+ (.cpu-usage-stacked-view > .details > .name):
+ (body[dir=rtl] .cpu-usage-stacked-view > .graph):
+ (.cpu-usage-stacked-view > .graph):
+ (.cpu-usage-stacked-view > .graph,):
+ * UserInterface/Views/CPUUsageStackedView.js:
+ (WI.CPUUsageStackedView):
+ (WI.CPUUsageStackedView.prototype.get chart):
+ (WI.CPUUsageStackedView.prototype.clear):
+ (WI.CPUUsageStackedView.prototype.updateChart):
+ (WI.CPUUsageStackedView.prototype._updateDetails):
+ Same as CPUUsageView except Stacked for the total.
+
+ * UserInterface/Views/CPUUsageView.css:
+ (.cpu-usage-view):
+ (.cpu-usage-view > .details):
+ (.cpu-usage-view > .details > .name):
+ (.cpu-usage-view > .graph):
+ * UserInterface/Views/CPUUsageView.js:
+ (WI.CPUUsageView):
+ (WI.CPUUsageView.prototype.get chart):
+ (WI.CPUUsageView.prototype.clear):
+ (WI.CPUUsageView.prototype.updateChart):
+ (WI.CPUUsageView.prototype._updateDetails):
+ Slight modifications for the new UI.
+
+ * UserInterface/Views/LegacyCPUTimelineView.css:
+ (.timeline-view.legacy-cpu .cpu-usage-view .line-chart > svg > path):
+ * UserInterface/Views/LegacyCPUTimelineView.js:
+ (WI.LegacyCPUTimelineView.prototype.layout):
+ Update API calls in the legacy view for minor changes.
+
+ * UserInterface/Views/MemoryCategoryView.css:
+ (.memory-category-view > .details):
+ (.memory-category-view > .details > .name):
+ * UserInterface/Views/MemoryTimelineOverviewGraph.js:
+ (WI.MemoryTimelineOverviewGraph.prototype.layout):
+ * UserInterface/Views/MemoryTimelineView.css:
+ (body .timeline-view.memory):
+ (.timeline-view.memory): Deleted.
+ Improvements ported from the CPU timeline views.
+
+ * UserInterface/Views/StackedColumnChart.js: Added.
+ (WI.StackedColumnChart):
+ (WI.StackedColumnChart.prototype.get size):
+ (WI.StackedColumnChart.prototype.set size):
+ (WI.StackedColumnChart.prototype.initializeSections):
+ (WI.StackedColumnChart.prototype.addColumnSet):
+ (WI.StackedColumnChart.prototype.clear):
+ (WI.StackedColumnChart.prototype.layout):
+ A stacked column chart implementation.
+
+ * UserInterface/Views/View.js:
+ (WI.View.prototype.removeUnparentedSubview):
+ Add a way to remove a subview that had its `element` moved
+ someplace other than a direct child of our element.
+
+2019-02-25 Joseph Pecoraro <pecor...@apple.com>
+
Web Inspector: Dark Mode: Network Overview Graph segments have distracting white box shadow
https://bugs.webkit.org/show_bug.cgi?id=194966
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -158,6 +158,7 @@
localizedStrings["Break on\u2026"] = "Break on\u2026";
localizedStrings["Breakdown"] = "Breakdown";
localizedStrings["Breakdown of each memory category at the end of the selected time range"] = "Breakdown of each memory category at the end of the selected time range";
+localizedStrings["Breakdown of time spent on the main thread"] = "Breakdown of time spent on the main thread";
localizedStrings["Breakpoint"] = "Breakpoint";
localizedStrings["Breakpoints"] = "Breakpoints";
localizedStrings["Breakpoints disabled"] = "Breakpoints disabled";
@@ -599,6 +600,7 @@
localizedStrings["MIME Type"] = "MIME Type";
localizedStrings["MIME Type:"] = "MIME Type:";
localizedStrings["MSE Logging:"] = "MSE Logging:";
+localizedStrings["Main Thread"] = "Main Thread";
localizedStrings["Manifest URL"] = "Manifest URL";
localizedStrings["Mass"] = "Mass";
localizedStrings["Matching"] = "Matching";
@@ -688,6 +690,7 @@
localizedStrings["Originator"] = "Originator";
localizedStrings["Other"] = "Other";
localizedStrings["Other Issue"] = "Other Issue";
+localizedStrings["Other Threads"] = "Other Threads";
localizedStrings["Other\u2026"] = "Other\u2026";
localizedStrings["Outgoing message"] = "Outgoing message";
localizedStrings["Output: "] = "Output: ";
@@ -955,6 +958,7 @@
localizedStrings["Stopping the \u201C%s\u201D audit"] = "Stopping the \u201C%s\u201D audit";
localizedStrings["Storage"] = "Storage";
localizedStrings["Style Attribute"] = "Style Attribute";
+localizedStrings["Style Resolution"] = "Style Resolution";
localizedStrings["Style rule"] = "Style rule";
localizedStrings["Styles"] = "Styles";
localizedStrings["Styles Invalidated"] = "Styles Invalidated";
@@ -1000,7 +1004,9 @@
localizedStrings["This object is a root"] = "This object is a root";
localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
localizedStrings["This text resource could benefit from compression"] = "This text resource could benefit from compression";
+localizedStrings["Threads"] = "Threads";
localizedStrings["Time"] = "Time";
+localizedStrings["Time spent on the main thread"] = "Time spent on the main thread";
localizedStrings["Time to First Byte"] = "Time to First Byte";
localizedStrings["Timeline"] = "Timeline";
localizedStrings["Timeline Recording %d"] = "Timeline Recording %d";
@@ -1016,6 +1022,7 @@
localizedStrings["Toggle Classes"] = "Toggle Classes";
localizedStrings["Toggle Visibility"] = "Toggle Visibility";
localizedStrings["Top Functions"] = "Top Functions";
+localizedStrings["Total"] = "Total";
localizedStrings["Total Time"] = "Total Time";
localizedStrings["Total memory size at the end of the selected time range"] = "Total memory size at the end of the selected time range";
localizedStrings["Total time"] = "Total time";
@@ -1074,6 +1081,7 @@
localizedStrings["Watch Expressions"] = "Watch Expressions";
localizedStrings["Waterfall"] = "Waterfall";
localizedStrings["Web Inspector"] = "Web Inspector";
+localizedStrings["WebKit Threads"] = "WebKit Threads";
localizedStrings["WebRTC"] = "WebRTC";
localizedStrings["WebRTC Logging:"] = "WebRTC Logging:";
localizedStrings["WebSocket Connection Established"] = "WebSocket Connection Established";
@@ -1081,6 +1089,7 @@
localizedStrings["Width"] = "Width";
localizedStrings["With Object Properties"] = "With Object Properties";
localizedStrings["Worker"] = "Worker";
+localizedStrings["Worker Thread"] = "Worker Thread";
localizedStrings["Worker \u2014 %s"] = "Worker \u2014 %s";
localizedStrings["Working Copy"] = "Working Copy";
localizedStrings["Wrap lines to editor width"] = "Wrap lines to editor width";
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-02-26 06:36:34 UTC (rev 242073)
@@ -45,6 +45,7 @@
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
+ <link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
@@ -592,6 +593,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
@@ -806,6 +808,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -43,6 +43,7 @@
this._webkitThreadUsage = 0;
this._workerThreadUsage = 0;
this._unknownThreadUsage = 0;
+ this._workersData = null;
for (let thread of threads) {
if (thread.type === InspectorBackend.domains.CPUProfiler.ThreadInfoType.Main) {
@@ -52,10 +53,15 @@
}
if (thread.type === InspectorBackend.domains.CPUProfiler.ThreadInfoType.WebKit) {
- if (thread.targetId)
+ if (thread.targetId) {
+ if (!this._workersData)
+ this._workersData = [];
+ this._workersData.push(thread);
this._workerThreadUsage += thread.usage;
- else
- this._webkitThreadUsage += thread.usage;
+ continue;
+ }
+
+ this._webkitThreadUsage += thread.usage;
continue;
}
@@ -72,4 +78,5 @@
get webkitThreadUsage() { return this._webkitThreadUsage; }
get workerThreadUsage() { return this._workerThreadUsage; }
get unknownThreadUsage() { return this._unknownThreadUsage; }
+ get workersData() { return this._workersData; }
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -57,11 +57,29 @@
background-color: var(--timeline-even-background-color);
}
-body[dir=rtl] .timeline-overview-graph.cpu > .column-chart {
+body[dir=rtl] .timeline-overview-graph.cpu > .stacked-column-chart {
transform: scaleX(-1);
}
-.timeline-overview-graph.cpu > .column-chart > svg > rect {
+.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect {
stroke: var(--cpu-stroke-color);
fill: var(--cpu-fill-color);
}
+
+.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage {
+ fill: var(--cpu-main-thread-fill-color);
+}
+
+.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage {
+ fill: var(--cpu-worker-thread-fill-color);
+}
+
+/* LegacyCPUTimeline */
+.timeline-overview-graph.cpu > .column-chart > svg > rect {
+ stroke: var(--cpu-stroke-color);
+ fill: var(--cpu-main-thread-fill-color);
+}
+
+body[dir=rtl] .timeline-overview-graph.cpu > .column-chart {
+ transform: scaleX(-1);
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -38,7 +38,11 @@
this._cpuTimeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this);
let size = new WI.Size(0, this.height);
- this._chart = new WI.ColumnChart(size);
+ if (WI.settings.experimentalEnableCPUUsageEnhancements.value) {
+ this._chart = new WI.StackedColumnChart(size);
+ this._chart.initializeSections(["main-thread-usage", "worker-thread-usage", "total-usage"]);
+ } else
+ this._chart = new WI.ColumnChart(size);
this.addSubview(this._chart);
this.element.appendChild(this._chart.element);
@@ -114,13 +118,15 @@
// Bars for each record.
for (let record of visibleRecords) {
let w = intervalWidth;
- let h = Math.max(minimumDisplayHeight, yScale(record.usage));
+ let h3 = Math.max(minimumDisplayHeight, yScale(record.usage));
let x = xScale(record.startTime - (samplingRatePerSecond / 2));
- let y = height - h;
- this._chart.addColumn(x, y, w, h);
+ if (WI.settings.experimentalEnableCPUUsageEnhancements.value) {
+ let h1 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage));
+ let h2 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage + record.workerThreadUsage));
+ this._chart.addColumnSet(x, height, w, [h1, h2, h3]);
+ } else
+ this._chart.addColumn(x, height - h3, w, h3);
}
-
- this._chart.updateLayout();
}
// Private
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -27,8 +27,12 @@
overflow: scroll;
}
-.timeline-view.cpu > .content {
- margin-top: 10px;
+.timeline-view.cpu > .content > .overview {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 10px;
+ padding: 10px;
+ border-bottom: 1px solid var(--border-color);
}
.timeline-view.cpu > .content .subtitle {
@@ -63,12 +67,150 @@
border-bottom: 1px solid var(--border-color);
}
-.cpu-usage-view .line-chart > svg > path {
+.timeline-view.cpu > .content > .details > .subtitle.threads {
+ position: relative;
+ z-index: calc(var(--timeline-marker-z-index) + 1);
+ padding-top: 10px;
+ background-color: var(--background-color-content);
+}
+
+.timeline-view.cpu > .content > .overview > .chart {
+ width: 420px;
+ text-align: center;
+}
+
+.timeline-view.cpu > .content > .overview > .chart > .subtitle {
+ margin-bottom: 1em;
+}
+
+.timeline-view.cpu > .content > .overview > .chart > .container {
+ display: flex;
+ justify-content: center;
+}
+
+.timeline-view.cpu > .content > .overview .samples,
+.timeline-view.cpu > .content > .overview .legend .size {
+ margin: auto;
+ color: var(--text-color-secondary);
+}
+
+.timeline-view.cpu .legend {
+ -webkit-padding-start: 20px;
+ text-align: start;
+}
+
+.timeline-view.cpu .legend .row {
+ display: flex;
+}
+
+.timeline-view.cpu .legend .row + .row {
+ margin-top: 4px;
+}
+
+.timeline-view.cpu .legend .swatch {
+ width: 1em;
+ height: 1em;
+ margin-top: 1px;
+ -webkit-margin-end: 8px;
+}
+
+.timeline-view.cpu .legend > .row > .swatch.sample-type-idle {
+ border: 1px solid var(--cpu-idle-stroke-color);
+ background-color: var(--cpu-idle-fill-color);
+}
+
+.timeline-view.cpu .legend > .row > .swatch.sample-type-script {
+ border: 1px solid var(--cpu-script-stroke-color);
+ background-color: var(--cpu-script-fill-color);
+}
+
+.timeline-view.cpu .legend > .row > .swatch.sample-type-style {
+ border: 1px solid var(--cpu-style-stroke-color);
+ background-color: var(--cpu-style-fill-color);
+}
+
+.timeline-view.cpu .legend > .row > .swatch.sample-type-layout {
+ border: 1px solid var(--cpu-layout-stroke-color);
+ background-color: var(--cpu-layout-fill-color);
+}
+
+.timeline-view.cpu .legend > .row > .swatch.sample-type-paint {
+ border: 1px solid var(--cpu-paint-stroke-color);
+ background-color: var(--cpu-paint-fill-color);
+}
+
+.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-idle {
+ stroke: var(--cpu-idle-stroke-color);
+ fill: var(--cpu-idle-fill-color);
+}
+
+.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-script {
+ stroke: var(--cpu-script-stroke-color);
+ fill: var(--cpu-script-fill-color);
+}
+
+.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-style {
+ stroke: var(--cpu-style-stroke-color);
+ fill: var(--cpu-style-fill-color);
+}
+
+.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-layout {
+ stroke: var(--cpu-layout-stroke-color);
+ fill: var(--cpu-layout-fill-color);
+}
+
+.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-paint {
+ stroke: var(--cpu-paint-stroke-color);
+ fill: var(--cpu-paint-fill-color);
+}
+
+.timeline-view.cpu svg > path {
stroke: var(--cpu-stroke-color);
fill: var(--cpu-fill-color);
}
-.timeline-view.cpu .legend > .row > .swatch.current {
- border: 1px solid var(--cpu-max-comparison-stroke-color);
- background: var(--cpu-max-comparison-fill-color);
+.timeline-view.cpu .main-thread svg > path,
+.timeline-view.cpu svg > path.main-thread-usage {
+ fill: var(--cpu-main-thread-fill-color);
}
+
+.timeline-view.cpu .worker-thread svg > path,
+.timeline-view.cpu svg > path.worker-thread-usage {
+ fill: var(--cpu-worker-thread-fill-color);
+}
+
+.timeline-view.cpu .cpu-usage-view.empty {
+ display: none;
+}
+
+.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ pointer-events: none;
+}
+
+.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div {
+ position: absolute;
+ z-index: 10;
+ width: 100%;
+ height: 1px;
+ background-color: hsla(0, 0%, var(--foreground-lightness), 0.07);
+}
+
+body[dir=ltr] .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div {
+ text-align: end;
+}
+
+body[dir=rtl] .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div {
+ transform: scaleX(-1);
+}
+
+.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div > .label {
+ padding: 2px;
+ font-size: 8px;
+ color: var(--text-color-secondary);
+ background-color: var(--background-color-content);
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -35,29 +35,29 @@
this.element.classList.add("cpu");
- let contentElement = this.element.appendChild(document.createElement("div"));
- contentElement.classList.add("content");
+ timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this);
+ }
- // FIXME: Overview with charts.
+ // Static
- let detailsContainerElement = this._detailsContainerElement = contentElement.appendChild(document.createElement("div"));
- detailsContainerElement.classList.add("details");
+ static displayNameForSampleType(type)
+ {
+ switch (type) {
+ case WI.CPUTimelineView.SampleType.Script:
+ return WI.UIString("Script");
+ case WI.CPUTimelineView.SampleType.Layout:
+ return WI.UIString("Layout");
+ case WI.CPUTimelineView.SampleType.Paint:
+ return WI.UIString("Paint");
+ case WI.CPUTimelineView.SampleType.Style:
+ return WI.UIString("Style Resolution");
+ }
+ console.error("Unknown sample type", type);
+ }
- this._timelineRuler = new WI.TimelineRuler;
- this.addSubview(this._timelineRuler);
- detailsContainerElement.appendChild(this._timelineRuler.element);
+ static get cpuUsageViewHeight() { return 150; }
+ static get threadCPUUsageViewHeight() { return 65; }
- let detailsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div"));
- detailsSubtitleElement.classList.add("subtitle");
- detailsSubtitleElement.textContent = WI.UIString("CPU Usage");
-
- this._cpuUsageView = new WI.CPUUsageView;
- this.addSubview(this._cpuUsageView);
- this._detailsContainerElement.appendChild(this._cpuUsageView.element);
-
- timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this);
- }
-
// Public
shown()
@@ -64,7 +64,8 @@
{
super.shown();
- this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize);
+ if (this._timelineRuler)
+ this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize);
}
closed()
@@ -82,7 +83,27 @@
clear()
{
- this._cpuUsageView.clear();
+ if (!this.didInitialLayout)
+ return;
+
+ this._breakdownChart.clear();
+ this._breakdownChart.needsLayout();
+ this._clearBreakdownLegend();
+
+ function clearUsageView(view) {
+ view.clear();
+
+ let markersElement = view.chart.element.querySelector(".markers");
+ if (markersElement)
+ markersElement.remove();
+ }
+
+ clearUsageView(this._cpuUsageView);
+ clearUsageView(this._mainThreadUsageView);
+ clearUsageView(this._webkitThreadUsageView);
+ clearUsageView(this._unknownThreadUsageView);
+
+ this._removeWorkerThreadViews();
}
get scrollableElements()
@@ -94,6 +115,110 @@
get showsFilterBar() { return false; }
+ initialLayout()
+ {
+ this.element.style.setProperty("--cpu-usage-stacked-view-height", CPUTimelineView.cpuUsageViewHeight + "px");
+ this.element.style.setProperty("--cpu-usage-view-height", CPUTimelineView.threadCPUUsageViewHeight + "px");
+
+ let contentElement = this.element.appendChild(document.createElement("div"));
+ contentElement.classList.add("content");
+
+ let overviewElement = contentElement.appendChild(document.createElement("div"));
+ overviewElement.classList.add("overview");
+
+ function createChartContainer(parentElement, subtitle, tooltip) {
+ let chartElement = parentElement.appendChild(document.createElement("div"));
+ chartElement.classList.add("chart");
+
+ let chartSubtitleElement = chartElement.appendChild(document.createElement("div"));
+ chartSubtitleElement.classList.add("subtitle");
+ chartSubtitleElement.textContent = subtitle;
+ chartSubtitleElement.title = tooltip;
+
+ let chartFlexContainerElement = chartElement.appendChild(document.createElement("div"));
+ chartFlexContainerElement.classList.add("container");
+ return chartFlexContainerElement;
+ }
+
+ function appendLegendRow(legendElement, sampleType) {
+ let rowElement = legendElement.appendChild(document.createElement("div"));
+ rowElement.classList.add("row");
+
+ let swatchElement = rowElement.appendChild(document.createElement("div"));
+ swatchElement.classList.add("swatch", sampleType);
+
+ let valueContainer = rowElement.appendChild(document.createElement("div"));
+ valueContainer.classList.add("value");
+
+ let labelElement = valueContainer.appendChild(document.createElement("div"));
+ labelElement.classList.add("label");
+ labelElement.textContent = WI.CPUTimelineView.displayNameForSampleType(sampleType);
+
+ let sizeElement = valueContainer.appendChild(document.createElement("div"));
+ sizeElement.classList.add("size");
+
+ return sizeElement;
+ }
+
+ let breakdownChartContainerElement = createChartContainer(overviewElement, WI.UIString("Main Thread"), WI.UIString("Breakdown of time spent on the main thread"));
+ this._breakdownChart = new WI.CircleChart({size: 120, innerRadiusRatio: 0.5});
+ this._breakdownChart.segments = Object.values(WI.CPUTimelineView.SampleType);
+ this.addSubview(this._breakdownChart);
+ breakdownChartContainerElement.appendChild(this._breakdownChart.element);
+
+ this._breakdownLegendElement = breakdownChartContainerElement.appendChild(document.createElement("div"));
+ this._breakdownLegendElement.classList.add("legend");
+
+ this._breakdownLegendScriptElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Script);
+ this._breakdownLegendLayoutElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Layout);
+ this._breakdownLegendPaintElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Paint);
+ this._breakdownLegendStyleElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Style);
+
+ let detailsContainerElement = this._detailsContainerElement = contentElement.appendChild(document.createElement("div"));
+ detailsContainerElement.classList.add("details");
+
+ this._timelineRuler = new WI.TimelineRuler;
+ this._timelineRuler.zeroTime = this.zeroTime;
+ this._timelineRuler.startTime = this.startTime;
+ this._timelineRuler.endTime = this.endTime;
+
+ this.addSubview(this._timelineRuler);
+ detailsContainerElement.appendChild(this._timelineRuler.element);
+
+ // Cause the TimelineRuler to layout now so we will have some of its
+ // important properties initialized for our layout.
+ this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize);
+
+ let detailsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div"));
+ detailsSubtitleElement.classList.add("subtitle");
+ detailsSubtitleElement.textContent = WI.UIString("CPU Usage");
+
+ this._cpuUsageView = new WI.CPUUsageStackedView(WI.UIString("Total"));
+ this.addSubview(this._cpuUsageView);
+ this._detailsContainerElement.appendChild(this._cpuUsageView.element);
+
+ let threadsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div"));
+ threadsSubtitleElement.classList.add("subtitle", "threads");
+ threadsSubtitleElement.textContent = WI.UIString("Threads");
+
+ this._mainThreadUsageView = new WI.CPUUsageView(WI.UIString("Main Thread"));
+ this._mainThreadUsageView.element.classList.add("main-thread");
+ this.addSubview(this._mainThreadUsageView);
+ this._detailsContainerElement.appendChild(this._mainThreadUsageView.element);
+
+ this._webkitThreadUsageView = new WI.CPUUsageView(WI.UIString("WebKit Threads"));
+ this._webkitThreadUsageView.element.classList.add("non-main-thread");
+ this.addSubview(this._webkitThreadUsageView);
+ this._detailsContainerElement.appendChild(this._webkitThreadUsageView.element);
+
+ this._unknownThreadUsageView = new WI.CPUUsageView(WI.UIString("Other Threads"));
+ this._unknownThreadUsageView.element.classList.add("non-main-thread");
+ this.addSubview(this._unknownThreadUsageView);
+ this._detailsContainerElement.appendChild(this._unknownThreadUsageView.element);
+
+ this._workerViews = [];
+ }
+
layout()
{
if (this.layoutReason === WI.View.LayoutReason.Resize)
@@ -104,14 +229,16 @@
this._timelineRuler.startTime = this.startTime;
this._timelineRuler.endTime = this.endTime;
- const cpuUsageViewHeight = 75; // Keep this in sync with .cpu-usage-view
+ let secondsPerPixel = this._timelineRuler.secondsPerPixel;
+ if (!secondsPerPixel)
+ return;
let graphStartTime = this.startTime;
let graphEndTime = this.endTime;
- let secondsPerPixel = this._timelineRuler.secondsPerPixel;
let visibleEndTime = Math.min(this.endTime, this.currentTime);
let discontinuities = this._recording.discontinuitiesInTimeRange(graphStartTime, visibleEndTime);
+ let originalDiscontinuities = discontinuities.slice();
// Don't include the record before the graph start if the graph start is within a gap.
let includeRecordBeforeStart = !discontinuities.length || discontinuities[0].startTime > graphStartTime;
@@ -121,20 +248,59 @@
return;
}
- // Update total usage chart with the last record's data.
- let lastRecord = visibleRecords.lastValue;
+ let samplingData = this._computeSamplingData(graphStartTime, visibleEndTime);
+ let nonIdleSamplesCount = samplingData.samples.length - samplingData.samplesIdle;
+ if (!nonIdleSamplesCount) {
+ this._breakdownChart.clear();
+ this._breakdownChart.needsLayout();
+ this._clearBreakdownLegend();
+ } else {
+ let percentScript = samplingData.samplesScript / nonIdleSamplesCount;
+ let percentLayout = samplingData.samplesLayout / nonIdleSamplesCount;
+ let percentPaint = samplingData.samplesPaint / nonIdleSamplesCount;
+ let percentStyle = samplingData.samplesStyle / nonIdleSamplesCount;
- // FIXME: Left chart.
- // FIXME: Right chart.
+ this._breakdownLegendScriptElement.textContent = `${Number.percentageString(percentScript)} (${samplingData.samplesScript})`;
+ this._breakdownLegendLayoutElement.textContent = `${Number.percentageString(percentLayout)} (${samplingData.samplesLayout})`;
+ this._breakdownLegendPaintElement.textContent = `${Number.percentageString(percentPaint)} (${samplingData.samplesPaint})`;
+ this._breakdownLegendStyleElement.textContent = `${Number.percentageString(percentStyle)} (${samplingData.samplesStyle})`;
+ this._breakdownChart.values = [percentScript * 100, percentLayout * 100, percentPaint * 100, percentStyle * 100];
+ this._breakdownChart.needsLayout();
+
+ let centerElement = this._breakdownChart.centerElement;
+ let samplesElement = centerElement.firstChild;
+ if (!samplesElement) {
+ samplesElement = centerElement.appendChild(document.createElement("div"));
+ samplesElement.classList.add("samples");
+ samplesElement.title = WI.UIString("Time spent on the main thread");
+ }
+
+ let millisecondsStringNoDecimal = WI.UIString("%.0fms").format(nonIdleSamplesCount);
+ samplesElement.textContent = millisecondsStringNoDecimal;
+ }
+
let dataPoints = [];
+ let workersDataMap = new Map;
+
let max = -Infinity;
+ let mainThreadMax = -Infinity;
+ let webkitThreadMax = -Infinity;
+ let unknownThreadMax = -Infinity;
+
let min = Infinity;
+ let mainThreadMin = Infinity;
+ let webkitThreadMin = Infinity;
+ let unknownThreadMin = Infinity;
+
let average = 0;
+ let mainThreadAverage = 0;
+ let webkitThreadAverage = 0;
+ let unknownThreadAverage = 0;
for (let record of visibleRecords) {
let time = record.startTime;
- let usage = record.usage;
+ let {usage, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage} = record;
if (discontinuities.length && discontinuities[0].endTime < time) {
let startDiscontinuity = discontinuities.shift();
@@ -141,52 +307,390 @@
let endDiscontinuity = startDiscontinuity;
while (discontinuities.length && discontinuities[0].endTime < time)
endDiscontinuity = discontinuities.shift();
- dataPoints.push({time: startDiscontinuity.startTime, size: 0});
- dataPoints.push({time: endDiscontinuity.endTime, size: 0});
- dataPoints.push({time: endDiscontinuity.endTime, size: usage});
+
+ if (dataPoints.length) {
+ let previousDataPoint = dataPoints.lastValue;
+ dataPoints.push({
+ time: startDiscontinuity.startTime,
+ mainThreadUsage: previousDataPoint.mainThreadUsage,
+ workerThreadUsage: previousDataPoint.workerThreadUsage,
+ webkitThreadUsage: previousDataPoint.webkitThreadUsage,
+ unknownThreadUsage: previousDataPoint.unknownThreadUsage,
+ usage: previousDataPoint.usage,
+ });
+ }
+
+ dataPoints.push({time: startDiscontinuity.startTime, mainThreadUsage: 0, workerThreadUsage: 0, webkitThreadUsage: 0, unknownThreadUsage: 0, usage: 0});
+ dataPoints.push({time: endDiscontinuity.endTime, mainThreadUsage: 0, workerThreadUsage: 0, webkitThreadUsage: 0, unknownThreadUsage: 0, usage: 0});
+ dataPoints.push({time: endDiscontinuity.endTime, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage, usage});
}
- dataPoints.push({time, size: usage});
+ dataPoints.push({time, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage, usage});
+
max = Math.max(max, usage);
+ mainThreadMax = Math.max(mainThreadMax, mainThreadUsage);
+ webkitThreadMax = Math.max(webkitThreadMax, webkitThreadUsage);
+ unknownThreadMax = Math.max(unknownThreadMax, unknownThreadUsage);
+
min = Math.min(min, usage);
+ mainThreadMin = Math.min(mainThreadMin, mainThreadUsage);
+ webkitThreadMin = Math.min(webkitThreadMin, webkitThreadUsage);
+ unknownThreadMin = Math.min(unknownThreadMin, unknownThreadUsage);
+
average += usage;
+ mainThreadAverage += mainThreadUsage;
+ webkitThreadAverage += webkitThreadUsage;
+ unknownThreadAverage += unknownThreadUsage;
+
+ if (record.workersData && record.workersData.length) {
+ for (let {targetId, usage} of record.workersData) {
+ let workerData = workersDataMap.get(targetId);
+ if (!workerData) {
+ workerData = {
+ discontinuities: originalDiscontinuities.slice(),
+ recordsCount: 0,
+ dataPoints: [],
+ min: Infinity,
+ max: -Infinity,
+ average: 0
+ };
+
+ while (workerData.discontinuities.length && workerData.discontinuities[0].endTime <= graphStartTime)
+ workerData.discontinuities.shift();
+ workerData.dataPoints.push({time: graphStartTime, usage: 0});
+ workerData.dataPoints.push({time, usage: 0});
+ workersDataMap.set(targetId, workerData);
+ }
+
+ if (workerData.discontinuities.length && workerData.discontinuities[0].endTime < time) {
+ let startDiscontinuity = workerData.discontinuities.shift();
+ let endDiscontinuity = startDiscontinuity;
+ while (workerData.discontinuities.length && workerData.discontinuities[0].endTime < time)
+ endDiscontinuity = workerData.discontinuities.shift();
+ if (workerData.dataPoints.length) {
+ let previousDataPoint = workerData.dataPoints.lastValue;
+ workerData.dataPoints.push({time: startDiscontinuity.startTime, usage: previousDataPoint.usage});
+ }
+ workerData.dataPoints.push({time: startDiscontinuity.startTime, usage: 0});
+ workerData.dataPoints.push({time: endDiscontinuity.endTime, usage: 0});
+ workerData.dataPoints.push({time: endDiscontinuity.endTime, usage});
+ }
+
+ workerData.dataPoints.push({time, usage});
+ workerData.recordsCount += 1;
+ workerData.max = Math.max(workerData.max, usage);
+ workerData.min = Math.min(workerData.min, usage);
+ workerData.average += usage;
+ }
+ }
}
average /= visibleRecords.length;
+ mainThreadAverage /= visibleRecords.length;
+ webkitThreadAverage /= visibleRecords.length;
+ unknownThreadAverage /= visibleRecords.length;
+ for (let [workerId, workerData] of workersDataMap)
+ workerData.average = workerData.average / workerData.recordsCount;
+
// If the graph end time is inside a gap, the last data point should
// only be extended to the start of the discontinuity.
if (discontinuities.length)
visibleEndTime = discontinuities[0].startTime;
- function layoutView(view, {dataPoints, min, max, average}) {
+ function removeGreaterThan(arr, max) {
+ return arr.filter((x) => x <= max);
+ }
+
+ function markerValuesForMaxValue(max) {
+ if (max < 1)
+ return [0.5];
+ if (max < 7)
+ return removeGreaterThan([1, 3, 5], max);
+ if (max < 12.5)
+ return removeGreaterThan([5, 10], max);
+ if (max < 20)
+ return removeGreaterThan([5, 10, 15], max);
+ if (max < 30)
+ return removeGreaterThan([10, 20, 30], max);
+ if (max < 50)
+ return removeGreaterThan([15, 30, 45], max);
+ if (max < 100)
+ return removeGreaterThan([25, 50, 75], max);
+ if (max < 200)
+ return removeGreaterThan([50, 100, 150], max);
+ if (max >= 200) {
+ let hundreds = Math.floor(max / 100);
+ let even = (hundreds % 2) === 0;
+ if (even) {
+ let top = hundreds * 100;
+ let bottom = top / 2;
+ return [bottom, top];
+ }
+ let top = hundreds * 100;
+ let bottom = 100;
+ let mid = (top + bottom) / 2;
+ return [bottom, mid, top];
+ }
+ }
+
+ // Layout all graphs to the same time scale. The maximum value is
+ // the maximum total CPU usage across all threads.
+ let layoutMax = max;
+
+ function layoutView(view, property, graphHeight, {dataPoints, min, max, average}) {
if (min === Infinity)
min = 0;
if (max === -Infinity)
max = 0;
+ if (layoutMax === -Infinity)
+ layoutMax = 0;
- // Zoom in to the top of each graph to accentuate small changes.
- let graphMin = min * 0.95;
- let graphMax = (max * 1.05) - graphMin;
+ let isAllThreadsGraph = property === null;
+ let graphMax = layoutMax * 1.05;
+
function xScale(time) {
return (time - graphStartTime) / secondsPerPixel;
}
- let size = new WI.Size(xScale(graphEndTime), cpuUsageViewHeight);
+ let size = new WI.Size(xScale(graphEndTime), graphHeight);
function yScale(value) {
- return size.height - (((value - graphMin) / graphMax) * size.height);
+ return size.height - ((value / graphMax) * size.height);
}
- view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale);
+ view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, property);
+
+ let markersElement = view.chart.element.querySelector(".markers");
+ if (!markersElement) {
+ markersElement = view.chart.element.appendChild(document.createElement("div"));
+ markersElement.className = "markers";
+ }
+ markersElement.removeChildren();
+
+ let markerValues;
+ if (isAllThreadsGraph)
+ markerValues = markerValuesForMaxValue(max);
+ else {
+ const minimumMarkerTextHeight = 17;
+ let percentPerPixel = 1 / (graphHeight / layoutMax);
+ if (layoutMax < 5) {
+ let minimumDisplayablePercentByTwo = Math.ceil((minimumMarkerTextHeight * percentPerPixel) / 2) * 2;
+ markerValues = [Math.max(minimumDisplayablePercentByTwo, Math.floor(max))];
+ } else {
+ let minimumDisplayablePercentByFive = Math.ceil((minimumMarkerTextHeight * percentPerPixel) / 5) * 5;
+ markerValues = [Math.max(minimumDisplayablePercentByFive, Math.floor(max))];
+ }
+ }
+
+ for (let value of markerValues) {
+ let marginTop = yScale(value);
+
+ let markerElement = markersElement.appendChild(document.createElement("div"));
+ markerElement.style.marginTop = marginTop.toFixed(2) + "px";
+
+ let labelElement = markerElement.appendChild(document.createElement("span"));
+ labelElement.classList.add("label");
+ const precision = 0;
+ labelElement.innerText = Number.percentageString(value / 100, precision);
+ }
}
- layoutView(this._cpuUsageView, {dataPoints, min, max, average});
+ layoutView(this._cpuUsageView, null, CPUTimelineView.cpuUsageViewHeight, {dataPoints, min, max, average});
+ layoutView(this._mainThreadUsageView, "mainThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: mainThreadMin, max: mainThreadMax, average: mainThreadAverage});
+ layoutView(this._webkitThreadUsageView, "webkitThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: webkitThreadMin, max: webkitThreadMax, average: webkitThreadAverage});
+ layoutView(this._unknownThreadUsageView, "unknownThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: unknownThreadMin, max: unknownThreadMax, average: unknownThreadAverage});
+
+ this._removeWorkerThreadViews();
+
+ for (let [workerId, workerData] of workersDataMap) {
+ let worker = WI.targetManager.targetForIdentifier(workerId);
+ let displayName = worker ? worker.displayName : WI.UIString("Worker Thread");
+ let workerView = new WI.CPUUsageView(displayName);
+ workerView.element.classList.add("worker-thread");
+ this.addSubview(workerView);
+ this._detailsContainerElement.insertBefore(workerView.element, this._webkitThreadUsageView.element);
+ this._workerViews.push(workerView);
+
+ layoutView(workerView, "usage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints: workerData.dataPoints, min: workerData.min, max: workerData.max, average: workerData.average});
+ }
}
// Private
+ _computeSamplingData(startTime, endTime)
+ {
+ // Compute per-millisecond samples of what the main thread was doing.
+ // We construct an array for every millisecond between the start and end time
+ // and mark each millisecond with the best representation of the work that
+ // was being done at that time. We start by populating the samples with
+ // all of the script periods and then override with layout and rendering
+ // samples. This means a forced layout would be counted as a layout:
+ //
+ // Initial: [ ------, ------, ------, ------, ------ ]
+ // Script Samples: [ ------, Script, Script, Script, ------ ]
+ // Layout Samples: [ ------, Script, Layout, Script, ------ ]
+ //
+ // The undefined samples are considered Idle, but in actuality WebKit
+ // may have been doing some work (such as hit testing / inspector protocol)
+ // that is not included it in generic Timeline data. This just works with
+ // with the data available to the frontend and is quite accurate for most
+ // Main Thread activity.
+
+ const includeRecordBeforeStart = true;
+
+ let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script);
+ let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : [];
+ scriptRecords = scriptRecords.filter((record) => {
+ switch (record.eventType) {
+ case WI.ScriptTimelineRecord.EventType.ScriptEvaluated:
+ case WI.ScriptTimelineRecord.EventType.APIScriptEvaluated:
+ case WI.ScriptTimelineRecord.EventType.ObserverCallback:
+ case WI.ScriptTimelineRecord.EventType.EventDispatched:
+ case WI.ScriptTimelineRecord.EventType.MicrotaskDispatched:
+ case WI.ScriptTimelineRecord.EventType.TimerFired:
+ case WI.ScriptTimelineRecord.EventType.AnimationFrameFired:
+ // These event types define script entry/exits.
+ return true;
+
+ case WI.ScriptTimelineRecord.EventType.AnimationFrameRequested:
+ case WI.ScriptTimelineRecord.EventType.AnimationFrameCanceled:
+ case WI.ScriptTimelineRecord.EventType.TimerInstalled:
+ case WI.ScriptTimelineRecord.EventType.TimerRemoved:
+ case WI.ScriptTimelineRecord.EventType.ProbeSampleRecorded:
+ case WI.ScriptTimelineRecord.EventType.ConsoleProfileRecorded:
+ case WI.ScriptTimelineRecord.EventType.GarbageCollected:
+ // These event types have no time range, or are contained by the others.
+ return false;
+
+ default:
+ console.error("Unhandled ScriptTimelineRecord.EventType", record.eventType);
+ return false;
+ }
+ });
+
+ let layoutTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Layout);
+ let layoutRecords = layoutTimeline ? layoutTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : [];
+ layoutRecords = layoutRecords.filter((record) => {
+ switch (record.eventType) {
+ case WI.LayoutTimelineRecord.EventType.RecalculateStyles:
+ case WI.LayoutTimelineRecord.EventType.ForcedLayout:
+ case WI.LayoutTimelineRecord.EventType.Layout:
+ case WI.LayoutTimelineRecord.EventType.Paint:
+ case WI.LayoutTimelineRecord.EventType.Composite:
+ // These event types define layout and rendering entry/exits.
+ return true;
+
+ case WI.LayoutTimelineRecord.EventType.InvalidateStyles:
+ case WI.LayoutTimelineRecord.EventType.InvalidateLayout:
+ // These event types have no time range.
+ return false;
+
+ default:
+ console.error("Unhandled LayoutTimelineRecord.EventType", record.eventType);
+ return false;
+ }
+ });
+
+ let millisecondStartTime = Math.round(startTime * 1000);
+ let millisecondEndTime = Math.round(endTime * 1000);
+ let millisecondDuration = millisecondEndTime - millisecondStartTime;
+
+ let samples = new Array(millisecondDuration);
+
+ function markRecordEntries(records, callback) {
+ for (let record of records) {
+ let recordStart = Math.round(record.startTime * 1000);
+ let recordEnd = Math.round(record.endTime * 1000);
+ if (recordStart > millisecondEndTime)
+ continue;
+ if (recordEnd < millisecondStartTime)
+ continue;
+
+ let offset = recordStart - millisecondStartTime;
+ recordStart = Math.max(recordStart, millisecondStartTime);
+ recordEnd = Math.min(recordEnd, millisecondEndTime);
+
+ let value = callback(record);
+ for (let t = recordStart; t <= recordEnd; ++t)
+ samples[t - millisecondStartTime] = value;
+ }
+ }
+
+ markRecordEntries(scriptRecords, (record) => {
+ return WI.CPUTimelineView.SampleType.Script;
+ });
+
+ markRecordEntries(layoutRecords, (record) => {
+ switch (record.eventType) {
+ case WI.LayoutTimelineRecord.EventType.RecalculateStyles:
+ return WI.CPUTimelineView.SampleType.Style;
+ case WI.LayoutTimelineRecord.EventType.ForcedLayout:
+ case WI.LayoutTimelineRecord.EventType.Layout:
+ return WI.CPUTimelineView.SampleType.Layout;
+ case WI.LayoutTimelineRecord.EventType.Paint:
+ case WI.LayoutTimelineRecord.EventType.Composite:
+ return WI.CPUTimelineView.SampleType.Paint;
+ }
+ });
+
+ let samplesIdle = 0;
+ let samplesScript = 0;
+ let samplesLayout = 0;
+ let samplesPaint = 0;
+ let samplesStyle = 0;
+ for (let i = 0; i < samples.length; ++i) {
+ switch (samples[i]) {
+ case undefined:
+ samplesIdle++;
+ break;
+ case WI.CPUTimelineView.SampleType.Script:
+ samplesScript++;
+ break;
+ case WI.CPUTimelineView.SampleType.Layout:
+ samplesLayout++;
+ break;
+ case WI.CPUTimelineView.SampleType.Paint:
+ samplesPaint++;
+ break;
+ case WI.CPUTimelineView.SampleType.Style:
+ samplesStyle++;
+ break;
+ }
+ }
+
+ return {
+ samples,
+ samplesIdle,
+ samplesScript,
+ samplesLayout,
+ samplesPaint,
+ samplesStyle,
+ };
+ }
+
+ _removeWorkerThreadViews()
+ {
+ if (!this._workerViews.length)
+ return;
+
+ for (let view of this._workerViews)
+ this.removeSubview(view);
+
+ this._workerViews = [];
+ }
+
+ _clearBreakdownLegend()
+ {
+ this._breakdownLegendScriptElement.textContent = emDash;
+ this._breakdownLegendLayoutElement.textContent = emDash;
+ this._breakdownLegendPaintElement.textContent = emDash;
+ this._breakdownLegendStyleElement.textContent = emDash;
+
+ this._breakdownChart.centerElement.removeChildren();
+ }
+
_cpuTimelineRecordAdded(event)
{
let cpuTimelineRecord = event.data.record;
@@ -196,3 +700,11 @@
this.needsLayout();
}
};
+
+// NOTE: UI follows this order.
+WI.CPUTimelineView.SampleType = {
+ Script: "sample-type-script",
+ Layout: "sample-type-layout",
+ Paint: "sample-type-paint",
+ Style: "sample-type-style",
+};
Copied: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css (from rev 242072, trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.css) (0 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.cpu-usage-stacked-view {
+ display: flex;
+ width: 100%;
+ height: calc(var(--cpu-usage-stacked-view-height) + 1px); /* +1 for border-bottom */
+ border-bottom: 1px solid var(--border-color);
+}
+
+.cpu-usage-stacked-view > .details {
+ flex-shrink: 0;
+ width: 150px;
+ padding-top: 10px;
+ -webkit-padding-start: 15px;
+ font-family: -webkit-system-font, sans-serif;
+ font-size: 12px;
+ color: var(--text-color-secondary);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-border-end: 1px solid var(--border-color);
+}
+
+.cpu-usage-stacked-view > .details > .name {
+ color: var(--text-color);
+ white-space: nowrap;
+}
+
+body[dir=rtl] .cpu-usage-stacked-view > .graph {
+ transform: scaleX(-1);
+}
+
+.cpu-usage-stacked-view > .graph {
+ position: relative;
+}
+
+.cpu-usage-stacked-view > .graph,
+.cpu-usage-stacked-view > .graph > .stacked-line-chart,
+.cpu-usage-stacked-view > .graph > .stacked-line-chart > svg {
+ width: 100%;
+ height: 100%;
+}
Copied: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js (from rev 242072, trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js) (0 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.CPUUsageStackedView = class CPUUsageStackedView extends WI.View
+{
+ constructor(displayName)
+ {
+ super();
+
+ this.element.classList.add("cpu-usage-stacked-view");
+
+ this._detailsElement = this.element.appendChild(document.createElement("div"));
+ this._detailsElement.classList.add("details");
+
+ let detailsNameElement = this._detailsElement.appendChild(document.createElement("span"));
+ detailsNameElement.classList.add("name");
+ detailsNameElement.textContent = displayName;
+
+ this._detailsElement.appendChild(document.createElement("br"));
+ this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span"));
+ this._detailsElement.appendChild(document.createElement("br"));
+ this._detailsMaxElement = this._detailsElement.appendChild(document.createElement("span"));
+ this._detailsElement.appendChild(document.createElement("br"));
+ this._detailsMinElement = this._detailsElement.appendChild(document.createElement("span"));
+ this._updateDetails(NaN, NaN);
+
+ this._graphElement = this.element.appendChild(document.createElement("div"));
+ this._graphElement.classList.add("graph");
+
+ this._chart = new WI.StackedLineChart;
+ this._chart.initializeSections(["main-thread-usage", "worker-thread-usage", "total-usage"]);
+ this.addSubview(this._chart);
+ this._graphElement.appendChild(this._chart.element);
+ }
+
+ // Public
+
+ get chart() { return this._chart; }
+
+ clear()
+ {
+ this._cachedAverageSize = undefined;
+ this._cachedMinSize = undefined;
+ this._cachedMaxSize = undefined;
+ this._updateDetails(NaN, NaN);
+
+ this._chart.clear();
+ this._chart.needsLayout();
+ }
+
+ updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale)
+ {
+ console.assert(size instanceof WI.Size);
+ console.assert(min >= 0);
+ console.assert(max >= 0);
+ console.assert(min <= max);
+ console.assert(min <= average && average <= max);
+
+ this._updateDetails(min, max, average);
+
+ this._chart.clear();
+ this._chart.size = size;
+ this._chart.needsLayout();
+
+ if (!dataPoints.length)
+ return;
+
+ // Ensure an empty graph is empty.
+ if (!max)
+ return;
+
+ // Extend the first data point to the start so it doesn't look like we originate at zero size.
+ let firstX = 0;
+ let firstY1 = yScale(dataPoints[0].mainThreadUsage);
+ let firstY2 = yScale(dataPoints[0].mainThreadUsage + dataPoints[0].workerThreadUsage);
+ let firstY3 = yScale(dataPoints[0].usage);
+ this._chart.addPointSet(firstX, [firstY1, firstY2, firstY3]);
+
+ // Points for data points.
+ for (let dataPoint of dataPoints) {
+ let x = xScale(dataPoint.time);
+ let y1 = yScale(dataPoint.mainThreadUsage);
+ let y2 = yScale(dataPoint.mainThreadUsage + dataPoint.workerThreadUsage);
+ let y3 = yScale(dataPoint.usage)
+ this._chart.addPointSet(x, [y1, y2, y3]);
+ }
+
+ // Extend the last data point to the end time.
+ let lastDataPoint = dataPoints.lastValue;
+ let lastX = Math.floor(xScale(visibleEndTime));
+ let lastY1 = yScale(lastDataPoint.mainThreadUsage);
+ let lastY2 = yScale(lastDataPoint.mainThreadUsage + lastDataPoint.workerThreadUsage);
+ let lastY3 = yScale(lastDataPoint.usage);
+ this._chart.addPointSet(lastX, [lastY1, lastY2, lastY3]);
+ }
+
+ // Private
+
+ _updateDetails(minSize, maxSize, averageSize)
+ {
+ if (this._cachedMinSize === minSize && this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
+ return;
+
+ this._cachedAverageSize = averageSize;
+ this._cachedMinSize = minSize;
+ this._cachedMaxSize = maxSize;
+
+ this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(maxSize) ? Number.percentageString(averageSize / 100) : emDash);
+ this._detailsMaxElement.textContent = WI.UIString("Highest: %s").format(Number.isFinite(maxSize) ? Number.percentageString(maxSize / 100) : emDash);
+ this._detailsMinElement.textContent = WI.UIString("Lowest: %s").format(Number.isFinite(minSize) ? Number.percentageString(minSize / 100) : emDash);
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -26,7 +26,7 @@
.cpu-usage-view {
display: flex;
width: 100%;
- height: 76px; /* Keep this in sync with cpuUsageViewHeight + 1 (for border-bottom) */
+ height: calc(var(--cpu-usage-view-height) + 1px); /* +1 for border-bottom */
border-bottom: 1px solid var(--border-color);
}
@@ -34,26 +34,28 @@
flex-shrink: 0;
width: 150px;
padding-top: 10px;
+ -webkit-padding-start: 15px;
font-family: -webkit-system-font, sans-serif;
font-size: 12px;
color: var(--text-color-secondary);
- -webkit-padding-start: 15px;
-
- --cpu-usage-view-details-border-end: 1px solid var(--border-color);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-border-end: 1px solid var(--border-color);
}
-body[dir=ltr] .cpu-usage-view > .details {
- border-right: var(--cpu-usage-view-details-border-end);
+.cpu-usage-view > .details > .name {
+ color: var(--text-color);
+ white-space: nowrap;
}
-body[dir=rtl] .cpu-usage-view > .details {
- border-left: var(--cpu-usage-view-details-border-end);
-}
-
body[dir=rtl] .cpu-usage-view > .graph {
transform: scaleX(-1);
}
+.cpu-usage-view > .graph {
+ position: relative;
+}
+
.cpu-usage-view > .graph,
.cpu-usage-view > .graph > .line-chart,
.cpu-usage-view > .graph > .line-chart > svg {
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -25,7 +25,7 @@
WI.CPUUsageView = class CPUUsageView extends WI.View
{
- constructor()
+ constructor(displayName)
{
super();
@@ -34,11 +34,16 @@
this._detailsElement = this.element.appendChild(document.createElement("div"));
this._detailsElement.classList.add("details");
+ if (displayName) {
+ let detailsNameElement = this._detailsElement.appendChild(document.createElement("span"));
+ detailsNameElement.classList.add("name");
+ detailsNameElement.textContent = displayName;
+ this._detailsElement.appendChild(document.createElement("br"));
+ }
+
this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span"));
this._detailsElement.appendChild(document.createElement("br"));
this._detailsMaxElement = this._detailsElement.appendChild(document.createElement("span"));
- this._detailsElement.appendChild(document.createElement("br"));
- this._detailsMinElement = this._detailsElement.appendChild(document.createElement("span"));
this._updateDetails(NaN, NaN);
this._graphElement = this.element.appendChild(document.createElement("div"));
@@ -51,10 +56,11 @@
// Public
+ get chart() { return this._chart; }
+
clear()
{
this._cachedAverageSize = undefined;
- this._cachedMinSize = undefined;
this._cachedMaxSize = undefined;
this._updateDetails(NaN, NaN);
@@ -61,7 +67,7 @@
this._chart.clear();
}
- updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale)
+ updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, property)
{
console.assert(size instanceof WI.Size);
console.assert(min >= 0);
@@ -68,6 +74,7 @@
console.assert(max >= 0);
console.assert(min <= max);
console.assert(min <= average && average <= max);
+ console.assert(property, "CPUUsageView needs a property of the dataPoints to graph");
this._updateDetails(min, max, average);
@@ -84,13 +91,13 @@
// Extend the first data point to the start so it doesn't look like we originate at zero size.
let firstX = 0;
- let firstY = yScale(dataPoints[0].size);
+ let firstY = yScale(dataPoints[0][property]);
this._chart.addPoint(firstX, firstY);
// Points for data points.
for (let dataPoint of dataPoints) {
let x = xScale(dataPoint.time);
- let y = yScale(dataPoint.size);
+ let y = yScale(dataPoint[property]);
this._chart.addPoint(x, y);
}
@@ -97,7 +104,7 @@
// Extend the last data point to the end time.
let lastDataPoint = dataPoints.lastValue;
let lastX = Math.floor(xScale(visibleEndTime));
- let lastY = yScale(lastDataPoint.size);
+ let lastY = yScale(lastDataPoint[property]);
this._chart.addPoint(lastX, lastY);
}
@@ -105,15 +112,16 @@
_updateDetails(minSize, maxSize, averageSize)
{
- if (this._cachedMinSize === minSize && this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
+ if (this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)
return;
this._cachedAverageSize = averageSize;
- this._cachedMinSize = minSize;
this._cachedMaxSize = maxSize;
- this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(maxSize) ? Number.percentageString(averageSize / 100) : emDash);
+ this._detailsAverageElement.hidden = !Number.isFinite(averageSize);
+ this._detailsMaxElement.hidden = !Number.isFinite(maxSize);
+
+ this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(averageSize) ? Number.percentageString(averageSize / 100) : emDash);
this._detailsMaxElement.textContent = WI.UIString("Highest: %s").format(Number.isFinite(maxSize) ? Number.percentageString(maxSize / 100) : emDash);
- this._detailsMinElement.textContent = WI.UIString("Lowest: %s").format(Number.isFinite(minSize) ? Number.percentageString(minSize / 100) : emDash);
}
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -65,5 +65,5 @@
.timeline-view.legacy-cpu .cpu-usage-view .line-chart > svg > path {
stroke: var(--cpu-stroke-color);
- fill: var(--cpu-fill-color);
+ fill: var(--cpu-main-thread-fill-color);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -57,6 +57,10 @@
timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this);
}
+ // Static
+
+ static get cpuUsageViewHeight() { return 150; }
+
// Public
shown()
@@ -95,6 +99,11 @@
get showsFilterBar() { return false; }
+ initialLayout()
+ {
+ this.element.style.setProperty("--cpu-usage-view-height", LegacyCPUTimelineView.cpuUsageViewHeight + "px");
+ }
+
layout()
{
if (this.layoutReason === WI.View.LayoutReason.Resize)
@@ -105,8 +114,6 @@
this._timelineRuler.startTime = this.startTime;
this._timelineRuler.endTime = this.endTime;
- const cpuUsageViewHeight = 75; // Keep this in sync with .legacy-cpu-usage-view
-
let graphStartTime = this.startTime;
let graphEndTime = this.endTime;
let secondsPerPixel = this._timelineRuler.secondsPerPixel;
@@ -168,13 +175,13 @@
return (time - graphStartTime) / secondsPerPixel;
}
- let size = new WI.Size(xScale(graphEndTime), cpuUsageViewHeight);
+ let size = new WI.Size(xScale(graphEndTime), LegacyCPUTimelineView.cpuUsageViewHeight);
function yScale(value) {
return size.height - (((value - graphMin) / graphMax) * size.height);
}
- view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale);
+ view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, "size");
}
layoutView(this._cpuUsageView, {dataPoints, min, max, average});
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MemoryCategoryView.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MemoryCategoryView.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MemoryCategoryView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -34,27 +34,20 @@
flex-shrink: 0;
width: 150px;
padding-top: 10px;
+ -webkit-padding-start: 15px;
font-family: -webkit-system-font, sans-serif;
font-size: 12px;
color: var(--text-color-secondary);
- -webkit-padding-start: 15px;
-
- --memory-category-view-details-border-end: 1px solid var(--border-color);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-border-end: 1px solid var(--border-color);
}
-body[dir=ltr] .memory-category-view > .details {
- border-right: var(--memory-category-view-details-border-end);
-}
-
-body[dir=rtl] .memory-category-view > .details {
- border-left: var(--memory-category-view-details-border-end);
-}
-
.memory-category-view > .details > .name {
color: var(--text-color);
+ white-space: nowrap;
}
-
body[dir=rtl] .memory-category-view > .graph {
transform: scaleX(-1);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -210,8 +210,6 @@
this._chart.addPointSet(x, pointSetForRecord(lastRecord));
}
}
-
- this._chart.updateLayout();
}
// Private
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -23,7 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-.timeline-view.memory {
+body .timeline-view.memory {
overflow: scroll;
}
Added: trunk/Source/WebInspectorUI/UserInterface/Views/StackedColumnChart.js (0 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/StackedColumnChart.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/StackedColumnChart.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// StackedColumnChart creates a chart with filled columns each stratified with sections.
+//
+// Initialize the chart with a size.
+// To populate with data, first initialize the sections. The class names you
+// provide for the segments will allow you to style them. You can then include
+// a new set of (x, totalHeight, w, [h1, h2, h3]) points in the chart via `addColumnSet`.
+// The order of `h` values must be in the same order as the sections.
+// The `y` value to be used for each column is `totalHeight - h`.
+//
+// SVG:
+//
+// - There is a single rect for each bar and each section.
+// - Each bar extends all the way down to the bottom, they are layered such
+// that the rects for early sections overlap the sections for later sections.
+//
+// <div class="stacked-column-chart">
+// <svg viewBox="0 0 800 75">
+// <rect class="section-class-name-3" width="<w>" height="<h3>" transform="translate(<x>, <y>)" />
+// <rect class="section-class-name-2" width="<w>" height="<h2>" transform="translate(<x>, <y>)" />
+// <rect class="section-class-name-1" width="<w>" height="<h1>" transform="translate(<x>, <y>)" />
+// ...
+// </svg>
+// </div>
+
+WI.StackedColumnChart = class StackedColumnChart extends WI.View
+{
+ constructor(size)
+ {
+ super();
+
+ this.element.classList.add("stacked-column-chart");
+
+ this._svgElement = this.element.appendChild(createSVGElement("svg"));
+ this._svgElement.setAttribute("preserveAspectRatio", "none");
+
+ this._sections = null;
+ this._columns = [];
+ this.size = size;
+ }
+
+ // Public
+
+ get size()
+ {
+ return this._size;
+ }
+
+ set size(size)
+ {
+ this._size = size;
+
+ this._svgElement.setAttribute("viewBox", `0 0 ${size.width} ${size.height}`);
+ }
+
+ initializeSections(sectionClassNames)
+ {
+ console.assert(this._sections === null, "Should not initialize multiple times");
+
+ this._sections = sectionClassNames;
+ }
+
+ addColumnSet(x, totalHeight, width, heights)
+ {
+ console.assert(heights.length === this._sections.length, "Wrong number of sections in columns set", heights.length, this._sections.length);
+
+ this._columns.push({x, totalHeight, width, heights});
+ }
+
+ clear()
+ {
+ this._columns = [];
+ }
+
+ // Protected
+
+ layout()
+ {
+ super.layout();
+
+ if (this.layoutReason === WI.View.LayoutReason.Resize)
+ return;
+
+ this._svgElement.removeChildren();
+
+ for (let {x, totalHeight, width, heights} of this._columns) {
+ let sectionIndex = 0;
+ for (let i = heights.length - 1; i >= 0; --i) {
+ let height = heights[i];
+ // Next rect will be identical, skip this one.
+ if (height === heights[i - 1])
+ continue;
+ let y = totalHeight - height;
+ let rect = this._svgElement.appendChild(createSVGElement("rect"));
+ rect.classList.add(this._sections[i]);
+ rect.setAttribute("width", width);
+ rect.setAttribute("height", height);
+ rect.setAttribute("transform", `translate(${x}, ${y})`);
+ }
+ }
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css 2019-02-26 06:36:34 UTC (rev 242073)
@@ -129,8 +129,21 @@
--memory-max-comparison-stroke-color: hsl(220, 10%, 55%);
--cpu-stroke-color: hsl(118, 33%, 42%);
- --cpu-fill-color: hsl(118, 43%, 55%);
+ --cpu-fill-color: hsl(81, 80%, 50%);
+ --cpu-main-thread-fill-color: hsl(118, 43%, 55%);
+ --cpu-worker-thread-fill-color: hsl(45, 94.75%, 55%);
+ --cpu-idle-fill-color: hsl(220, 10%, 75%);
+ --cpu-idle-stroke-color: hsl(220, 10%, 55%);
+ --cpu-script-fill-color: hsl(269, 65%, 75%);
+ --cpu-script-stroke-color: hsl(269, 33%, 50%);
+ --cpu-style-fill-color: hsl(22, 60%, 70%);
+ --cpu-style-stroke-color: hsl(22, 40%, 50%);
+ --cpu-layout-fill-color: hsl(0, 65%, 75%);
+ --cpu-layout-stroke-color: hsl(0, 54%, 50%);
+ --cpu-paint-fill-color: hsl(76, 49%, 75%);
+ --cpu-paint-stroke-color: hsl(79, 45%, 50%);
+
--network-header-color: hsl(204, 52%, 55%);
--network-system-color: hsl(79, 32%, 50%);
--network-pseudo-header-color: hsl(312, 35%, 51%);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/View.js (242072 => 242073)
--- trunk/Source/WebInspectorUI/UserInterface/Views/View.js 2019-02-26 06:36:28 UTC (rev 242072)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/View.js 2019-02-26 06:36:34 UTC (rev 242073)
@@ -117,7 +117,7 @@
removeSubview(view)
{
console.assert(view instanceof WI.View);
- console.assert(view.element.parentNode === this._element, "Subview DOM element must be a child of the parent view element.");
+ console.assert(this._element.contains(view.element), "Subview DOM element must be a child of the parent view element.");
let index = this._subviews.lastIndexOf(view);
if (index === -1) {