Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(426)

Side by Side Diff: Source/devtools/front_end/CountersGraph.js

Issue 210053002: DevTools: merge MemoryStatistics.js and CountersGraph.js, extract DOMCountersGraph.js (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/devtools/devtools.gypi ('k') | Source/devtools/front_end/DOMCountersGraph.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer 11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the 12 * in the documentation and/or other materials provided with the
(...skipping 10 matching lines...) Expand all
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 /** 31 /**
32 * @constructor 32 * @constructor
33 * @extends {WebInspector.MemoryStatistics} 33 * @extends {WebInspector.SplitView}
34 * @implements {WebInspector.TimelineModeView}
35 * @param {!WebInspector.TimelineModeViewDelegate} delegate 34 * @param {!WebInspector.TimelineModeViewDelegate} delegate
36 * @param {!WebInspector.TimelineModel} model 35 * @param {!WebInspector.TimelineModel} model
37 */ 36 */
38 WebInspector.CountersGraph = function(delegate, model) 37 WebInspector.CountersGraph = function(delegate, model)
39 { 38 {
40 WebInspector.MemoryStatistics.call(this, delegate, model); 39 WebInspector.SplitView.call(this, true, false);
40
41 this.element.id = "memory-graphs-container";
42
43 this._delegate = delegate;
44 this._model = model;
45 this._calculator = new WebInspector.TimelineCalculator(this._model);
46
47 this._graphsContainer = this.mainElement();
48 this._createCurrentValuesBar();
49 this._canvasView = new WebInspector.VBoxWithResizeCallback(this._resize.bind (this));
50 this._canvasView.show(this._graphsContainer);
51 this._canvasContainer = this._canvasView.element;
52 this._canvasContainer.id = "memory-graphs-canvas-container";
53 this._canvas = this._canvasContainer.createChild("canvas");
54 this._canvas.id = "memory-counters-graph";
55
56 this._canvasContainer.addEventListener("mouseover", this._onMouseMove.bind(t his), true);
57 this._canvasContainer.addEventListener("mousemove", this._onMouseMove.bind(t his), true);
58 this._canvasContainer.addEventListener("mouseout", this._onMouseOut.bind(thi s), true);
59 this._canvasContainer.addEventListener("click", this._onClick.bind(this), tr ue);
60 // We create extra timeline grid here to reuse its event dividers.
61 this._timelineGrid = new WebInspector.TimelineGrid();
62 this._canvasContainer.appendChild(this._timelineGrid.dividersElement);
63
64 // Populate sidebar
65 this.sidebarElement().createChild("div", "sidebar-tree sidebar-tree-section" ).textContent = WebInspector.UIString("COUNTERS");
66 this._counters = [];
67 this._counterUI = [];
41 } 68 }
42 69
43 /**
44 * @constructor
45 * @extends {WebInspector.CounterUIBase}
46 * @param {!WebInspector.CountersGraph} memoryCountersPane
47 * @param {string} title
48 * @param {string} currentValueLabel
49 * @param {!string} color
50 * @param {!WebInspector.MemoryStatistics.Counter} counter
51 */
52 WebInspector.CounterUI = function(memoryCountersPane, title, currentValueLabel, color, counter)
53 {
54 WebInspector.CounterUIBase.call(this, memoryCountersPane, title, color, coun ter)
55 this._range = this._swatch.element.createChild("span");
56
57 this._value = memoryCountersPane._currentValuesBar.createChild("span", "memo ry-counter-value");
58 this._value.style.color = color;
59 this._currentValueLabel = currentValueLabel;
60 this._marker = memoryCountersPane._canvasContainer.createChild("div", "memor y-counter-marker");
61 this._marker.style.backgroundColor = color;
62 this.clearCurrentValueAndMarker();
63
64 this.graphColor = color;
65 this.graphYValues = [];
66 }
67
68 WebInspector.CounterUI.prototype = {
69 reset: function()
70 {
71 this._range.textContent = "";
72 },
73
74 /**
75 * @param {number} minValue
76 * @param {number} maxValue
77 */
78 setRange: function(minValue, maxValue)
79 {
80 this._range.textContent = WebInspector.UIString("[%d:%d]", minValue, max Value);
81 },
82
83 __proto__: WebInspector.CounterUIBase.prototype
84 }
85
86
87 WebInspector.CountersGraph.prototype = { 70 WebInspector.CountersGraph.prototype = {
88 _createCurrentValuesBar: function() 71 _createCurrentValuesBar: function()
89 { 72 {
90 this._currentValuesBar = this._graphsContainer.createChild("div"); 73 this._currentValuesBar = this._graphsContainer.createChild("div");
91 this._currentValuesBar.id = "counter-values-bar"; 74 this._currentValuesBar.id = "counter-values-bar";
92 this._graphsContainer.classList.add("dom-counters");
93 },
94
95 createAllCounters: function()
96 {
97 this._counters = [];
98 this._counterUI = [];
99 this._createCounter(WebInspector.UIString("Documents"), WebInspector.UIS tring("Documents: %d"), "#d00", "documents");
100 this._createCounter(WebInspector.UIString("Nodes"), WebInspector.UIStrin g("Nodes: %d"), "#0a0", "nodes");
101 this._createCounter(WebInspector.UIString("Listeners"), WebInspector.UIS tring("Listeners: %d"), "#00d", "jsEventListeners");
102 if (WebInspector.experimentsSettings.gpuTimeline.isEnabled())
103 this._createCounter(WebInspector.UIString("GPU Memory"), WebInspecto r.UIString("GPU Memory [KB]: %d"), "#c0c", "gpuMemoryUsedKB");
104 }, 75 },
105 76
106 /** 77 /**
107 * @param {string} uiName 78 * @param {string} uiName
108 * @param {string} uiValueTemplate 79 * @param {string} uiValueTemplate
109 * @param {string} color 80 * @param {string} color
110 * @param {string} protocolName 81 * @return {!WebInspector.CountersGraph.Counter}
111 */ 82 */
112 _createCounter: function(uiName, uiValueTemplate, color, protocolName) 83 createCounter: function(uiName, uiValueTemplate, color)
113 { 84 {
114 var counter = new WebInspector.MemoryStatistics.Counter(protocolName); 85 var counter = new WebInspector.CountersGraph.Counter();
115 this._counters.push(counter); 86 this._counters.push(counter);
116 this._counterUI.push(new WebInspector.CounterUI(this, uiName, uiValueTem plate, color, counter)); 87 this._counterUI.push(new WebInspector.CountersGraph.CounterUI(this, uiNa me, uiValueTemplate, color, counter));
117 }, 88 return counter;
118 89 },
119 /** 90
120 * @param {!WebInspector.TimelineModel.Record} record 91 reset: function()
121 */ 92 {
122 addRecord: function(record) 93 for (var i = 0; i < this._counters.length; ++i) {
123 { 94 this._counters[i].reset();
124 /** 95 this._counterUI[i].reset();
125 * @param {!WebInspector.TimelineModel.Record} record 96 }
126 * @this {!WebInspector.CountersGraph} 97 this.refresh();
127 */ 98 },
128 function addStatistics(record) 99
129 { 100 _resize: function()
130 var counters = record.counters; 101 {
131 if (!counters) 102 var parentElement = this._canvas.parentElement;
132 return; 103 this._canvas.width = parentElement.clientWidth;
133 var time = record.endTime || record.startTime; 104 this._canvas.height = parentElement.clientHeight;
134 for (var i = 0; i < this._counters.length; ++i) 105 var timelinePaddingLeft = 15;
135 this._counters[i].appendSample(time, counters); 106 this._calculator.setDisplayWindow(timelinePaddingLeft, this._canvas.widt h);
136 } 107 this.refresh();
137 WebInspector.TimelineModel.forAllRecords([record], null, addStatistics.b ind(this)); 108 },
109
110 /**
111 * @param {number} startTime
112 * @param {number} endTime
113 */
114 setWindowTimes: function(startTime, endTime)
115 {
116 this._calculator.setWindow(startTime, endTime);
138 this.scheduleRefresh(); 117 this.scheduleRefresh();
139 }, 118 },
140 119
120 scheduleRefresh: function()
121 {
122 if (this._refreshTimer)
123 return;
124 this._refreshTimer = setTimeout(this.refresh.bind(this), 300);
125 },
126
141 draw: function() 127 draw: function()
142 { 128 {
143 WebInspector.MemoryStatistics.prototype.draw.call(this); 129 for (var i = 0; i < this._counters.length; ++i) {
130 this._counters[i]._calculateVisibleIndexes(this._calculator);
131 this._counters[i]._calculateXValues(this._canvas.width);
132 }
133 this._clear();
134 this._setVerticalClip(10, this._canvas.height - 20);
135
144 for (var i = 0; i < this._counterUI.length; i++) 136 for (var i = 0; i < this._counterUI.length; i++)
145 this._drawGraph(this._counterUI[i]); 137 this._drawGraph(this._counterUI[i]);
146 }, 138 },
147 139
148 /** 140 /**
149 * @param {!WebInspector.CounterUIBase} counterUI 141 * @param {?Event} event
142 */
143 _onClick: function(event)
144 {
145 var x = event.x - this._canvasContainer.totalOffsetLeft();
146 var minDistance = Infinity;
147 var bestTime;
148 for (var i = 0; i < this._counterUI.length; ++i) {
149 var counterUI = this._counterUI[i];
150 if (!counterUI.counter.times.length)
151 continue;
152 var index = counterUI._recordIndexAt(x);
153 var distance = Math.abs(x - counterUI.counter.x[index]);
154 if (distance < minDistance) {
155 minDistance = distance;
156 bestTime = counterUI.counter.times[index];
157 }
158 }
159 if (bestTime !== undefined)
160 this._revealRecordAt(bestTime);
161 },
162
163 /**
164 * @param {number} time
165 */
166 _revealRecordAt: function(time)
167 {
168 var recordToReveal;
169 function findRecordToReveal(record)
170 {
171 if (record.startTime <= time && time <= record.endTime) {
172 recordToReveal = record;
173 return true;
174 }
175 // If there is no record containing the time than use the latest one before that time.
176 if (!recordToReveal || record.endTime < time && recordToReveal.endTi me < record.endTime)
177 recordToReveal = record;
178 return false;
179 }
180 this._model.forAllRecords(null, findRecordToReveal);
181 this._delegate.selectRecord(recordToReveal);
182 },
183
184 /**
185 * @param {?Event} event
186 */
187 _onMouseOut: function(event)
188 {
189 delete this._markerXPosition;
190 this._clearCurrentValueAndMarker();
191 },
192
193 _clearCurrentValueAndMarker: function()
194 {
195 for (var i = 0; i < this._counterUI.length; i++)
196 this._counterUI[i]._clearCurrentValueAndMarker();
197 },
198
199 /**
200 * @param {?Event} event
201 */
202 _onMouseMove: function(event)
203 {
204 var x = event.x - this._canvasContainer.totalOffsetLeft();
205 this._markerXPosition = x;
206 this._refreshCurrentValues();
207 },
208
209 _refreshCurrentValues: function()
210 {
211 if (this._markerXPosition === undefined)
212 return;
213 for (var i = 0; i < this._counterUI.length; ++i)
214 this._counterUI[i].updateCurrentValue(this._markerXPosition);
215 },
216
217 refresh: function()
218 {
219 delete this._refreshTimer;
220 this._timelineGrid.updateDividers(this._calculator);
221 this.draw();
222 this._refreshCurrentValues();
223 },
224
225 refreshRecords: function()
226 {
227 this.reset();
228 var records = this._model.records();
229 for (var i = 0; i < records.length; ++i)
230 this.addRecord(records[i]);
231 },
232
233 /**
234 * @param {number} originY
235 * @param {number} height
236 */
237 _setVerticalClip: function(originY, height)
238 {
239 this._originY = originY;
240 this._clippedHeight = height;
241 },
242
243 _clear: function()
244 {
245 var ctx = this._canvas.getContext("2d");
246 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
247 },
248
249 /**
250 * @param {?WebInspector.TimelineModel.Record} record
251 * @param {string=} regex
252 * @param {boolean=} selectRecord
253 */
254 highlightSearchResult: function(record, regex, selectRecord)
255 {
256 },
257
258 /**
259 * @param {?WebInspector.TimelineModel.Record} record
260 */
261 setSelectedRecord: function(record)
262 {
263 },
264
265 /**
266 * @param {!WebInspector.CountersGraph.CounterUI} counterUI
150 */ 267 */
151 _drawGraph: function(counterUI) 268 _drawGraph: function(counterUI)
152 { 269 {
153 var canvas = this._canvas; 270 var canvas = this._canvas;
154 var ctx = canvas.getContext("2d"); 271 var ctx = canvas.getContext("2d");
155 var width = canvas.width; 272 var width = canvas.width;
156 var height = this._clippedHeight; 273 var height = this._clippedHeight;
157 var originY = this._originY; 274 var originY = this._originY;
158 var counter = counterUI.counter; 275 var counter = counterUI.counter;
159 var values = counter.values; 276 var values = counter.values;
160 277
161 if (!values.length) 278 if (!values.length)
162 return; 279 return;
163 280
164 var maxValue; 281 var maxValue;
165 var minValue; 282 var minValue;
166 for (var i = counter._minimumIndex; i <= counter._maximumIndex; i++) { 283 for (var i = counter._minimumIndex; i <= counter._maximumIndex; i++) {
167 var value = values[i]; 284 var value = values[i];
168 if (minValue === undefined || value < minValue) 285 if (minValue === undefined || value < minValue)
169 minValue = value; 286 minValue = value;
170 if (maxValue === undefined || value > maxValue) 287 if (maxValue === undefined || value > maxValue)
171 maxValue = value; 288 maxValue = value;
172 } 289 }
173 minValue = minValue || 0; 290 minValue = minValue || 0;
174 maxValue = maxValue || 1; 291 maxValue = maxValue || 1;
175 292
176 counterUI.setRange(minValue, maxValue); 293 counterUI.setRange(minValue, maxValue);
177 294
178 if (!counterUI.visible) 295 if (!counterUI.visible())
179 return; 296 return;
180 297
181 var yValues = counterUI.graphYValues; 298 var yValues = counterUI.graphYValues;
182 yValues.length = this._counters.length; 299 yValues.length = this._counters.length;
183 300
184 var maxYRange = maxValue - minValue; 301 var maxYRange = maxValue - minValue;
185 var yFactor = maxYRange ? height / (maxYRange) : 1; 302 var yFactor = maxYRange ? height / (maxYRange) : 1;
186 303
187 ctx.save(); 304 ctx.save();
188 ctx.translate(0.5, 0.5); 305 ctx.translate(0.5, 0.5);
(...skipping 12 matching lines...) Expand all
201 yValues[i] = currentY; 318 yValues[i] = currentY;
202 } 319 }
203 ctx.lineTo(width, currentY); 320 ctx.lineTo(width, currentY);
204 ctx.lineWidth = 1; 321 ctx.lineWidth = 1;
205 ctx.strokeStyle = counterUI.graphColor; 322 ctx.strokeStyle = counterUI.graphColor;
206 ctx.stroke(); 323 ctx.stroke();
207 ctx.closePath(); 324 ctx.closePath();
208 ctx.restore(); 325 ctx.restore();
209 }, 326 },
210 327
211 __proto__: WebInspector.MemoryStatistics.prototype 328 __proto__: WebInspector.SplitView.prototype
212 } 329 }
213 330
331 /**
332 * @constructor
333 */
334 WebInspector.CountersGraph.Counter = function()
335 {
336 this.times = [];
337 this.values = [];
338 }
339
340 WebInspector.CountersGraph.Counter.prototype = {
341 /**
342 * @param {number} time
343 * @param {number} value
344 */
345 appendSample: function(time, value)
346 {
347 if (this.values.length && this.values.peekLast() === value)
348 return;
349 this.times.push(time);
350 this.values.push(value);
351 },
352
353 reset: function()
354 {
355 this.times = [];
356 this.values = [];
357 },
358
359 /**
360 * @param {!WebInspector.TimelineCalculator} calculator
361 */
362 _calculateVisibleIndexes: function(calculator)
363 {
364 var start = calculator.minimumBoundary();
365 var end = calculator.maximumBoundary();
366
367 // Maximum index of element whose time <= start.
368 this._minimumIndex = Number.constrain(this.times.upperBound(start) - 1, 0, this.times.length - 1);
369
370 // Minimum index of element whose time >= end.
371 this._maximumIndex = Number.constrain(this.times.lowerBound(end), 0, thi s.times.length - 1);
372
373 // Current window bounds.
374 this._minTime = start;
375 this._maxTime = end;
376 },
377
378 /**
379 * @param {number} width
380 */
381 _calculateXValues: function(width)
382 {
383 if (!this.values.length)
384 return;
385
386 var xFactor = width / (this._maxTime - this._minTime);
387
388 this.x = new Array(this.values.length);
389 this.x[this._minimumIndex] = 0;
390 for (var i = this._minimumIndex + 1; i < this._maximumIndex; i++)
391 this.x[i] = xFactor * (this.times[i] - this._minTime);
392 this.x[this._maximumIndex] = width;
393 }
394 }
395
396 /**
397 * @constructor
398 * @param {!WebInspector.CountersGraph} memoryCountersPane
399 * @param {string} title
400 * @param {string} currentValueLabel
401 * @param {string} graphColor
402 * @param {!WebInspector.CountersGraph.Counter} counter
403 */
404 WebInspector.CountersGraph.CounterUI = function(memoryCountersPane, title, curre ntValueLabel, graphColor, counter)
405 {
406 this._memoryCountersPane = memoryCountersPane;
407 this.counter = counter;
408 var container = memoryCountersPane.sidebarElement().createChild("div", "memo ry-counter-sidebar-info");
409 var swatchColor = graphColor;
410 this._swatch = new WebInspector.SwatchCheckbox(WebInspector.UIString(title), swatchColor);
411 this._swatch.addEventListener(WebInspector.SwatchCheckbox.Events.Changed, th is._toggleCounterGraph.bind(this));
412 container.appendChild(this._swatch.element);
413 this._range = this._swatch.element.createChild("span");
414
415 this._value = memoryCountersPane._currentValuesBar.createChild("span", "memo ry-counter-value");
416 this._value.style.color = graphColor;
417 this.graphColor = graphColor;
418 this.strokeColor = graphColor;
419 this.graphYValues = [];
420
421 this._currentValueLabel = currentValueLabel;
422 this._marker = memoryCountersPane._canvasContainer.createChild("div", "memor y-counter-marker");
423 this._marker.style.backgroundColor = graphColor;
424 this._clearCurrentValueAndMarker();
425 }
426
427 WebInspector.CountersGraph.CounterUI.prototype = {
428 reset: function()
429 {
430 this._range.textContent = "";
431 },
432
433 /**
434 * @param {number} minValue
435 * @param {number} maxValue
436 */
437 setRange: function(minValue, maxValue)
438 {
439 this._range.textContent = WebInspector.UIString("[%d:%d]", minValue, max Value);
440 },
441
442 _toggleCounterGraph: function(event)
443 {
444 this._value.classList.toggle("hidden", !this._swatch.checked);
445 this._memoryCountersPane.refresh();
446 },
447
448 /**
449 * @param {number} x
450 * @return {number}
451 */
452 _recordIndexAt: function(x)
453 {
454 return this.counter.x.upperBound(x, null, this.counter._minimumIndex + 1 , this.counter._maximumIndex + 1) - 1;
455 },
456
457 /**
458 * @param {number} x
459 */
460 updateCurrentValue: function(x)
461 {
462 if (!this.visible() || !this.counter.values.length)
463 return;
464 var index = this._recordIndexAt(x);
465 this._value.textContent = WebInspector.UIString(this._currentValueLabel, this.counter.values[index]);
466 var y = this.graphYValues[index];
467 this._marker.style.left = x + "px";
468 this._marker.style.top = y + "px";
469 this._marker.classList.remove("hidden");
470 },
471
472 _clearCurrentValueAndMarker: function()
473 {
474 this._value.textContent = "";
475 this._marker.classList.add("hidden");
476 },
477
478 /**
479 * @return {boolean}
480 */
481 visible: function()
482 {
483 return this._swatch.checked;
484 }
485 }
486
487
488 /**
489 * @constructor
490 * @extends {WebInspector.Object}
491 */
492 WebInspector.SwatchCheckbox = function(title, color)
493 {
494 this.element = document.createElement("div");
495 this._swatch = this.element.createChild("div", "swatch");
496 this.element.createChild("span", "title").textContent = title;
497 this._color = color;
498 this.checked = true;
499
500 this.element.addEventListener("click", this._toggleCheckbox.bind(this), true );
501 }
502
503 WebInspector.SwatchCheckbox.Events = {
504 Changed: "Changed"
505 }
506
507 WebInspector.SwatchCheckbox.prototype = {
508 get checked()
509 {
510 return this._checked;
511 },
512
513 set checked(v)
514 {
515 this._checked = v;
516 if (this._checked)
517 this._swatch.style.backgroundColor = this._color;
518 else
519 this._swatch.style.backgroundColor = "";
520 },
521
522 _toggleCheckbox: function(event)
523 {
524 this.checked = !this.checked;
525 this.dispatchEventToListeners(WebInspector.SwatchCheckbox.Events.Changed );
526 },
527
528 __proto__: WebInspector.Object.prototype
529 }
OLDNEW
« no previous file with comments | « Source/devtools/devtools.gypi ('k') | Source/devtools/front_end/DOMCountersGraph.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698