OLD | NEW |
1 /** | 1 /** |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 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 |
(...skipping 27 matching lines...) Expand all Loading... |
38 * @param {number} startTime | 38 * @param {number} startTime |
39 * @param {number} endTime | 39 * @param {number} endTime |
40 */ | 40 */ |
41 requestWindowTimes: function(startTime, endTime) { }, | 41 requestWindowTimes: function(startTime, endTime) { }, |
42 } | 42 } |
43 | 43 |
44 /** | 44 /** |
45 * @constructor | 45 * @constructor |
46 * @extends {WebInspector.VBox} | 46 * @extends {WebInspector.VBox} |
47 * @param {!WebInspector.FlameChartDataProvider} dataProvider | 47 * @param {!WebInspector.FlameChartDataProvider} dataProvider |
| 48 * @param {!WebInspector.FlameChartDelegate} flameChartDelegate |
| 49 * @param {boolean} isTopDown |
| 50 * @param {boolean} timeBasedWindow |
48 */ | 51 */ |
49 WebInspector.FlameChart = function(dataProvider) | 52 WebInspector.FlameChart = function(dataProvider, flameChartDelegate, isTopDown,
timeBasedWindow) |
50 { | 53 { |
51 WebInspector.VBox.call(this); | 54 WebInspector.VBox.call(this); |
52 this.registerRequiredCSS("flameChart.css"); | 55 this.element.classList.add("flame-chart-main-pane"); |
53 this.element.id = "cpu-flame-chart"; | 56 this._flameChartDelegate = flameChartDelegate; |
| 57 this._isTopDown = isTopDown; |
| 58 this._timeBasedWindow = timeBasedWindow; |
54 | 59 |
55 this._overviewPane = new WebInspector.FlameChart.OverviewPane(dataProvider); | 60 this._calculator = new WebInspector.FlameChart.Calculator(); |
56 this._overviewPane.show(this.element); | |
57 | 61 |
58 this._mainPane = new WebInspector.FlameChart.MainPane(dataProvider, this._ov
erviewPane, true, false); | 62 this._canvas = this.element.createChild("canvas"); |
59 this._mainPane.show(this.element); | 63 this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this)); |
60 this._mainPane.addEventListener(WebInspector.FlameChart.Events.EntrySelected
, this._onEntrySelected, this); | 64 this._canvas.addEventListener("mousewheel", this._onMouseWheel.bind(this), f
alse); |
61 this._overviewPane._overviewGrid.addEventListener(WebInspector.OverviewGrid.
Events.WindowChanged, this._onWindowChanged, this); | 65 this._canvas.addEventListener("click", this._onClick.bind(this), false); |
| 66 WebInspector.installDragHandle(this._canvas, this._startCanvasDragging.bind(
this), this._canvasDragging.bind(this), this._endCanvasDragging.bind(this), "mov
e", null); |
| 67 |
| 68 this._entryInfo = this.element.createChild("div", "profile-entry-info"); |
| 69 this._highlightElement = this.element.createChild("div", "flame-chart-highli
ght-element"); |
| 70 this._selectedElement = this.element.createChild("div", "flame-chart-selecte
d-element"); |
| 71 |
| 72 this._dataProvider = dataProvider; |
| 73 |
| 74 this._windowLeft = 0.0; |
| 75 this._windowRight = 1.0; |
| 76 this._windowWidth = 1.0; |
| 77 this._timeWindowLeft = 0; |
| 78 this._timeWindowRight = Infinity; |
| 79 this._barHeight = dataProvider.barHeight(); |
| 80 this._barHeightDelta = this._isTopDown ? -this._barHeight : this._barHeight; |
| 81 this._minWidth = 1; |
| 82 this._paddingLeft = this._dataProvider.paddingLeft(); |
| 83 this._highlightedEntryIndex = -1; |
| 84 this._selectedEntryIndex = -1; |
| 85 this._textWidth = {}; |
62 } | 86 } |
63 | 87 |
64 WebInspector.FlameChart.DividersBarHeight = 20; | 88 WebInspector.FlameChart.DividersBarHeight = 20; |
65 | 89 |
66 WebInspector.FlameChart.prototype = { | |
67 /** | |
68 * @param {!WebInspector.Event} event | |
69 */ | |
70 _onWindowChanged: function(event) | |
71 { | |
72 this._mainPane.changeWindow(this._overviewPane._overviewGrid.windowLeft(
), this._overviewPane._overviewGrid.windowRight()); | |
73 }, | |
74 | |
75 /** | |
76 * @param {!number} timeLeft | |
77 * @param {!number} timeRight | |
78 */ | |
79 selectRange: function(timeLeft, timeRight) | |
80 { | |
81 this._overviewPane._selectRange(timeLeft, timeRight); | |
82 }, | |
83 | |
84 /** | |
85 * @param {!WebInspector.Event} event | |
86 */ | |
87 _onEntrySelected: function(event) | |
88 { | |
89 this.dispatchEventToListeners(WebInspector.FlameChart.Events.EntrySelect
ed, event.data); | |
90 }, | |
91 | |
92 update: function() | |
93 { | |
94 this._overviewPane.update(); | |
95 this._mainPane.update(); | |
96 }, | |
97 | |
98 __proto__: WebInspector.VBox.prototype | |
99 }; | |
100 | |
101 /** | 90 /** |
102 * @interface | 91 * @interface |
103 */ | 92 */ |
104 WebInspector.FlameChartDataProvider = function() | 93 WebInspector.FlameChartDataProvider = function() |
105 { | 94 { |
106 } | 95 } |
107 | 96 |
108 /** @typedef {!{ | 97 /** @typedef {!{ |
109 entryLevels: !Array.<number>, | 98 entryLevels: !Array.<number>, |
110 entryTotalTimes: !Array.<number>, | 99 entryTotalTimes: !Array.<number>, |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 * @return {?{startTimeOffset: number, endTimeOffset: number}} | 204 * @return {?{startTimeOffset: number, endTimeOffset: number}} |
216 */ | 205 */ |
217 highlightTimeRange: function(entryIndex) { }, | 206 highlightTimeRange: function(entryIndex) { }, |
218 | 207 |
219 /** | 208 /** |
220 * @return {number} | 209 * @return {number} |
221 */ | 210 */ |
222 paddingLeft: function() { } | 211 paddingLeft: function() { } |
223 } | 212 } |
224 | 213 |
| 214 WebInspector.FlameChart.Events = { |
| 215 EntrySelected: "EntrySelected" |
| 216 } |
| 217 |
225 /** | 218 /** |
226 * @constructor | 219 * @constructor |
227 * @implements {WebInspector.TimelineGrid.Calculator} | 220 * @implements {WebInspector.TimelineGrid.Calculator} |
228 */ | 221 */ |
229 WebInspector.FlameChart.Calculator = function() | 222 WebInspector.FlameChart.Calculator = function() |
230 { | 223 { |
231 this._paddingLeft = 0; | 224 this._paddingLeft = 0; |
232 } | 225 } |
233 | 226 |
234 WebInspector.FlameChart.Calculator.prototype = { | 227 WebInspector.FlameChart.Calculator.prototype = { |
235 /** | 228 /** |
236 * @return {number} | 229 * @return {number} |
237 */ | 230 */ |
238 paddingLeft: function() | 231 paddingLeft: function() |
239 { | 232 { |
240 return this._paddingLeft; | 233 return this._paddingLeft; |
241 }, | 234 }, |
242 | 235 |
243 /** | 236 /** |
244 * @param {!WebInspector.FlameChart.MainPane} mainPane | 237 * @param {!WebInspector.FlameChart} mainPane |
245 */ | 238 */ |
246 _updateBoundaries: function(mainPane) | 239 _updateBoundaries: function(mainPane) |
247 { | 240 { |
248 this._totalTime = mainPane._dataProvider.totalTime(); | 241 this._totalTime = mainPane._dataProvider.totalTime(); |
249 this._zeroTime = mainPane._dataProvider.zeroTime(); | 242 this._zeroTime = mainPane._dataProvider.zeroTime(); |
250 this._minimumBoundaries = this._zeroTime + mainPane._windowLeft * this._
totalTime; | 243 this._minimumBoundaries = this._zeroTime + mainPane._windowLeft * this._
totalTime; |
251 this._maximumBoundaries = this._zeroTime + mainPane._windowRight * this.
_totalTime; | 244 this._maximumBoundaries = this._zeroTime + mainPane._windowRight * this.
_totalTime; |
252 this._paddingLeft = mainPane._paddingLeft; | 245 this._paddingLeft = mainPane._paddingLeft; |
253 this._width = mainPane._canvas.width / window.devicePixelRatio - this._p
addingLeft; | 246 this._width = mainPane._canvas.width / window.devicePixelRatio - this._p
addingLeft; |
254 this._timeToPixel = this._width / this.boundarySpan(); | 247 this._timeToPixel = this._width / this.boundarySpan(); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 | 292 |
300 /** | 293 /** |
301 * @return {number} | 294 * @return {number} |
302 */ | 295 */ |
303 boundarySpan: function() | 296 boundarySpan: function() |
304 { | 297 { |
305 return this._maximumBoundaries - this._minimumBoundaries; | 298 return this._maximumBoundaries - this._minimumBoundaries; |
306 } | 299 } |
307 } | 300 } |
308 | 301 |
309 /** | 302 WebInspector.FlameChart.prototype = { |
310 * @constructor | |
311 * @implements {WebInspector.TimelineGrid.Calculator} | |
312 */ | |
313 WebInspector.FlameChart.OverviewCalculator = function() | |
314 { | |
315 } | |
316 | |
317 WebInspector.FlameChart.OverviewCalculator.prototype = { | |
318 /** | |
319 * @return {number} | |
320 */ | |
321 paddingLeft: function() | |
322 { | |
323 return 0; | |
324 }, | |
325 | |
326 /** | |
327 * @param {!WebInspector.FlameChart.OverviewPane} overviewPane | |
328 */ | |
329 _updateBoundaries: function(overviewPane) | |
330 { | |
331 this._minimumBoundaries = 0; | |
332 var totalTime = overviewPane._dataProvider.totalTime(); | |
333 this._maximumBoundaries = totalTime; | |
334 this._xScaleFactor = overviewPane._overviewCanvas.width / totalTime; | |
335 }, | |
336 | |
337 /** | |
338 * @param {number} time | |
339 * @return {number} | |
340 */ | |
341 computePosition: function(time) | |
342 { | |
343 return (time - this._minimumBoundaries) * this._xScaleFactor; | |
344 }, | |
345 | |
346 /** | |
347 * @param {number} value | |
348 * @param {number=} precision | |
349 * @return {string} | |
350 */ | |
351 formatTime: function(value, precision) | |
352 { | |
353 return Number.secondsToString((value + this._minimumBoundaries) / 1000); | |
354 }, | |
355 | |
356 /** | |
357 * @return {number} | |
358 */ | |
359 maximumBoundary: function() | |
360 { | |
361 return this._maximumBoundaries; | |
362 }, | |
363 | |
364 /** | |
365 * @return {number} | |
366 */ | |
367 minimumBoundary: function() | |
368 { | |
369 return this._minimumBoundaries; | |
370 }, | |
371 | |
372 /** | |
373 * @return {number} | |
374 */ | |
375 zeroTime: function() | |
376 { | |
377 return this._minimumBoundaries; | |
378 }, | |
379 | |
380 /** | |
381 * @return {number} | |
382 */ | |
383 boundarySpan: function() | |
384 { | |
385 return this._maximumBoundaries - this._minimumBoundaries; | |
386 } | |
387 } | |
388 | |
389 WebInspector.FlameChart.Events = { | |
390 EntrySelected: "EntrySelected" | |
391 } | |
392 | |
393 /** | |
394 * @constructor | |
395 */ | |
396 WebInspector.FlameChart.ColorGenerator = function() | |
397 { | |
398 this._colors = {}; | |
399 this._currentColorIndex = 0; | |
400 } | |
401 | |
402 WebInspector.FlameChart.ColorGenerator.prototype = { | |
403 /** | |
404 * @param {string} id | |
405 * @param {string|!CanvasGradient} color | |
406 */ | |
407 setColorForID: function(id, color) | |
408 { | |
409 this._colors[id] = color; | |
410 }, | |
411 | |
412 /** | |
413 * @param {!string} id | |
414 * @param {number=} sat | |
415 * @return {!string} | |
416 */ | |
417 colorForID: function(id, sat) | |
418 { | |
419 if (typeof sat !== "number") | |
420 sat = 100; | |
421 var color = this._colors[id]; | |
422 if (!color) { | |
423 color = this._createColor(this._currentColorIndex++, sat); | |
424 this._colors[id] = color; | |
425 } | |
426 return color; | |
427 }, | |
428 | |
429 /** | |
430 * @param {!number} index | |
431 * @param {!number} sat | |
432 */ | |
433 _createColor: function(index, sat) | |
434 { | |
435 var hue = (index * 7 + 12 * (index % 2)) % 360; | |
436 return "hsla(" + hue + ", " + sat + "%, 66%, 0.7)"; | |
437 } | |
438 } | |
439 | |
440 /** | |
441 * @constructor | |
442 * @extends {WebInspector.VBox} | |
443 * @implements {WebInspector.FlameChartDelegate} | |
444 * @param {!WebInspector.FlameChartDataProvider} dataProvider | |
445 */ | |
446 WebInspector.FlameChart.OverviewPane = function(dataProvider) | |
447 { | |
448 WebInspector.VBox.call(this); | |
449 this.element.classList.add("flame-chart-overview-pane"); | |
450 this._overviewContainer = this.element.createChild("div", "overview-containe
r"); | |
451 this._overviewGrid = new WebInspector.OverviewGrid("flame-chart"); | |
452 this._overviewGrid.element.classList.add("fill"); | |
453 this._overviewCanvas = this._overviewContainer.createChild("canvas", "flame-
chart-overview-canvas"); | |
454 this._overviewContainer.appendChild(this._overviewGrid.element); | |
455 this._overviewCalculator = new WebInspector.FlameChart.OverviewCalculator(); | |
456 this._dataProvider = dataProvider; | |
457 } | |
458 | |
459 WebInspector.FlameChart.OverviewPane.prototype = { | |
460 /** | |
461 * @param {number} windowStartTime | |
462 * @param {number} windowEndTime | |
463 */ | |
464 requestWindowTimes: function(windowStartTime, windowEndTime) | |
465 { | |
466 this._overviewGrid.setWindow(windowStartTime / this._dataProvider.totalT
ime(), windowEndTime / this._dataProvider.totalTime()); | |
467 }, | |
468 | |
469 /** | |
470 * @param {!number} timeLeft | |
471 * @param {!number} timeRight | |
472 */ | |
473 _selectRange: function(timeLeft, timeRight) | |
474 { | |
475 this._overviewGrid.setWindow(timeLeft / this._dataProvider.totalTime(),
timeRight / this._dataProvider.totalTime()); | |
476 }, | |
477 | |
478 /** | |
479 * @return {?WebInspector.FlameChart.TimelineData} | |
480 */ | |
481 _timelineData: function() | |
482 { | |
483 return this._dataProvider.timelineData(); | |
484 }, | |
485 | |
486 onResize: function() | |
487 { | |
488 this._scheduleUpdate(); | |
489 }, | |
490 | |
491 _scheduleUpdate: function() | |
492 { | |
493 if (this._updateTimerId) | |
494 return; | |
495 this._updateTimerId = requestAnimationFrame(this.update.bind(this)); | |
496 }, | |
497 | |
498 update: function() | |
499 { | |
500 this._updateTimerId = 0; | |
501 var timelineData = this._timelineData(); | |
502 if (!timelineData) | |
503 return; | |
504 this._resetCanvas(this._overviewContainer.clientWidth, this._overviewCon
tainer.clientHeight - WebInspector.FlameChart.DividersBarHeight); | |
505 this._overviewCalculator._updateBoundaries(this); | |
506 this._overviewGrid.updateDividers(this._overviewCalculator); | |
507 WebInspector.FlameChart.OverviewPane.drawOverviewCanvas( | |
508 this._dataProvider, | |
509 timelineData, | |
510 this._overviewCanvas.getContext("2d"), | |
511 this._overviewContainer.clientWidth, | |
512 this._overviewContainer.clientHeight - WebInspector.FlameChart.Divid
ersBarHeight | |
513 ); | |
514 }, | |
515 | |
516 /** | |
517 * @param {!number} width | |
518 * @param {!number} height | |
519 */ | |
520 _resetCanvas: function(width, height) | |
521 { | |
522 var ratio = window.devicePixelRatio; | |
523 this._overviewCanvas.width = width * ratio; | |
524 this._overviewCanvas.height = height * ratio; | |
525 }, | |
526 | |
527 __proto__: WebInspector.VBox.prototype | |
528 } | |
529 | |
530 /** | |
531 * @param {!WebInspector.FlameChartDataProvider} dataProvider | |
532 * @param {!WebInspector.FlameChart.TimelineData} timelineData | |
533 * @param {!number} width | |
534 */ | |
535 WebInspector.FlameChart.OverviewPane.calculateDrawData = function(dataProvider,
timelineData, width) | |
536 { | |
537 var entryOffsets = timelineData.entryOffsets; | |
538 var entryTotalTimes = timelineData.entryTotalTimes; | |
539 var entryLevels = timelineData.entryLevels; | |
540 var length = entryOffsets.length; | |
541 | |
542 var drawData = new Uint8Array(width); | |
543 var scaleFactor = width / dataProvider.totalTime(); | |
544 | |
545 for (var entryIndex = 0; entryIndex < length; ++entryIndex) { | |
546 var start = Math.floor(entryOffsets[entryIndex] * scaleFactor); | |
547 var finish = Math.floor((entryOffsets[entryIndex] + entryTotalTimes[entr
yIndex]) * scaleFactor); | |
548 for (var x = start; x <= finish; ++x) | |
549 drawData[x] = Math.max(drawData[x], entryLevels[entryIndex] + 1); | |
550 } | |
551 return drawData; | |
552 } | |
553 | |
554 /** | |
555 * @param {!WebInspector.FlameChartDataProvider} dataProvider | |
556 * @param {!WebInspector.FlameChart.TimelineData} timelineData | |
557 * @param {!Object} context | |
558 * @param {!number} width | |
559 * @param {!number} height | |
560 */ | |
561 WebInspector.FlameChart.OverviewPane.drawOverviewCanvas = function(dataProvider,
timelineData, context, width, height) | |
562 { | |
563 var drawData = WebInspector.FlameChart.OverviewPane.calculateDrawData(dataPr
ovider, timelineData, width); | |
564 if (!drawData) | |
565 return; | |
566 | |
567 var ratio = window.devicePixelRatio; | |
568 var canvasWidth = width * ratio; | |
569 var canvasHeight = height * ratio; | |
570 | |
571 var yScaleFactor = canvasHeight / (dataProvider.maxStackDepth() * 1.1); | |
572 context.lineWidth = 1; | |
573 context.translate(0.5, 0.5); | |
574 context.strokeStyle = "rgba(20,0,0,0.4)"; | |
575 context.fillStyle = "rgba(214,225,254,0.8)"; | |
576 context.moveTo(-1, canvasHeight - 1); | |
577 if (drawData) | |
578 context.lineTo(-1, Math.round(height - drawData[0] * yScaleFactor - 1)); | |
579 var value; | |
580 for (var x = 0; x < width; ++x) { | |
581 value = Math.round(canvasHeight - drawData[x] * yScaleFactor - 1); | |
582 context.lineTo(x * ratio, value); | |
583 } | |
584 context.lineTo(canvasWidth + 1, value); | |
585 context.lineTo(canvasWidth + 1, canvasHeight - 1); | |
586 context.fill(); | |
587 context.stroke(); | |
588 context.closePath(); | |
589 } | |
590 | |
591 /** | |
592 * @constructor | |
593 * @extends {WebInspector.VBox} | |
594 * @param {!WebInspector.FlameChartDataProvider} dataProvider | |
595 * @param {!WebInspector.FlameChartDelegate} flameChartDelegate | |
596 * @param {boolean} isTopDown | |
597 * @param {boolean} timeBasedWindow | |
598 */ | |
599 WebInspector.FlameChart.MainPane = function(dataProvider, flameChartDelegate, is
TopDown, timeBasedWindow) | |
600 { | |
601 WebInspector.VBox.call(this); | |
602 this.element.classList.add("flame-chart-main-pane"); | |
603 this._flameChartDelegate = flameChartDelegate; | |
604 this._isTopDown = isTopDown; | |
605 this._timeBasedWindow = timeBasedWindow; | |
606 | |
607 this._calculator = new WebInspector.FlameChart.Calculator(); | |
608 | |
609 this._canvas = this.element.createChild("canvas"); | |
610 this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this)); | |
611 this._canvas.addEventListener("mousewheel", this._onMouseWheel.bind(this), f
alse); | |
612 this._canvas.addEventListener("click", this._onClick.bind(this), false); | |
613 WebInspector.installDragHandle(this._canvas, this._startCanvasDragging.bind(
this), this._canvasDragging.bind(this), this._endCanvasDragging.bind(this), "mov
e", null); | |
614 | |
615 this._entryInfo = this.element.createChild("div", "profile-entry-info"); | |
616 this._highlightElement = this.element.createChild("div", "flame-chart-highli
ght-element"); | |
617 this._selectedElement = this.element.createChild("div", "flame-chart-selecte
d-element"); | |
618 | |
619 this._dataProvider = dataProvider; | |
620 | |
621 this._windowLeft = 0.0; | |
622 this._windowRight = 1.0; | |
623 this._windowWidth = 1.0; | |
624 this._timeWindowLeft = 0; | |
625 this._timeWindowRight = Infinity; | |
626 this._barHeight = dataProvider.barHeight(); | |
627 this._barHeightDelta = this._isTopDown ? -this._barHeight : this._barHeight; | |
628 this._minWidth = 1; | |
629 this._paddingLeft = this._dataProvider.paddingLeft(); | |
630 this._highlightedEntryIndex = -1; | |
631 this._selectedEntryIndex = -1; | |
632 this._textWidth = {}; | |
633 } | |
634 | |
635 WebInspector.FlameChart.MainPane.prototype = { | |
636 _resetCanvas: function() | 303 _resetCanvas: function() |
637 { | 304 { |
638 var ratio = window.devicePixelRatio; | 305 var ratio = window.devicePixelRatio; |
639 this._canvas.width = this._offsetWidth * ratio; | 306 this._canvas.width = this._offsetWidth * ratio; |
640 this._canvas.height = this._offsetHeight * ratio; | 307 this._canvas.height = this._offsetHeight * ratio; |
641 }, | 308 }, |
642 | 309 |
643 /** | 310 /** |
644 * @return {?WebInspector.FlameChart.TimelineData} | 311 * @return {?WebInspector.FlameChart.TimelineData} |
645 */ | 312 */ |
(...skipping 475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1121 reset: function() | 788 reset: function() |
1122 { | 789 { |
1123 this._highlightedEntryIndex = -1; | 790 this._highlightedEntryIndex = -1; |
1124 this._selectedEntryIndex = -1; | 791 this._selectedEntryIndex = -1; |
1125 this._textWidth = {}; | 792 this._textWidth = {}; |
1126 this.update(); | 793 this.update(); |
1127 }, | 794 }, |
1128 | 795 |
1129 __proto__: WebInspector.VBox.prototype | 796 __proto__: WebInspector.VBox.prototype |
1130 } | 797 } |
OLD | NEW |