Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (242103 => 242104)
--- trunk/Source/WebInspectorUI/ChangeLog 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/ChangeLog 2019-02-26 21:40:50 UTC (rev 242104)
@@ -1,3 +1,77 @@
+2019-02-26 Joseph Pecoraro <[email protected]>
+
+ Web Inspector: CPU Usage Timeline - Main Thread Indicator
+ https://bugs.webkit.org/show_bug.cgi?id=194972
+
+ Reviewed by Devin Rousso.
+
+ * UserInterface/Main.html:
+ * UserInterface/Base/Utilities.js:
+ (value):
+ The existing enclosingNode doesn't work for SVG because its names
+ are lowercase. Add a simplified version for the svg case.
+
+ * UserInterface/Views/RangeChart.js: Added.
+ (WI.RangeChart):
+ (WI.RangeChart.prototype.get size):
+ (WI.RangeChart.prototype.set size):
+ (WI.RangeChart.prototype.addRange):
+ (WI.RangeChart.prototype.clear):
+ (WI.RangeChart.prototype.layout):
+ A new chart that draws rects for given ranges.
+
+ * UserInterface/Models/Timeline.js:
+ (WI.Timeline.prototype.recordsOverlappingTimeRange):
+ Helper to specifically get records touching a range. Useful
+ for when we have a single pixel spanning (startTime -> endTime)
+ and we want to find records in that pixel.
+
+ * UserInterface/Views/CPUTimelineView.css:
+ (.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart rect):
+ (.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-script):
+ (.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-style):
+ (.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-layout):
+ (.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-paint):
+ * UserInterface/Views/CPUTimelineView.js:
+ (WI.CPUTimelineView.prototype.get indicatorViewHeight):
+ (WI.CPUTimelineView.prototype.clear):
+ (WI.CPUTimelineView.prototype.get scrollableElements):
+ (WI.CPUTimelineView.prototype.initialLayout):
+ (WI.CPUTimelineView.prototype.layout):
+ (WI.CPUTimelineView.prototype._graphPositionForMouseEvent):
+ (WI.CPUTimelineView.prototype._handleIndicatorClick):
+ (WI.CPUTimelineView.prototype._attemptSelectIndicatatorTimelineRecord):
+ (WI.CPUTimelineView.prototype._selectTimelineRecord):
+ Place the Main Thread Indicator view beneath the big graph.
+ Clicking inside it selects records in the Timeline Overview.
+
+ * UserInterface/Views/CPUUsageIndicatorView.css: Added.
+ (.cpu-usage-indicator-view):
+ (.cpu-usage-indicator-view > .details):
+ (body[dir=ltr] .cpu-usage-indicator-view > .details):
+ (body[dir=rtl] .cpu-usage-indicator-view > .details):
+ (body[dir=rtl] .cpu-usage-indicator-view > .graph):
+ (.cpu-usage-indicator-view > .graph):
+ (.cpu-usage-indicator-view > .graph,):
+ * UserInterface/Views/CPUUsageIndicatorView.js: Added.
+ (WI.CPUUsageIndicatorView):
+ (WI.CPUUsageIndicatorView.prototype.get chart):
+ (WI.CPUUsageIndicatorView.prototype.clear):
+ (WI.CPUUsageIndicatorView.prototype.updateChart):
+ Converts the CPU samples data into a RangeChart. It works to coalesce
+ many samples of the same type into a single range to reduce total ranges.
+
+ * UserInterface/Views/TimelineRecordingContentView.js:
+ (WI.TimelineRecordingContentView):
+ (WI.TimelineRecordingContentView.prototype._recordSelected):
+ (WI.TimelineRecordingContentView.prototype._recordWasSelected):
+ (WI.TimelineRecordingContentView.prototype._selectRecordInTimelineOverview):
+ (WI.TimelineRecordingContentView.prototype._selectRecordInTimelineView):
+ * UserInterface/Views/TimelineView.js:
+ Add a path for a TimelineView to dispatch a record selected event and cause
+ have the TimelineRecordingContentView react to it by updating the timeline
+ overview and relevent timeline view.
+
2019-02-26 Devin Rousso <[email protected]>
Web Inspector: navigation sidebar says "No Search Results" when a slow search is in progress
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -197,11 +197,11 @@
{
value(nodeNames)
{
- let upperCaseNodeNames = nodeNames.map((name) => name.toUpperCase());
+ let lowerCaseNodeNames = nodeNames.map((name) => name.toLowerCase());
for (let node = this; node; node = node.parentElement) {
- for (let nodeName of upperCaseNodeNames) {
- if (node.nodeName === nodeName)
+ for (let nodeName of lowerCaseNodeNames) {
+ if (node.nodeName.toLowerCase() === nodeName)
return node;
}
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-02-26 21:40:50 UTC (rev 242104)
@@ -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=""
@@ -594,6 +595,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
@@ -747,6 +749,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Timeline.js (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Models/Timeline.js 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Timeline.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -93,6 +93,14 @@
this.dispatchEventToListeners(WI.Timeline.Event.Refreshed);
}
+ recordsOverlappingTimeRange(startTime, endTime)
+ {
+ let lowerIndex = this._records.lowerBound(startTime, (time, record) => time - record.endTime);
+ let upperIndex = this._records.upperBound(endTime, (time, record) => time - record.startTime);
+
+ return this._records.slice(lowerIndex, upperIndex);
+ }
+
recordsInTimeRange(startTime, endTime, includeRecordBeforeStart)
{
let lowerIndex = this._records.lowerBound(startTime, (time, record) => time - record.startTime);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css 2019-02-26 21:40:50 UTC (rev 242104)
@@ -214,3 +214,27 @@
color: var(--text-color-secondary);
background-color: var(--background-color-content);
}
+
+.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart rect {
+ stroke-opacity: 0.25;
+}
+
+.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-script {
+ stroke: var(--cpu-script-stroke-color);
+ fill: var(--cpu-script-fill-color);
+}
+
+.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-style {
+ stroke: var(--cpu-style-stroke-color);
+ fill: var(--cpu-style-fill-color);
+}
+
+.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-layout {
+ stroke: var(--cpu-layout-stroke-color);
+ fill: var(--cpu-layout-fill-color);
+}
+
+.timeline-view.cpu .cpu-usage-indicator-view > .graph > .range-chart .sample-type-paint {
+ stroke: var(--cpu-paint-stroke-color);
+ fill: var(--cpu-paint-fill-color);
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -57,6 +57,7 @@
static get cpuUsageViewHeight() { return 150; }
static get threadCPUUsageViewHeight() { return 65; }
+ static get indicatorViewHeight() { return 15; }
// Public
@@ -104,21 +105,24 @@
clearUsageView(this._unknownThreadUsageView);
this._removeWorkerThreadViews();
+
+ this._mainThreadWorkIndicatorView.clear();
}
+ // Protected
+
+ get showsFilterBar() { return false; }
+
get scrollableElements()
{
return [this.element];
}
- // Protected
-
- 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");
+ this.element.style.setProperty("--cpu-usage-indicator-view-height", CPUTimelineView.indicatorViewHeight + "px");
let contentElement = this.element.appendChild(document.createElement("div"));
contentElement.classList.add("content");
@@ -197,6 +201,12 @@
this.addSubview(this._cpuUsageView);
this._detailsContainerElement.appendChild(this._cpuUsageView.element);
+ this._mainThreadWorkIndicatorView = new WI.CPUUsageIndicatorView;
+ this.addSubview(this._mainThreadWorkIndicatorView);
+ this._detailsContainerElement.appendChild(this._mainThreadWorkIndicatorView.element);
+
+ this._mainThreadWorkIndicatorView.chart.element.addEventListener("click", this._handleIndicatorClick.bind(this));
+
let threadsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div"));
threadsSubtitleElement.classList.add("subtitle", "threads");
threadsSubtitleElement.textContent = WI.UIString("Threads");
@@ -515,6 +525,14 @@
layoutView(workerView, "usage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints: workerData.dataPoints, min: workerData.min, max: workerData.max, average: workerData.average});
}
+
+ function xScaleIndicatorRange(sampleIndex) {
+ return (sampleIndex / 1000) / secondsPerPixel;
+ }
+
+ let graphWidth = (graphEndTime - graphStartTime) / secondsPerPixel;
+ let size = new WI.Size(graphWidth, CPUTimelineView.indicatorViewHeight);
+ this._mainThreadWorkIndicatorView.updateChart(samplingData.samples, size, visibleEndTime, xScaleIndicatorRange);
}
// Private
@@ -699,6 +717,115 @@
if (cpuTimelineRecord.startTime >= this.startTime && cpuTimelineRecord.endTime <= this.endTime)
this.needsLayout();
}
+
+ _graphPositionForMouseEvent(event)
+ {
+ let svgElement = event.target.enclosingNodeOrSelfWithNodeName("svg");
+ if (!svgElement)
+ return NaN;
+
+ let svgRect = svgElement.getBoundingClientRect();
+ let position = event.pageX - svgRect.left;
+
+ if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL)
+ return svgRect.width - position;
+ return position;
+ }
+
+ _handleIndicatorClick(event)
+ {
+ let clickPosition = this._graphPositionForMouseEvent(event);
+ if (isNaN(clickPosition))
+ return;
+
+ let secondsPerPixel = this._timelineRuler.secondsPerPixel;
+ let graphClickTime = clickPosition * secondsPerPixel;
+ let graphStartTime = this.startTime;
+
+ let clickStartTime = graphStartTime + graphClickTime;
+ let clickEndTime = clickStartTime + secondsPerPixel;
+
+ // Try at the exact clicked pixel.
+ if (event.target.localName === "rect") {
+ if (this._attemptSelectIndicatatorTimelineRecord(clickStartTime, clickEndTime))
+ return;
+ console.assert(false, "If the user clicked on a rect there should have been a record in this pixel range");
+ }
+
+ // Spiral out 4 pixels each side to try and select a nearby record.
+ for (let i = 1, delta = 0; i <= 4; ++i) {
+ delta += secondsPerPixel;
+ if (this._attemptSelectIndicatatorTimelineRecord(clickStartTime - delta, clickStartTime))
+ return;
+ if (this._attemptSelectIndicatatorTimelineRecord(clickEndTime, clickEndTime + delta))
+ return;
+ }
+ }
+
+ _attemptSelectIndicatatorTimelineRecord(startTime, endTime)
+ {
+ let layoutTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Layout);
+ let layoutRecords = layoutTimeline ? layoutTimeline.recordsOverlappingTimeRange(startTime, endTime) : [];
+ 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:
+ return true;
+ case WI.LayoutTimelineRecord.EventType.InvalidateStyles:
+ case WI.LayoutTimelineRecord.EventType.InvalidateLayout:
+ return false;
+ default:
+ console.error("Unhandled LayoutTimelineRecord.EventType", record.eventType);
+ return false;
+ }
+ });
+
+ if (layoutRecords.length) {
+ this._selectTimelineRecord(layoutRecords[0]);
+ return true;
+ }
+
+ let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script);
+ let scriptRecords = scriptTimeline ? scriptTimeline.recordsOverlappingTimeRange(startTime, endTime) : [];
+ 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:
+ 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:
+ return false;
+ default:
+ console.error("Unhandled ScriptTimelineRecord.EventType", record.eventType);
+ return false;
+ }
+ });
+
+ if (scriptRecords.length) {
+ this._selectTimelineRecord(scriptRecords[0]);
+ return true;
+ }
+
+ return false;
+ }
+
+ _selectTimelineRecord(record)
+ {
+ this.dispatchEventToListeners(WI.TimelineView.Event.RecordWasSelected, {record});
+ }
};
// NOTE: UI follows this order.
Added: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.css (0 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.css (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.css 2019-02-26 21:40:50 UTC (rev 242104)
@@ -0,0 +1,55 @@
+/*
+ * 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-indicator-view {
+ display: flex;
+ width: 100%;
+ height: calc(var(--cpu-usage-indicator-view-height) + 1px); /* 1 for border-bottom */
+ border-bottom: 1px solid var(--border-color);
+}
+
+.cpu-usage-indicator-view > .details {
+ flex-shrink: 0;
+ width: 150px;
+ -webkit-padding-start: 15px;
+ -webkit-border-end: 1px solid var(--border-color);
+}
+
+body[dir=rtl] .cpu-usage-indicator-view > .graph {
+ transform: scaleX(-1);
+}
+
+.cpu-usage-indicator-view > .graph {
+ position: relative;
+ z-index: calc(var(--timeline-marker-z-index) + 1);
+ background-color: var(--background-color-content);
+}
+
+.cpu-usage-indicator-view > .graph,
+.cpu-usage-indicator-view > .graph > .range-chart,
+.cpu-usage-indicator-view > .graph > .range-chart > svg {
+ width: 100%;
+ height: 100%;
+}
Added: trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.js (0 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageIndicatorView.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -0,0 +1,109 @@
+/*
+ * 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.CPUUsageIndicatorView = class CPUUsageIndicatorView extends WI.View
+{
+ constructor(delegate)
+ {
+ super();
+
+ this.element.classList.add("cpu-usage-indicator-view");
+
+ this._detailsElement = this.element.appendChild(document.createElement("div"));
+ this._detailsElement.classList.add("details");
+
+ this._graphElement = this.element.appendChild(document.createElement("div"));
+ this._graphElement.classList.add("graph");
+
+ this._chart = new WI.RangeChart;
+ this.addSubview(this._chart);
+ this._graphElement.appendChild(this._chart.element);
+ }
+
+ // Public
+
+ get chart() { return this._chart; }
+
+ clear()
+ {
+ this._chart.clear();
+ this._chart.needsLayout();
+ }
+
+ updateChart(samples, size, visibleEndTime, xScale)
+ {
+ console.assert(size instanceof WI.Size);
+
+ this._chart.clear();
+ this._chart.size = size;
+ this._chart.needsLayout();
+
+ if (!samples.length)
+ return;
+
+ // Coalesce ranges of samples.
+ let ranges = [];
+ let currentRange = null;
+ let currentSampleType = undefined;
+ for (let i = 0; i < samples.length; ++i) {
+ // Back to idle, close any current chunk.
+ let type = samples[i];
+ if (!type) {
+ if (currentRange) {
+ ranges.push(currentRange);
+ currentRange = null;
+ currentSampleType = undefined;
+ }
+ continue;
+ }
+
+ // Expand existing chunk.
+ if (type === currentSampleType) {
+ currentRange.endIndex = i;
+ continue;
+ }
+
+ // If type changed, close current chunk.
+ if (currentSampleType) {
+ ranges.push(currentRange);
+ currentRange = null;
+ currentSampleType = undefined;
+ }
+
+ // Start a new chunk.
+ console.assert(!currentRange);
+ console.assert(!currentSampleType);
+ currentRange = {type, startIndex: i, endIndex: i};
+ currentSampleType = type;
+ }
+
+ for (let {type, startIndex, endIndex} of ranges) {
+ let startX = xScale(startIndex);
+ let endX = xScale(endIndex + 1);
+ let width = endX - startX;
+ this._chart.addRange(startX, width, type);
+ }
+ }
+};
Added: trunk/Source/WebInspectorUI/UserInterface/Views/RangeChart.js (0 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RangeChart.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RangeChart.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+// RangeChart creates a chart filled with ranges of equal height.
+//
+// [ |------|------| |------| ]
+//
+// Initialize the chart with a size. You can then include a new range
+// in the chart by providing an (x, w, class) via `addRange`.
+//
+// SVG:
+//
+// - There is a single rect for each range.
+//
+// <div class="range-chart">
+// <svg viewBox="0 0 800 75">
+// <rect width="<w>" height="100%" transform="translateX(<x>)" class="<class>" />
+// <rect width="<w>" height="100%" transform="translateX(<x>)" class="<class>" />
+// ...
+// </svg>
+// </div>
+
+WI.RangeChart = class RangeChart extends WI.View
+{
+ constructor()
+ {
+ super();
+
+ this.element.classList.add("range-chart");
+
+ this._svgElement = this.element.appendChild(createSVGElement("svg"));
+ this._svgElement.setAttribute("preserveAspectRatio", "none");
+
+ this._ranges = [];
+ this._size = null;
+ }
+
+ // Public
+
+ get size()
+ {
+ return this._size;
+ }
+
+ set size(size)
+ {
+ if (this._size && this._size.equals(size))
+ return;
+
+ this._size = size;
+
+ this._svgElement.setAttribute("viewBox", `0 0 ${size.width} ${size.height}`);
+ }
+
+ addRange(x, width, className)
+ {
+ this._ranges.push({x, width, className});
+ }
+
+ clear()
+ {
+ this._ranges = [];
+ }
+
+ // Protected
+
+ layout()
+ {
+ super.layout();
+
+ if (this.layoutReason === WI.View.LayoutReason.Resize)
+ return;
+
+ if (!this._size)
+ return;
+
+ this._svgElement.removeChildren();
+
+ let h = 0;
+ for (let {x, width, className} of this._ranges) {
+ let rect = this._svgElement.appendChild(createSVGElement("rect"));
+ rect.setAttribute("width", width);
+ rect.setAttribute("height", this.size.height);
+ rect.setAttribute("transform", `translate(${x}, 0)`);
+ rect.classList.add(className);
+ }
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -96,7 +96,8 @@
WI.ContentView.addEventListener(WI.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
WI.ContentView.addEventListener(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._contentViewSupplementalRepresentedObjectsDidChange, this);
- WI.TimelineView.addEventListener(WI.TimelineView.Event.RecordWasFiltered, this._recordWasFiltered, this);
+ WI.TimelineView.addEventListener(WI.TimelineView.Event.RecordWasFiltered, this._handleTimelineViewRecordFiltered, this);
+ WI.TimelineView.addEventListener(WI.TimelineView.Event.RecordWasSelected, this._handleTimelineViewRecordSelected, this);
WI.notifications.addEventListener(WI.Notification.VisibilityStateDidChange, this._inspectorVisibilityStateChanged, this);
@@ -700,15 +701,7 @@
{
let {record} = event.data;
- for (let timelineView of this._timelineViewMap.values()) {
- let recordMatchesTimeline = record && timelineView.representedObject.type === record.type;
-
- if (recordMatchesTimeline && timelineView !== this.currentTimelineView)
- this.showTimelineViewForTimeline(timelineView.representedObject);
-
- if (!record || recordMatchesTimeline)
- timelineView.selectRecord(record);
- }
+ this._selectRecordInTimelineView(record);
}
_timelineSelected()
@@ -804,7 +797,7 @@
this.currentTimelineView.updateFilter(this._filterBarNavigationItem.filterBar.filters);
}
- _recordWasFiltered(event)
+ _handleTimelineViewRecordFiltered(event)
{
if (event.target !== this.currentTimelineView)
return;
@@ -820,6 +813,39 @@
this._timelineOverview.recordWasFiltered(timeline, record, filtered);
}
+ _handleTimelineViewRecordSelected(event)
+ {
+ if (!this.visible)
+ return;
+
+ let {record} = event.data;
+
+ this._selectRecordInTimelineOverview(record);
+ this._selectRecordInTimelineView(record);
+ }
+
+ _selectRecordInTimelineOverview(record)
+ {
+ let timeline = this._recording.timelineForRecordType(record.type);
+ if (!timeline)
+ return;
+
+ this._timelineOverview.selectRecord(timeline, record);
+ }
+
+ _selectRecordInTimelineView(record)
+ {
+ for (let timelineView of this._timelineViewMap.values()) {
+ let recordMatchesTimeline = record && timelineView.representedObject.type === record.type;
+
+ if (recordMatchesTimeline && timelineView !== this.currentTimelineView)
+ this.showTimelineViewForTimeline(timelineView.representedObject);
+
+ if (!record || recordMatchesTimeline)
+ timelineView.selectRecord(record);
+ }
+ }
+
_updateProgressView()
{
let isCapturing = WI.timelineManager.isCapturing();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js (242103 => 242104)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js 2019-02-26 21:40:48 UTC (rev 242103)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js 2019-02-26 21:40:50 UTC (rev 242104)
@@ -335,5 +335,6 @@
};
WI.TimelineView.Event = {
- RecordWasFiltered: "record-was-filtered"
+ RecordWasFiltered: "timeline-view-record-was-filtered",
+ RecordWasSelected: "timeline-view-record-was-selected",
};