Web Inspector: CPU Usage Timeline - Add legend and graph hover effects
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / CPUUsageView.js
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.CPUUsageView = class CPUUsageView extends WI.View
27 {
28     constructor(displayName)
29     {
30         super();
31
32         this.element.classList.add("cpu-usage-view");
33
34         this._detailsElement = this.element.appendChild(document.createElement("div"));
35         this._detailsElement.classList.add("details");
36
37         if (displayName) {
38             let detailsNameElement = this._detailsElement.appendChild(document.createElement("span"));
39             detailsNameElement.classList.add("name");
40             detailsNameElement.textContent = displayName;
41             this._detailsElement.appendChild(document.createElement("br"));
42         }
43
44         this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span"));
45         this._detailsElement.appendChild(document.createElement("br"));
46         this._detailsUsageElement = this._detailsElement.appendChild(document.createElement("span"));
47
48         this.clearLegend();
49         this._updateDetails(NaN);
50
51         this._graphElement = this.element.appendChild(document.createElement("div"));
52         this._graphElement.classList.add("graph");
53
54         this._chart = new WI.AreaChart;
55         this.addSubview(this._chart);
56         this._graphElement.appendChild(this._chart.element);
57     }
58
59     // Public
60
61     get graphElement() { return this._graphElement; }
62     get chart() { return this._chart; }
63
64     clear()
65     {
66         this._cachedAverageSize = undefined;
67         this._updateDetails(NaN);
68
69         this.clearLegend();
70
71         this._chart.clear();
72         this._chart.needsLayout();
73     }
74
75     updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, property)
76     {
77         console.assert(size instanceof WI.Size);
78         console.assert(min >= 0);
79         console.assert(max >= 0);
80         console.assert(min <= max);
81         console.assert(min <= average && average <= max);
82         console.assert(property, "CPUUsageView needs a property of the dataPoints to graph");
83
84         this._updateDetails(average);
85
86         this._chart.clearPoints();
87         this._chart.size = size;
88         this._chart.needsLayout();
89
90         if (!dataPoints.length)
91             return;
92
93         // Ensure an empty graph is empty.
94         if (!max)
95             return;
96
97         // Extend the first data point to the start so it doesn't look like we originate at zero size.
98         let firstX = 0;
99         let firstY = yScale(dataPoints[0][property]);
100         this._chart.addPoint(firstX, firstY);
101
102         // Points for data points.
103         for (let dataPoint of dataPoints) {
104             let x = xScale(dataPoint.time);
105             let y = yScale(dataPoint[property]);
106             this._chart.addPoint(x, y);
107         }
108
109         // Extend the last data point to the end time.
110         let lastDataPoint = dataPoints.lastValue;
111         let lastX = Math.floor(xScale(visibleEndTime));
112         let lastY = yScale(lastDataPoint[property]);
113         this._chart.addPoint(lastX, lastY);
114     }
115
116     clearLegend()
117     {
118         this._detailsUsageElement.hidden = true;
119         this._detailsUsageElement.textContent = emDash;
120     }
121
122     updateLegend(value)
123     {
124         let usage = Number.isFinite(value) ? Number.percentageString(value / 100) : emDash;
125
126         this._detailsUsageElement.hidden = false;
127         this._detailsUsageElement.textContent = WI.UIString("Usage: %s").format(usage);
128     }
129
130     // Private
131
132     _updateDetails(averageSize)
133     {
134         if (this._cachedAverageSize === averageSize)
135             return;
136
137         this._cachedAverageSize = averageSize;
138
139         this._detailsAverageElement.hidden = !Number.isFinite(averageSize);
140         this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(averageSize) ? Number.percentageString(averageSize / 100) : emDash);
141     }
142 };