blob: c5f844d31430817e81386c14b621e8549bc6f3bf [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/**
2 * Copyright (C) 2013 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
29 */
30
Tim van der Lippebb769172020-02-12 15:32:4431import * as Common from '../common/common.js';
32import * as Host from '../host/host.js';
Christy Chenf9033fa2021-01-21 10:07:0433import * as i18n from '../i18n/i18n.js';
Jack Franklin1be909c2020-03-04 08:57:4134import * as Platform from '../platform/platform.js';
Paul Lewiscf1f8a42020-11-04 21:05:2435import * as Root from '../root/root.js';
Paul Lewisead45752020-06-23 09:51:3636import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars
Paul Lewisca569a52020-09-09 16:11:5137import * as ThemeSupport from '../theme_support/theme_support.js';
Tim van der Lippedfbb48f2020-11-19 14:49:1538import * as TimelineModel from '../timeline_model/timeline_model.js'; // eslint-disable-line no-unused-vars
Tim van der Lippebb769172020-02-12 15:32:4439import * as UI from '../ui/ui.js';
40
Paul Lewis2cfa94e2020-01-13 10:30:1541import {ChartViewport, ChartViewportDelegate} from './ChartViewport.js'; // eslint-disable-line no-unused-vars
42import {Calculator, TimelineGrid} from './TimelineGrid.js'; // eslint-disable-line no-unused-vars
43
Christy Chenf9033fa2021-01-21 10:07:0444export const UIStrings = {
45 /**
46 *@description Aria accessible name in Flame Chart of the Performance panel
47 */
48 flameChart: 'Flame Chart',
49 /**
50 *@description Text for the screen reader to announce a hovered group
51 *@example {Network} PH1
52 */
53 sHovered: '{PH1} hovered',
54 /**
55 *@description Text for screen reader to announce a selected group.
56 *@example {Network} PH1
57 */
58 sSelected: '{PH1} selected',
59 /**
60 *@description Text for screen reader to announce an expanded group
61 *@example {Network} PH1
62 */
63 sExpanded: '{PH1} expanded',
64 /**
65 *@description Text for screen reader to announce a collapsed group
66 *@example {Network} PH1
67 */
68 sCollapsed: '{PH1} collapsed',
69};
70const str_ = i18n.i18n.registerUIStrings('perf_ui/FlameChart.js', UIStrings);
71const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Blink Reformat4c46d092018-04-07 15:32:3772/**
73 * @interface
74 */
Tim van der Lippefd2b2ce2020-01-03 15:05:1875export class FlameChartDelegate {
Blink Reformat4c46d092018-04-07 15:32:3776 /**
77 * @param {number} startTime
78 * @param {number} endTime
79 * @param {boolean} animate
80 */
Tim van der Lippefd2b2ce2020-01-03 15:05:1881 windowChanged(startTime, endTime, animate) {
82 }
Blink Reformat4c46d092018-04-07 15:32:3783
84 /**
85 * @param {number} startTime
86 * @param {number} endTime
87 */
Tim van der Lippefd2b2ce2020-01-03 15:05:1888 updateRangeSelection(startTime, endTime) {
89 }
Blink Reformat4c46d092018-04-07 15:32:3790
91 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:1892 * @param {!FlameChart} flameChart
Tim van der Lippe8ef250c2020-02-20 16:29:2593 * @param {?Group} group
Blink Reformat4c46d092018-04-07 15:32:3794 */
Tim van der Lippefd2b2ce2020-01-03 15:05:1895 updateSelectedGroup(flameChart, group) {
96 }
97}
Blink Reformat4c46d092018-04-07 15:32:3798
99/**
Paul Lewis2cfa94e2020-01-13 10:30:15100 * @implements {Calculator}
101 * @implements {ChartViewportDelegate}
Blink Reformat4c46d092018-04-07 15:32:37102 */
Tim van der Lippebb769172020-02-12 15:32:44103export class FlameChart extends UI.Widget.VBox {
Blink Reformat4c46d092018-04-07 15:32:37104 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:18105 * @param {!FlameChartDataProvider} dataProvider
106 * @param {!FlameChartDelegate} flameChartDelegate
Paul Lewiscf1f8a42020-11-04 21:05:24107 * @param {!Common.Settings.Setting<?>=} groupExpansionSetting
Blink Reformat4c46d092018-04-07 15:32:37108 */
109 constructor(dataProvider, flameChartDelegate, groupExpansionSetting) {
110 super(true);
Jack Franklin71519f82020-11-03 12:08:59111 this.registerRequiredCSS('perf_ui/flameChart.css', {enableLegacyPatching: true});
Blink Reformat4c46d092018-04-07 15:32:37112 this.contentElement.classList.add('flame-chart-main-pane');
113 this._groupExpansionSetting = groupExpansionSetting;
114 this._groupExpansionState = groupExpansionSetting && groupExpansionSetting.get() || {};
115 this._flameChartDelegate = flameChartDelegate;
116
Tim van der Lippe99e59b82019-09-30 20:00:59117 this._useWebGL = Root.Runtime.experiments.isEnabled('timelineWebGL');
Paul Lewis2cfa94e2020-01-13 10:30:15118 this._chartViewport = new ChartViewport(this);
Blink Reformat4c46d092018-04-07 15:32:37119 this._chartViewport.show(this.contentElement);
120
121 this._dataProvider = dataProvider;
122
Paul Lewis49b16822020-02-21 14:22:03123 this._candyStripeCanvas = /** @type {!HTMLCanvasElement} */ (document.createElement('canvas'));
124 this._createCandyStripePattern();
125
Blink Reformat4c46d092018-04-07 15:32:37126 this._viewportElement = this._chartViewport.viewportElement;
Alexei Filippov57ccafb2018-08-14 20:59:05127 if (this._useWebGL) {
128 this._canvasGL = /** @type {!HTMLCanvasElement} */ (this._viewportElement.createChild('canvas', 'fill'));
129 this._initWebGL();
130 }
Paul Lewiscf1f8a42020-11-04 21:05:24131 /** @type {!HTMLCanvasElement} */
Alexei Filippov57ccafb2018-08-14 20:59:05132 this._canvas = /** @type {!HTMLCanvasElement} */ (this._viewportElement.createChild('canvas', 'fill'));
Blink Reformat4c46d092018-04-07 15:32:37133
Joel Einbinder83fc76e2018-06-11 23:19:47134 this._canvas.tabIndex = 0;
Christy Chenf9033fa2021-01-21 10:07:04135 UI.ARIAUtils.setAccessibleName(this._canvas, i18nString(UIStrings.flameChart));
Anubha Mathur72dd5822019-06-13 23:05:19136 UI.ARIAUtils.markAsTree(this._canvas);
Blink Reformat4c46d092018-04-07 15:32:37137 this.setDefaultFocusedElement(this._canvas);
Anubha Mathur72dd5822019-06-13 23:05:19138 this._canvas.classList.add('flame-chart-canvas');
Blink Reformat4c46d092018-04-07 15:32:37139 this._canvas.addEventListener('mousemove', this._onMouseMove.bind(this), false);
140 this._canvas.addEventListener('mouseout', this._onMouseOut.bind(this), false);
141 this._canvas.addEventListener('click', this._onClick.bind(this), false);
142 this._canvas.addEventListener('keydown', this._onKeyDown.bind(this), false);
143
144 this._entryInfo = this._viewportElement.createChild('div', 'flame-chart-entry-info');
145 this._markerHighlighElement = this._viewportElement.createChild('div', 'flame-chart-marker-highlight-element');
146 this._highlightElement = this._viewportElement.createChild('div', 'flame-chart-highlight-element');
147 this._selectedElement = this._viewportElement.createChild('div', 'flame-chart-selected-element');
Michael Liao712bbc22019-10-15 19:21:51148 this._canvas.addEventListener('focus', () => {
149 this._selectedElement.classList.remove('flame-chart-unfocused-selected-element');
Tim van der Lippefd2b2ce2020-01-03 15:05:18150 this.dispatchEventToListeners(Events.CanvasFocused);
Michael Liao712bbc22019-10-15 19:21:51151 }, false);
152 this._canvas.addEventListener('blur', () => {
153 this._selectedElement.classList.add('flame-chart-unfocused-selected-element');
154 }, false);
Blink Reformat4c46d092018-04-07 15:32:37155
Tim van der Lippebb769172020-02-12 15:32:44156 UI.UIUtils.installDragHandle(
Blink Reformat4c46d092018-04-07 15:32:37157 this._viewportElement, this._startDragging.bind(this), this._dragging.bind(this), this._endDragging.bind(this),
158 null);
159
160 this._rulerEnabled = true;
161 this._rangeSelectionStart = 0;
162 this._rangeSelectionEnd = 0;
163 this._barHeight = 17;
164 this._textBaseline = 5;
165 this._textPadding = 5;
166 this._markerRadius = 6;
167 this._chartViewport.setWindowTimes(
168 dataProvider.minimumBoundary(), dataProvider.minimumBoundary() + dataProvider.totalTime());
169
170 /** @const */
171 this._headerLeftPadding = 6;
172 /** @const */
173 this._arrowSide = 8;
174 /** @const */
175 this._expansionArrowIndent = this._headerLeftPadding + this._arrowSide / 2;
176 /** @const */
177 this._headerLabelXPadding = 3;
178 /** @const */
179 this._headerLabelYPadding = 2;
180
181 this._highlightedMarkerIndex = -1;
182 this._highlightedEntryIndex = -1;
183 this._selectedEntryIndex = -1;
184 this._rawTimelineDataLength = 0;
Alexei Filippov72d792d2018-11-06 07:15:04185 /** @type {!Map<string, !Map<string,number>>} */
Blink Reformat4c46d092018-04-07 15:32:37186 this._textWidth = new Map();
Alexei Filippov6c622e92018-11-10 02:13:59187 /** @type {!Map<number, !{x: number, width: number}>} */
188 this._markerPositions = new Map();
Blink Reformat4c46d092018-04-07 15:32:37189
190 this._lastMouseOffsetX = 0;
191 this._selectedGroup = -1;
Anubha Mathur72dd5822019-06-13 23:05:19192
193 // Keyboard focused group is used to navigate groups irrespective of whether they are selectable or not
194 this._keyboardFocusedGroup = -1;
195
Paul Lewisca569a52020-09-09 16:11:51196 this._selectedGroupBackroundColor = ThemeSupport.ThemeSupport.instance().patchColorText(
197 Colors.SelectedGroupBackground, ThemeSupport.ThemeSupport.ColorUsage.Background);
198 this._selectedGroupBorderColor = ThemeSupport.ThemeSupport.instance().patchColorText(
199 Colors.SelectedGroupBorder, ThemeSupport.ThemeSupport.ColorUsage.Background);
Paul Lewiscf1f8a42020-11-04 21:05:24200
201 /** @type {number} */
202 this._offsetWidth;
203
204 /** @type {number} */
205 this._offsetHeight;
206
207 /** @type {!HTMLCanvasElement} */
208 this._canvasGL;
209
210 /** @type {number} */
211 this._dragStartX;
212
213 /** @type {number} */
214 this._dragStartY;
215
216 /** @type {number} */
217 this._lastMouseOffsetX;
218
219 /** @type {number} */
220 this._lastMouseOffsetY;
221
222 /** @type {number} */
223 this._minimumBoundary;
Blink Reformat4c46d092018-04-07 15:32:37224 }
225
226 /**
227 * @override
228 */
229 willHide() {
230 this.hideHighlight();
231 }
232
233 /**
234 * @param {number} value
235 */
236 setBarHeight(value) {
237 this._barHeight = value;
238 }
239
240 /**
241 * @param {number} value
242 */
243 setTextBaseline(value) {
244 this._textBaseline = value;
245 }
246
247 /**
248 * @param {number} value
249 */
250 setTextPadding(value) {
251 this._textPadding = value;
252 }
253
254 /**
255 * @param {boolean} enable
256 */
257 enableRuler(enable) {
258 this._rulerEnabled = enable;
259 }
260
261 alwaysShowVerticalScroll() {
262 this._chartViewport.alwaysShowVerticalScroll();
263 }
264
265 disableRangeSelection() {
266 this._chartViewport.disableRangeSelection();
267 }
268
269 /**
270 * @param {number} entryIndex
271 */
272 highlightEntry(entryIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34273 if (this._highlightedEntryIndex === entryIndex) {
Blink Reformat4c46d092018-04-07 15:32:37274 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34275 }
276 if (!this._dataProvider.entryColor(entryIndex)) {
Blink Reformat4c46d092018-04-07 15:32:37277 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34278 }
Blink Reformat4c46d092018-04-07 15:32:37279 this._highlightedEntryIndex = entryIndex;
280 this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
Tim van der Lippefd2b2ce2020-01-03 15:05:18281 this.dispatchEventToListeners(Events.EntryHighlighted, entryIndex);
Blink Reformat4c46d092018-04-07 15:32:37282 }
283
284 hideHighlight() {
285 this._entryInfo.removeChildren();
286 this._highlightedEntryIndex = -1;
287 this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
Tim van der Lippefd2b2ce2020-01-03 15:05:18288 this.dispatchEventToListeners(Events.EntryHighlighted, -1);
Blink Reformat4c46d092018-04-07 15:32:37289 }
290
Paul Lewis49b16822020-02-21 14:22:03291 _createCandyStripePattern() {
292 // Set the candy stripe pattern to 17px so it repeats well.
293 const size = 17;
294 this._candyStripeCanvas.width = size;
295 this._candyStripeCanvas.height = size;
296
297 const ctx = this._candyStripeCanvas.getContext('2d');
Paul Lewiscf1f8a42020-11-04 21:05:24298 if (!ctx) {
299 return;
300 }
Paul Lewis49b16822020-02-21 14:22:03301
302 // Rotate the stripe by 45deg to the right.
303 ctx.translate(size * 0.5, size * 0.5);
304 ctx.rotate(Math.PI * 0.25);
305 ctx.translate(-size * 0.5, -size * 0.5);
306
307 ctx.fillStyle = 'rgba(255, 0, 0, 0.4)';
308 for (let x = -size; x < size * 2; x += 3) {
309 ctx.fillRect(x, -size, 1, size * 3);
310 }
311 }
312
Blink Reformat4c46d092018-04-07 15:32:37313 _resetCanvas() {
314 const ratio = window.devicePixelRatio;
315 const width = Math.round(this._offsetWidth * ratio);
316 const height = Math.round(this._offsetHeight * ratio);
317 this._canvas.width = width;
318 this._canvas.height = height;
319 this._canvas.style.width = `${width / ratio}px`;
320 this._canvas.style.height = `${height / ratio}px`;
Alexei Filippov57ccafb2018-08-14 20:59:05321 if (this._useWebGL) {
322 this._canvasGL.width = width;
323 this._canvasGL.height = height;
324 this._canvasGL.style.width = `${width / ratio}px`;
325 this._canvasGL.style.height = `${height / ratio}px`;
326 }
Blink Reformat4c46d092018-04-07 15:32:37327 }
328
329 /**
330 * @override
331 * @param {number} startTime
332 * @param {number} endTime
333 * @param {boolean} animate
334 */
Alexei Filippov2578eb02018-04-11 08:15:05335 windowChanged(startTime, endTime, animate) {
336 this._flameChartDelegate.windowChanged(startTime, endTime, animate);
Blink Reformat4c46d092018-04-07 15:32:37337 }
338
339 /**
340 * @override
341 * @param {number} startTime
342 * @param {number} endTime
343 */
344 updateRangeSelection(startTime, endTime) {
345 this._flameChartDelegate.updateRangeSelection(startTime, endTime);
346 }
347
348 /**
349 * @override
350 * @param {number} width
351 * @param {number} height
352 */
353 setSize(width, height) {
354 this._offsetWidth = width;
355 this._offsetHeight = height;
356 }
357
358 /**
359 * @param {!MouseEvent} event
360 */
361 _startDragging(event) {
362 this.hideHighlight();
363 this._maxDragOffset = 0;
364 this._dragStartX = event.pageX;
365 this._dragStartY = event.pageY;
366 return true;
367 }
368
369 /**
370 * @param {!MouseEvent} event
371 */
372 _dragging(event) {
373 const dx = event.pageX - this._dragStartX;
374 const dy = event.pageY - this._dragStartY;
375 this._maxDragOffset = Math.max(this._maxDragOffset, Math.sqrt(dx * dx + dy * dy));
376 }
377
378 /**
379 * @param {!MouseEvent} event
380 */
381 _endDragging(event) {
382 this._updateHighlight();
383 }
384
385 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:18386 * @return {?TimelineData}
Blink Reformat4c46d092018-04-07 15:32:37387 */
388 _timelineData() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34389 if (!this._dataProvider) {
Blink Reformat4c46d092018-04-07 15:32:37390 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34391 }
Blink Reformat4c46d092018-04-07 15:32:37392 const timelineData = this._dataProvider.timelineData();
Paul Lewiscf1f8a42020-11-04 21:05:24393 if (timelineData !== this._rawTimelineData ||
394 (timelineData && timelineData.entryStartTimes.length !== this._rawTimelineDataLength)) {
Blink Reformat4c46d092018-04-07 15:32:37395 this._processTimelineData(timelineData);
Tim van der Lippe1d6e57a2019-09-30 11:55:34396 }
Paul Lewiscf1f8a42020-11-04 21:05:24397 return this._rawTimelineData || null;
Blink Reformat4c46d092018-04-07 15:32:37398 }
399
400 /**
401 * @param {number} entryIndex
402 */
403 _revealEntry(entryIndex) {
404 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:34405 if (!timelineData) {
Blink Reformat4c46d092018-04-07 15:32:37406 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34407 }
Blink Reformat4c46d092018-04-07 15:32:37408 const timeLeft = this._chartViewport.windowLeftTime();
409 const timeRight = this._chartViewport.windowRightTime();
410 const entryStartTime = timelineData.entryStartTimes[entryIndex];
411 const entryTotalTime = timelineData.entryTotalTimes[entryIndex];
412 const entryEndTime = entryStartTime + entryTotalTime;
413 let minEntryTimeWindow = Math.min(entryTotalTime, timeRight - timeLeft);
414
415 const level = timelineData.entryLevels[entryIndex];
416 this._chartViewport.setScrollOffset(this._levelToOffset(level), this._levelHeight(level));
417
418 const minVisibleWidthPx = 30;
419 const futurePixelToTime = (timeRight - timeLeft) / this._offsetWidth;
420 minEntryTimeWindow = Math.max(minEntryTimeWindow, futurePixelToTime * minVisibleWidthPx);
421 if (timeLeft > entryEndTime) {
422 const delta = timeLeft - entryEndTime + minEntryTimeWindow;
Alexei Filippov2578eb02018-04-11 08:15:05423 this.windowChanged(timeLeft - delta, timeRight - delta, /* animate */ true);
Blink Reformat4c46d092018-04-07 15:32:37424 } else if (timeRight < entryStartTime) {
425 const delta = entryStartTime - timeRight + minEntryTimeWindow;
Alexei Filippov2578eb02018-04-11 08:15:05426 this.windowChanged(timeLeft + delta, timeRight + delta, /* animate */ true);
Blink Reformat4c46d092018-04-07 15:32:37427 }
428 }
429
430 /**
431 * @param {number} startTime
432 * @param {number} endTime
433 * @param {boolean=} animate
434 */
435 setWindowTimes(startTime, endTime, animate) {
436 this._chartViewport.setWindowTimes(startTime, endTime, animate);
437 this._updateHighlight();
438 }
439
440 /**
441 * @param {!Event} event
442 */
443 _onMouseMove(event) {
Paul Lewiscf1f8a42020-11-04 21:05:24444 const mouseEvent = /** @type {!MouseEvent} */ (event);
445 this._lastMouseOffsetX = mouseEvent.offsetX;
446 this._lastMouseOffsetY = mouseEvent.offsetY;
Tim van der Lippe1d6e57a2019-09-30 11:55:34447 if (!this._enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37448 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34449 }
450 if (this._chartViewport.isDragging()) {
Blink Reformat4c46d092018-04-07 15:32:37451 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34452 }
Paul Lewiscf1f8a42020-11-04 21:05:24453 if (this._coordinatesToGroupIndex(mouseEvent.offsetX, mouseEvent.offsetY, true /* headerOnly */) >= 0) {
Blink Reformat4c46d092018-04-07 15:32:37454 this.hideHighlight();
455 this._viewportElement.style.cursor = 'pointer';
456 return;
457 }
458 this._updateHighlight();
459 }
460
461 _updateHighlight() {
Alexei Filippov8ee66382018-11-30 01:53:56462 const entryIndex = this._coordinatesToEntryIndex(this._lastMouseOffsetX, this._lastMouseOffsetY);
Blink Reformat4c46d092018-04-07 15:32:37463 if (entryIndex === -1) {
464 this.hideHighlight();
465 const group =
466 this._coordinatesToGroupIndex(this._lastMouseOffsetX, this._lastMouseOffsetY, false /* headerOnly */);
Paul Lewiscf1f8a42020-11-04 21:05:24467 if (group >= 0 && this._rawTimelineData && this._rawTimelineData.groups &&
468 this._rawTimelineData.groups[group].selectable) {
Blink Reformat4c46d092018-04-07 15:32:37469 this._viewportElement.style.cursor = 'pointer';
Tim van der Lippe1d6e57a2019-09-30 11:55:34470 } else {
Blink Reformat4c46d092018-04-07 15:32:37471 this._viewportElement.style.cursor = 'default';
Tim van der Lippe1d6e57a2019-09-30 11:55:34472 }
Blink Reformat4c46d092018-04-07 15:32:37473 return;
474 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34475 if (this._chartViewport.isDragging()) {
Blink Reformat4c46d092018-04-07 15:32:37476 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34477 }
Blink Reformat4c46d092018-04-07 15:32:37478 this._updatePopover(entryIndex);
479 this._viewportElement.style.cursor = this._dataProvider.canJumpToEntry(entryIndex) ? 'pointer' : 'default';
480 this.highlightEntry(entryIndex);
481 }
482
483 _onMouseOut() {
484 this._lastMouseOffsetX = -1;
485 this._lastMouseOffsetY = -1;
486 this.hideHighlight();
487 }
488
489 /**
490 * @param {number} entryIndex
491 */
492 _updatePopover(entryIndex) {
493 if (entryIndex === this._highlightedEntryIndex) {
494 this._updatePopoverOffset();
495 return;
496 }
497 this._entryInfo.removeChildren();
498 const popoverElement = this._dataProvider.prepareHighlightedEntryInfo(entryIndex);
499 if (popoverElement) {
500 this._entryInfo.appendChild(popoverElement);
501 this._updatePopoverOffset();
502 }
503 }
504
505 _updatePopoverOffset() {
506 const mouseX = this._lastMouseOffsetX;
507 const mouseY = this._lastMouseOffsetY;
Paul Lewiscf1f8a42020-11-04 21:05:24508 const parentWidth = this._entryInfo.parentElement ? this._entryInfo.parentElement.clientWidth : 0;
509 const parentHeight = this._entryInfo.parentElement ? this._entryInfo.parentElement.clientHeight : 0;
Blink Reformat4c46d092018-04-07 15:32:37510 const infoWidth = this._entryInfo.clientWidth;
511 const infoHeight = this._entryInfo.clientHeight;
512 const /** @const */ offsetX = 10;
513 const /** @const */ offsetY = 6;
514 let x;
515 let y;
516 for (let quadrant = 0; quadrant < 4; ++quadrant) {
517 const dx = quadrant & 2 ? -offsetX - infoWidth : offsetX;
518 const dy = quadrant & 1 ? -offsetY - infoHeight : offsetY;
Jack Franklin1be909c2020-03-04 08:57:41519 x = Platform.NumberUtilities.clamp(mouseX + dx, 0, parentWidth - infoWidth);
520 y = Platform.NumberUtilities.clamp(mouseY + dy, 0, parentHeight - infoHeight);
Tim van der Lippe1d6e57a2019-09-30 11:55:34521 if (x >= mouseX || mouseX >= x + infoWidth || y >= mouseY || mouseY >= y + infoHeight) {
Blink Reformat4c46d092018-04-07 15:32:37522 break;
Tim van der Lippe1d6e57a2019-09-30 11:55:34523 }
Blink Reformat4c46d092018-04-07 15:32:37524 }
525 this._entryInfo.style.left = x + 'px';
526 this._entryInfo.style.top = y + 'px';
527 }
528
529 /**
530 * @param {!Event} event
531 */
532 _onClick(event) {
Paul Lewiscf1f8a42020-11-04 21:05:24533 const mouseEvent = /** @type {!MouseEvent} */ (event);
Blink Reformat4c46d092018-04-07 15:32:37534 this.focus();
535 // onClick comes after dragStart and dragEnd events.
536 // So if there was drag (mouse move) in the middle of that events
537 // we skip the click. Otherwise we jump to the sources.
538 const /** @const */ clickThreshold = 5;
Tim van der Lippe1d6e57a2019-09-30 11:55:34539 if (this._maxDragOffset > clickThreshold) {
Blink Reformat4c46d092018-04-07 15:32:37540 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34541 }
Blink Reformat4c46d092018-04-07 15:32:37542
Paul Lewiscf1f8a42020-11-04 21:05:24543 this._selectGroup(this._coordinatesToGroupIndex(mouseEvent.offsetX, mouseEvent.offsetY, false /* headerOnly */));
544 this._toggleGroupExpand(
545 this._coordinatesToGroupIndex(mouseEvent.offsetX, mouseEvent.offsetY, true /* headerOnly */));
Blink Reformat4c46d092018-04-07 15:32:37546 const timelineData = this._timelineData();
Paul Lewiscf1f8a42020-11-04 21:05:24547 if (mouseEvent.shiftKey && this._highlightedEntryIndex !== -1 && timelineData) {
Blink Reformat4c46d092018-04-07 15:32:37548 const start = timelineData.entryStartTimes[this._highlightedEntryIndex];
549 const end = start + timelineData.entryTotalTimes[this._highlightedEntryIndex];
550 this._chartViewport.setRangeSelection(start, end);
551 } else {
Paul Lewiscf1f8a42020-11-04 21:05:24552 this._chartViewport.onClick(mouseEvent);
Tim van der Lippefd2b2ce2020-01-03 15:05:18553 this.dispatchEventToListeners(Events.EntryInvoked, this._highlightedEntryIndex);
Blink Reformat4c46d092018-04-07 15:32:37554 }
555 }
556
557 /**
558 * @param {number} groupIndex
559 */
560 _selectGroup(groupIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34561 if (groupIndex < 0 || this._selectedGroup === groupIndex) {
Blink Reformat4c46d092018-04-07 15:32:37562 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34563 }
Paul Lewiscf1f8a42020-11-04 21:05:24564 if (!this._rawTimelineData) {
565 return;
566 }
567
Anubha Mathur72dd5822019-06-13 23:05:19568 const groups = this._rawTimelineData.groups;
Paul Lewiscf1f8a42020-11-04 21:05:24569 if (!groups) {
570 return;
571 }
572
Anubha Mathur72dd5822019-06-13 23:05:19573 this._keyboardFocusedGroup = groupIndex;
Anubha Mathur430a80f2019-12-12 00:26:11574 this._scrollGroupIntoView(groupIndex);
Michael Liao0bad0c32020-01-02 18:51:04575 const groupName = groups[groupIndex].name;
Anubha Mathur72dd5822019-06-13 23:05:19576 if (!groups[groupIndex].selectable) {
577 this._deselectAllGroups();
Christy Chenf9033fa2021-01-21 10:07:04578 UI.ARIAUtils.alert(i18nString(UIStrings.sHovered, {PH1: groupName}), this._canvas);
Anubha Mathur72dd5822019-06-13 23:05:19579 } else {
580 this._selectedGroup = groupIndex;
581 this._flameChartDelegate.updateSelectedGroup(this, groups[groupIndex]);
582 this._resetCanvas();
583 this._draw();
Christy Chenf9033fa2021-01-21 10:07:04584 UI.ARIAUtils.alert(i18nString(UIStrings.sSelected, {PH1: groupName}), this._canvas);
Anubha Mathur72dd5822019-06-13 23:05:19585 }
586 }
Blink Reformat4c46d092018-04-07 15:32:37587
Anubha Mathur72dd5822019-06-13 23:05:19588 _deselectAllGroups() {
589 this._selectedGroup = -1;
590 this._flameChartDelegate.updateSelectedGroup(this, null);
591 this._resetCanvas();
592 this._draw();
593 }
594
595 _deselectAllEntries() {
596 this._selectedEntryIndex = -1;
Blink Reformat4c46d092018-04-07 15:32:37597 this._resetCanvas();
598 this._draw();
599 }
600
601 /**
Anubha Mathur72dd5822019-06-13 23:05:19602 * @param {number} index
603 */
604 _isGroupFocused(index) {
605 return index === this._selectedGroup || index === this._keyboardFocusedGroup;
606 }
607
608 /**
Anubha Mathur430a80f2019-12-12 00:26:11609 * @param {number} index
610 */
611 _scrollGroupIntoView(index) {
612 if (index < 0) {
613 return;
614 }
615
Paul Lewiscf1f8a42020-11-04 21:05:24616 if (!this._rawTimelineData) {
617 return;
618 }
619
Anubha Mathur430a80f2019-12-12 00:26:11620 const groups = this._rawTimelineData.groups;
621 const groupOffsets = this._groupOffsets;
Paul Lewiscf1f8a42020-11-04 21:05:24622 if (!groupOffsets || !groups) {
623 return;
624 }
Anubha Mathur430a80f2019-12-12 00:26:11625 const groupTop = groupOffsets[index];
626
627 let nextOffset = groupOffsets[index + 1];
628 if (index === groups.length - 1) {
629 nextOffset += groups[index].style.padding;
630 }
631
632 // For the top group, scroll all the way to the top of the chart
633 // to accommodate the bar with time markers
634 const scrollTop = index === 0 ? 0 : groupTop;
635
636 const scrollHeight = Math.min(nextOffset - scrollTop, this._chartViewport.chartHeight());
637 this._chartViewport.setScrollOffset(scrollTop, scrollHeight);
638 }
639
640 /**
Blink Reformat4c46d092018-04-07 15:32:37641 * @param {number} groupIndex
642 */
Anubha Mathur72dd5822019-06-13 23:05:19643 _toggleGroupExpand(groupIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34644 if (groupIndex < 0 || !this._isGroupCollapsible(groupIndex)) {
Anubha Mathur72dd5822019-06-13 23:05:19645 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34646 }
Anubha Mathur72dd5822019-06-13 23:05:19647
Paul Lewiscf1f8a42020-11-04 21:05:24648 if (!this._rawTimelineData || !this._rawTimelineData.groups) {
649 return;
650 }
651
Anubha Mathur72dd5822019-06-13 23:05:19652 this._expandGroup(groupIndex, !this._rawTimelineData.groups[groupIndex].expanded /* setExpanded */);
653 }
654
655 /**
656 * @param {number} groupIndex
657 * @param {boolean=} setExpanded
Michael Liao (WPT)b54514b2019-08-16 23:11:53658 * @param {boolean=} propagatedExpand
Anubha Mathur72dd5822019-06-13 23:05:19659 */
Michael Liao (WPT)b54514b2019-08-16 23:11:53660 _expandGroup(groupIndex, setExpanded = true, propagatedExpand = false) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34661 if (groupIndex < 0 || !this._isGroupCollapsible(groupIndex)) {
Blink Reformat4c46d092018-04-07 15:32:37662 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34663 }
Blink Reformat4c46d092018-04-07 15:32:37664
Paul Lewiscf1f8a42020-11-04 21:05:24665 if (!this._rawTimelineData) {
666 return;
667 }
668
Blink Reformat4c46d092018-04-07 15:32:37669 const groups = this._rawTimelineData.groups;
Paul Lewiscf1f8a42020-11-04 21:05:24670 if (!groups) {
671 return;
672 }
673
Blink Reformat4c46d092018-04-07 15:32:37674 const group = groups[groupIndex];
Anubha Mathur72dd5822019-06-13 23:05:19675 group.expanded = setExpanded;
676
Blink Reformat4c46d092018-04-07 15:32:37677 this._groupExpansionState[group.name] = group.expanded;
Tim van der Lippe1d6e57a2019-09-30 11:55:34678 if (this._groupExpansionSetting) {
Blink Reformat4c46d092018-04-07 15:32:37679 this._groupExpansionSetting.set(this._groupExpansionState);
Tim van der Lippe1d6e57a2019-09-30 11:55:34680 }
Blink Reformat4c46d092018-04-07 15:32:37681 this._updateLevelPositions();
682
683 this._updateHighlight();
684 if (!group.expanded) {
685 const timelineData = this._timelineData();
Paul Lewiscf1f8a42020-11-04 21:05:24686 if (timelineData) {
687 const level = timelineData.entryLevels[this._selectedEntryIndex];
688 if (this._selectedEntryIndex >= 0 && level >= group.startLevel &&
689 (groupIndex >= groups.length - 1 || groups[groupIndex + 1].startLevel > level)) {
690 this._selectedEntryIndex = -1;
691 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34692 }
Blink Reformat4c46d092018-04-07 15:32:37693 }
694
695 this._updateHeight();
696 this._resetCanvas();
697 this._draw();
Michael Liao (WPT)b54514b2019-08-16 23:11:53698
Anubha Mathur430a80f2019-12-12 00:26:11699 this._scrollGroupIntoView(groupIndex);
Michael Liao (WPT)b54514b2019-08-16 23:11:53700 // We only want to read expanded/collapsed state on user inputted expand/collapse
701 if (!propagatedExpand) {
702 const groupName = groups[groupIndex].name;
Christy Chenf9033fa2021-01-21 10:07:04703 const content = group.expanded ? i18nString(UIStrings.sExpanded, {PH1: groupName}) :
704 i18nString(UIStrings.sCollapsed, {PH1: groupName});
Michael Liao (WPT)b54514b2019-08-16 23:11:53705 UI.ARIAUtils.alert(content, this._canvas);
706 }
Blink Reformat4c46d092018-04-07 15:32:37707 }
708
709 /**
Tim van der Lippebcd6b5c2021-01-13 12:31:51710 * @param {!KeyboardEvent} e
Blink Reformat4c46d092018-04-07 15:32:37711 */
712 _onKeyDown(e) {
Tim van der Lippebb769172020-02-12 15:32:44713 if (!UI.KeyboardShortcut.KeyboardShortcut.hasNoModifiers(e) || !this._timelineData()) {
Anubha Mathur72dd5822019-06-13 23:05:19714 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34715 }
Anubha Mathur72dd5822019-06-13 23:05:19716
717 const eventHandled = this._handleSelectionNavigation(e);
718
719 // Handle keyboard navigation in groups
Tim van der Lippe1d6e57a2019-09-30 11:55:34720 if (!eventHandled && this._rawTimelineData && this._rawTimelineData.groups) {
Anubha Mathur72dd5822019-06-13 23:05:19721 this._handleKeyboardGroupNavigation(e);
Tim van der Lippe1d6e57a2019-09-30 11:55:34722 }
Blink Reformat4c46d092018-04-07 15:32:37723 }
724
725 /**
Ted Meyerb0928d02020-06-23 05:12:45726 * @param {string} eventName
Tim van der Lippe64688912020-11-02 11:55:52727 * @param {function(!Event):void} onEvent
Ted Meyerb0928d02020-06-23 05:12:45728 */
729 bindCanvasEvent(eventName, onEvent) {
730 this._canvas.addEventListener(eventName, onEvent);
731 }
732
733 /**
Paul Lewiscf1f8a42020-11-04 21:05:24734 * @param {!Event} event
Blink Reformat4c46d092018-04-07 15:32:37735 */
Paul Lewiscf1f8a42020-11-04 21:05:24736 _handleKeyboardGroupNavigation(event) {
737 const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
Anubha Mathur72dd5822019-06-13 23:05:19738 let handled = false;
739 let entrySelected = false;
740
Paul Lewiscf1f8a42020-11-04 21:05:24741 if (keyboardEvent.code === 'ArrowUp') {
Anubha Mathur72dd5822019-06-13 23:05:19742 handled = this._selectPreviousGroup();
Paul Lewiscf1f8a42020-11-04 21:05:24743 } else if (keyboardEvent.code === 'ArrowDown') {
Anubha Mathur72dd5822019-06-13 23:05:19744 handled = this._selectNextGroup();
Paul Lewiscf1f8a42020-11-04 21:05:24745 } else if (keyboardEvent.code === 'ArrowLeft') {
Anubha Mathur72dd5822019-06-13 23:05:19746 if (this._keyboardFocusedGroup >= 0) {
747 this._expandGroup(this._keyboardFocusedGroup, false /* setExpanded */);
748 handled = true;
749 }
Paul Lewiscf1f8a42020-11-04 21:05:24750 } else if (keyboardEvent.code === 'ArrowRight') {
Anubha Mathur72dd5822019-06-13 23:05:19751 if (this._keyboardFocusedGroup >= 0) {
752 this._expandGroup(this._keyboardFocusedGroup, true /* setExpanded */);
753 this._selectFirstChild();
754 handled = true;
755 }
Tim van der Lippebcd6b5c2021-01-13 12:31:51756 } else if (keyboardEvent.key === 'Enter') {
Anubha Mathur72dd5822019-06-13 23:05:19757 entrySelected = this._selectFirstEntryInCurrentGroup();
758 handled = entrySelected;
759 }
760
Tim van der Lippe1d6e57a2019-09-30 11:55:34761 if (handled && !entrySelected) {
Anubha Mathur72dd5822019-06-13 23:05:19762 this._deselectAllEntries();
Tim van der Lippe1d6e57a2019-09-30 11:55:34763 }
Anubha Mathur72dd5822019-06-13 23:05:19764
Tim van der Lippe1d6e57a2019-09-30 11:55:34765 if (handled) {
Paul Lewiscf1f8a42020-11-04 21:05:24766 keyboardEvent.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34767 }
Anubha Mathur72dd5822019-06-13 23:05:19768 }
769
770 /**
771 * @return {boolean}
772 */
773 _selectFirstEntryInCurrentGroup() {
Paul Lewiscf1f8a42020-11-04 21:05:24774 if (!this._rawTimelineData) {
775 return false;
776 }
777
Anubha Mathur72dd5822019-06-13 23:05:19778 const allGroups = this._rawTimelineData.groups;
779
Paul Lewiscf1f8a42020-11-04 21:05:24780 if (this._keyboardFocusedGroup < 0 || !allGroups) {
Anubha Mathur72dd5822019-06-13 23:05:19781 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34782 }
Anubha Mathur72dd5822019-06-13 23:05:19783
784 const group = allGroups[this._keyboardFocusedGroup];
785 const startLevelInGroup = group.startLevel;
786
787 // Return if no levels in this group
Tim van der Lippe1d6e57a2019-09-30 11:55:34788 if (startLevelInGroup < 0) {
Anubha Mathur72dd5822019-06-13 23:05:19789 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34790 }
Anubha Mathur72dd5822019-06-13 23:05:19791
792 // Make sure this is the innermost nested group with this startLevel
793 // This is because a parent group also contains levels of all its child groups
794 // So check if the next group has the same level, if it does, user should
795 // go to that child group to select this entry
796 if (this._keyboardFocusedGroup < allGroups.length - 1 &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34797 allGroups[this._keyboardFocusedGroup + 1].startLevel === startLevelInGroup) {
Anubha Mathur72dd5822019-06-13 23:05:19798 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34799 }
Anubha Mathur72dd5822019-06-13 23:05:19800
Paul Lewiscf1f8a42020-11-04 21:05:24801 if (!this._timelineLevels) {
802 return false;
803 }
Anubha Mathur72dd5822019-06-13 23:05:19804
805 // Get first (default) entry in startLevel of selected group
806 const firstEntryIndex = this._timelineLevels[startLevelInGroup][0];
807
808 this._expandGroup(this._keyboardFocusedGroup, true /* setExpanded */);
809 this.setSelectedEntry(firstEntryIndex);
810 return true;
811 }
812
813 /**
814 * @return {boolean}
815 */
816 _selectPreviousGroup() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34817 if (this._keyboardFocusedGroup <= 0) {
Anubha Mathur72dd5822019-06-13 23:05:19818 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34819 }
Anubha Mathur72dd5822019-06-13 23:05:19820
821 const groupIndexToSelect = this._getGroupIndexToSelect(-1 /* offset */);
822 this._selectGroup(groupIndexToSelect);
823 return true;
824 }
825
826 /**
827 * @return {boolean}
828 */
829 _selectNextGroup() {
Paul Lewiscf1f8a42020-11-04 21:05:24830 if (!this._rawTimelineData || !this._rawTimelineData.groups) {
831 return false;
832 }
833
Tim van der Lippe1d6e57a2019-09-30 11:55:34834 if (this._keyboardFocusedGroup >= this._rawTimelineData.groups.length - 1) {
Anubha Mathur72dd5822019-06-13 23:05:19835 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34836 }
Anubha Mathur72dd5822019-06-13 23:05:19837
838 const groupIndexToSelect = this._getGroupIndexToSelect(1 /* offset */);
839 this._selectGroup(groupIndexToSelect);
840 return true;
841 }
842
843 /**
844 * @param {number} offset
845 * @return {number}
846 */
847 _getGroupIndexToSelect(offset) {
Paul Lewiscf1f8a42020-11-04 21:05:24848 if (!this._rawTimelineData || !this._rawTimelineData.groups) {
849 throw new Error('No raw timeline data');
850 }
Anubha Mathur72dd5822019-06-13 23:05:19851 const allGroups = this._rawTimelineData.groups;
852 let groupIndexToSelect = this._keyboardFocusedGroup;
853 let groupName, groupWithSubNestingLevel;
854
855 do {
856 groupIndexToSelect += offset;
857 groupName = this._rawTimelineData.groups[groupIndexToSelect].name;
858 groupWithSubNestingLevel = this._keyboardFocusedGroup !== -1 &&
859 allGroups[groupIndexToSelect].style.nestingLevel > allGroups[this._keyboardFocusedGroup].style.nestingLevel;
860 } while (groupIndexToSelect > 0 && groupIndexToSelect < allGroups.length - 1 &&
861 (!groupName || groupWithSubNestingLevel));
862
863 return groupIndexToSelect;
864 }
865
866 _selectFirstChild() {
Paul Lewiscf1f8a42020-11-04 21:05:24867 if (!this._rawTimelineData || !this._rawTimelineData.groups) {
868 return;
869 }
870
Anubha Mathur72dd5822019-06-13 23:05:19871 const allGroups = this._rawTimelineData.groups;
Tim van der Lippe1d6e57a2019-09-30 11:55:34872 if (this._keyboardFocusedGroup < 0 || this._keyboardFocusedGroup >= allGroups.length - 1) {
Anubha Mathur72dd5822019-06-13 23:05:19873 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34874 }
Anubha Mathur72dd5822019-06-13 23:05:19875
876 const groupIndexToSelect = this._keyboardFocusedGroup + 1;
877 if (allGroups[groupIndexToSelect].style.nestingLevel > allGroups[this._keyboardFocusedGroup].style.nestingLevel) {
878 this._selectGroup(groupIndexToSelect);
Anubha Mathur72dd5822019-06-13 23:05:19879 }
880 }
881
882 /**
Tim van der Lippebcd6b5c2021-01-13 12:31:51883 * @param {!KeyboardEvent} event
Anubha Mathur72dd5822019-06-13 23:05:19884 * @return {boolean}
885 */
Paul Lewiscf1f8a42020-11-04 21:05:24886 _handleSelectionNavigation(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34887 if (this._selectedEntryIndex === -1) {
Anubha Mathur72dd5822019-06-13 23:05:19888 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34889 }
Blink Reformat4c46d092018-04-07 15:32:37890 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:34891 if (!timelineData) {
Anubha Mathur72dd5822019-06-13 23:05:19892 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34893 }
Blink Reformat4c46d092018-04-07 15:32:37894
895 /**
896 * @param {number} time
897 * @param {number} entryIndex
898 * @return {number}
899 */
900 function timeComparator(time, entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:24901 if (!timelineData) {
902 throw new Error('No timeline data');
903 }
Blink Reformat4c46d092018-04-07 15:32:37904 return time - timelineData.entryStartTimes[entryIndex];
905 }
906
907 /**
908 * @param {number} entry1
909 * @param {number} entry2
910 * @return {boolean}
911 */
912 function entriesIntersect(entry1, entry2) {
Paul Lewiscf1f8a42020-11-04 21:05:24913 if (!timelineData) {
914 throw new Error('No timeline data');
915 }
916
Blink Reformat4c46d092018-04-07 15:32:37917 const start1 = timelineData.entryStartTimes[entry1];
918 const start2 = timelineData.entryStartTimes[entry2];
919 const end1 = start1 + timelineData.entryTotalTimes[entry1];
920 const end2 = start2 + timelineData.entryTotalTimes[entry2];
921 return start1 < end2 && start2 < end1;
922 }
923
Paul Lewiscf1f8a42020-11-04 21:05:24924 const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
Blink Reformat4c46d092018-04-07 15:32:37925 const keys = UI.KeyboardShortcut.Keys;
Paul Lewiscf1f8a42020-11-04 21:05:24926 if (keyboardEvent.keyCode === keys.Left.code || keyboardEvent.keyCode === keys.Right.code) {
Blink Reformat4c46d092018-04-07 15:32:37927 const level = timelineData.entryLevels[this._selectedEntryIndex];
Paul Lewiscf1f8a42020-11-04 21:05:24928 const levelIndexes = this._timelineLevels ? this._timelineLevels[level] : [];
Blink Reformat4c46d092018-04-07 15:32:37929 let indexOnLevel = levelIndexes.lowerBound(this._selectedEntryIndex);
Paul Lewiscf1f8a42020-11-04 21:05:24930 indexOnLevel += keyboardEvent.keyCode === keys.Left.code ? -1 : 1;
931 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34932 if (indexOnLevel >= 0 && indexOnLevel < levelIndexes.length) {
Tim van der Lippefd2b2ce2020-01-03 15:05:18933 this.dispatchEventToListeners(Events.EntrySelected, levelIndexes[indexOnLevel]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34934 }
Anubha Mathur72dd5822019-06-13 23:05:19935 return true;
Blink Reformat4c46d092018-04-07 15:32:37936 }
Paul Lewiscf1f8a42020-11-04 21:05:24937
938 if (keyboardEvent.keyCode === keys.Up.code || keyboardEvent.keyCode === keys.Down.code) {
Blink Reformat4c46d092018-04-07 15:32:37939 let level = timelineData.entryLevels[this._selectedEntryIndex];
Paul Lewiscf1f8a42020-11-04 21:05:24940 level += keyboardEvent.keyCode === keys.Up.code ? -1 : 1;
941 if (level < 0 || (this._timelineLevels && level >= this._timelineLevels.length)) {
Anubha Mathur72dd5822019-06-13 23:05:19942 this._deselectAllEntries();
Paul Lewiscf1f8a42020-11-04 21:05:24943 keyboardEvent.consume(true);
Anubha Mathur72dd5822019-06-13 23:05:19944 return true;
945 }
Blink Reformat4c46d092018-04-07 15:32:37946 const entryTime = timelineData.entryStartTimes[this._selectedEntryIndex] +
947 timelineData.entryTotalTimes[this._selectedEntryIndex] / 2;
Paul Lewiscf1f8a42020-11-04 21:05:24948 const levelIndexes = this._timelineLevels ? this._timelineLevels[level] : [];
Blink Reformat4c46d092018-04-07 15:32:37949 let indexOnLevel = levelIndexes.upperBound(entryTime, timeComparator) - 1;
950 if (!entriesIntersect(this._selectedEntryIndex, levelIndexes[indexOnLevel])) {
951 ++indexOnLevel;
952 if (indexOnLevel >= levelIndexes.length ||
Anubha Mathur72dd5822019-06-13 23:05:19953 !entriesIntersect(this._selectedEntryIndex, levelIndexes[indexOnLevel])) {
Paul Lewiscf1f8a42020-11-04 21:05:24954 if (keyboardEvent.code === 'ArrowDown') {
Anubha Mathur72dd5822019-06-13 23:05:19955 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34956 }
Anubha Mathur72dd5822019-06-13 23:05:19957
958 // Stay in the current group and give focus to the parent group instead of entries
959 this._deselectAllEntries();
Paul Lewiscf1f8a42020-11-04 21:05:24960 keyboardEvent.consume(true);
Anubha Mathur72dd5822019-06-13 23:05:19961 return true;
962 }
Blink Reformat4c46d092018-04-07 15:32:37963 }
Paul Lewiscf1f8a42020-11-04 21:05:24964 keyboardEvent.consume(true);
Tim van der Lippefd2b2ce2020-01-03 15:05:18965 this.dispatchEventToListeners(Events.EntrySelected, levelIndexes[indexOnLevel]);
Anubha Mathur72dd5822019-06-13 23:05:19966 return true;
Blink Reformat4c46d092018-04-07 15:32:37967 }
Tim van der Lippebcd6b5c2021-01-13 12:31:51968 if (event.key === 'Enter') {
Paul Lewiscf1f8a42020-11-04 21:05:24969 event.consume(true);
Tim van der Lippefd2b2ce2020-01-03 15:05:18970 this.dispatchEventToListeners(Events.EntryInvoked, this._selectedEntryIndex);
Michael Liao712bbc22019-10-15 19:21:51971 return true;
972 }
Anubha Mathur72dd5822019-06-13 23:05:19973 return false;
Blink Reformat4c46d092018-04-07 15:32:37974 }
975
976 /**
977 * @param {number} x
978 * @param {number} y
979 * @return {number}
980 */
981 _coordinatesToEntryIndex(x, y) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34982 if (x < 0 || y < 0) {
Blink Reformat4c46d092018-04-07 15:32:37983 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:34984 }
Blink Reformat4c46d092018-04-07 15:32:37985 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:34986 if (!timelineData) {
Blink Reformat4c46d092018-04-07 15:32:37987 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:34988 }
Blink Reformat4c46d092018-04-07 15:32:37989 y += this._chartViewport.scrollOffset();
Paul Lewiscf1f8a42020-11-04 21:05:24990 if (!this._visibleLevelOffsets) {
991 throw new Error('No visible level offsets');
992 }
Blink Reformat4c46d092018-04-07 15:32:37993 const cursorLevel = this._visibleLevelOffsets.upperBound(y) - 1;
Paul Lewiscf1f8a42020-11-04 21:05:24994 if (cursorLevel < 0 || (this._visibleLevels && !this._visibleLevels[cursorLevel])) {
Blink Reformat4c46d092018-04-07 15:32:37995 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:34996 }
Blink Reformat4c46d092018-04-07 15:32:37997 const offsetFromLevel = y - this._visibleLevelOffsets[cursorLevel];
Tim van der Lippe1d6e57a2019-09-30 11:55:34998 if (offsetFromLevel > this._levelHeight(cursorLevel)) {
Blink Reformat4c46d092018-04-07 15:32:37999 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341000 }
Alexei Filippov6c622e92018-11-10 02:13:591001
1002 // Check markers first.
1003 for (const [index, pos] of this._markerPositions) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341004 if (timelineData.entryLevels[index] !== cursorLevel) {
Alexei Filippov6c622e92018-11-10 02:13:591005 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341006 }
1007 if (pos.x <= x && x < pos.x + pos.width) {
Alexei Filippov6c622e92018-11-10 02:13:591008 return /** @type {number} */ (index);
Tim van der Lippe1d6e57a2019-09-30 11:55:341009 }
Alexei Filippov6c622e92018-11-10 02:13:591010 }
1011
1012 // Check regular entries.
Blink Reformat4c46d092018-04-07 15:32:371013 const entryStartTimes = timelineData.entryStartTimes;
Paul Lewiscf1f8a42020-11-04 21:05:241014 /** @type {!Array.<number>} */
1015 const entriesOnLevel = this._timelineLevels ? this._timelineLevels[cursorLevel] : [];
Tim van der Lippe1d6e57a2019-09-30 11:55:341016 if (!entriesOnLevel || !entriesOnLevel.length) {
Blink Reformat4c46d092018-04-07 15:32:371017 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341018 }
Blink Reformat4c46d092018-04-07 15:32:371019
Blink Reformat4c46d092018-04-07 15:32:371020 const cursorTime = this._chartViewport.pixelToTime(x);
Alexei Filippov6c622e92018-11-10 02:13:591021 const indexOnLevel = Math.max(
1022 entriesOnLevel.upperBound(cursorTime, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1, 0);
Blink Reformat4c46d092018-04-07 15:32:371023
1024 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:181025 * @this {FlameChart}
Alexei Filippov72d792d2018-11-06 07:15:041026 * @param {number|undefined} entryIndex
Blink Reformat4c46d092018-04-07 15:32:371027 * @return {boolean}
1028 */
1029 function checkEntryHit(entryIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341030 if (entryIndex === undefined) {
Blink Reformat4c46d092018-04-07 15:32:371031 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:341032 }
Paul Lewiscf1f8a42020-11-04 21:05:241033
1034 if (!timelineData) {
1035 return false;
1036 }
1037
Blink Reformat4c46d092018-04-07 15:32:371038 const startTime = entryStartTimes[entryIndex];
Alexei Filippov6c622e92018-11-10 02:13:591039 const duration = timelineData.entryTotalTimes[entryIndex];
Blink Reformat4c46d092018-04-07 15:32:371040 const startX = this._chartViewport.timeToPosition(startTime);
Alexei Filippov6c622e92018-11-10 02:13:591041 const endX = this._chartViewport.timeToPosition(startTime + duration);
1042 const barThresholdPx = 3;
Blink Reformat4c46d092018-04-07 15:32:371043 return startX - barThresholdPx < x && x < endX + barThresholdPx;
1044 }
1045
Alexei Filippov6c622e92018-11-10 02:13:591046 let entryIndex = entriesOnLevel[indexOnLevel];
Tim van der Lippe1d6e57a2019-09-30 11:55:341047 if (checkEntryHit.call(this, entryIndex)) {
Blink Reformat4c46d092018-04-07 15:32:371048 return entryIndex;
Tim van der Lippe1d6e57a2019-09-30 11:55:341049 }
Alexei Filippov6c622e92018-11-10 02:13:591050 entryIndex = entriesOnLevel[indexOnLevel + 1];
Tim van der Lippe1d6e57a2019-09-30 11:55:341051 if (checkEntryHit.call(this, entryIndex)) {
Blink Reformat4c46d092018-04-07 15:32:371052 return entryIndex;
Tim van der Lippe1d6e57a2019-09-30 11:55:341053 }
Blink Reformat4c46d092018-04-07 15:32:371054 return -1;
1055 }
1056
1057 /**
1058 * @param {number} x
1059 * @param {number} y
1060 * @param {boolean} headerOnly
1061 * @return {number}
1062 */
1063 _coordinatesToGroupIndex(x, y, headerOnly) {
Paul Lewiscf1f8a42020-11-04 21:05:241064 if (!this._rawTimelineData || !this._rawTimelineData.groups || !this._groupOffsets) {
1065 return -1;
1066 }
1067
Tim van der Lippe1d6e57a2019-09-30 11:55:341068 if (x < 0 || y < 0) {
Blink Reformat4c46d092018-04-07 15:32:371069 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341070 }
Blink Reformat4c46d092018-04-07 15:32:371071 y += this._chartViewport.scrollOffset();
1072 const groups = this._rawTimelineData.groups || [];
1073 const group = this._groupOffsets.upperBound(y) - 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341074 if (group < 0 || group >= groups.length) {
Blink Reformat4c46d092018-04-07 15:32:371075 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341076 }
Blink Reformat4c46d092018-04-07 15:32:371077 const height = headerOnly ? groups[group].style.height : this._groupOffsets[group + 1] - this._groupOffsets[group];
Tim van der Lippe1d6e57a2019-09-30 11:55:341078 if (y - this._groupOffsets[group] >= height) {
Blink Reformat4c46d092018-04-07 15:32:371079 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341080 }
1081 if (!headerOnly) {
Blink Reformat4c46d092018-04-07 15:32:371082 return group;
Tim van der Lippe1d6e57a2019-09-30 11:55:341083 }
Blink Reformat4c46d092018-04-07 15:32:371084
1085 const context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
1086 context.save();
1087 context.font = groups[group].style.font;
1088 const right = this._headerLeftPadding + this._labelWidthForGroup(context, groups[group]);
1089 context.restore();
Tim van der Lippe1d6e57a2019-09-30 11:55:341090 if (x > right) {
Blink Reformat4c46d092018-04-07 15:32:371091 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341092 }
Blink Reformat4c46d092018-04-07 15:32:371093
1094 return group;
1095 }
1096
1097 /**
1098 * @param {number} x
1099 * @return {number}
1100 */
1101 _markerIndexAtPosition(x) {
Paul Lewiscf1f8a42020-11-04 21:05:241102 const timelineData = this._timelineData();
1103 if (!timelineData) {
1104 return -1;
1105 }
1106
1107 const markers = timelineData.markers;
Tim van der Lippe1d6e57a2019-09-30 11:55:341108 if (!markers) {
Blink Reformat4c46d092018-04-07 15:32:371109 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341110 }
Blink Reformat4c46d092018-04-07 15:32:371111 const /** @const */ accurracyOffsetPx = 4;
1112 const time = this._chartViewport.pixelToTime(x);
1113 const leftTime = this._chartViewport.pixelToTime(x - accurracyOffsetPx);
1114 const rightTime = this._chartViewport.pixelToTime(x + accurracyOffsetPx);
1115 const left = this._markerIndexBeforeTime(leftTime);
1116 let markerIndex = -1;
1117 let distance = Infinity;
1118 for (let i = left; i < markers.length && markers[i].startTime() < rightTime; i++) {
1119 const nextDistance = Math.abs(markers[i].startTime() - time);
1120 if (nextDistance < distance) {
1121 markerIndex = i;
1122 distance = nextDistance;
1123 }
1124 }
1125 return markerIndex;
1126 }
1127
1128 /**
1129 * @param {number} time
1130 * @return {number}
1131 */
1132 _markerIndexBeforeTime(time) {
Paul Lewiscf1f8a42020-11-04 21:05:241133 const timelineData = this._timelineData();
1134 if (!timelineData) {
1135 throw new Error('No timeline data');
1136 }
1137
1138 const markers = timelineData.markers;
1139 if (!markers) {
1140 throw new Error('No timeline markers');
1141 }
1142
1143 return timelineData.markers.lowerBound(time, (markerTimestamp, marker) => markerTimestamp - marker.startTime());
Blink Reformat4c46d092018-04-07 15:32:371144 }
1145
1146 _draw() {
1147 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:341148 if (!timelineData) {
Blink Reformat4c46d092018-04-07 15:32:371149 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341150 }
Blink Reformat4c46d092018-04-07 15:32:371151
Paul Lewiscf1f8a42020-11-04 21:05:241152 const visibleLevelOffsets = this._visibleLevelOffsets ? this._visibleLevelOffsets : [];
1153
Blink Reformat4c46d092018-04-07 15:32:371154 const width = this._offsetWidth;
1155 const height = this._offsetHeight;
1156 const context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
1157 context.save();
1158 const ratio = window.devicePixelRatio;
1159 const top = this._chartViewport.scrollOffset();
1160 context.scale(ratio, ratio);
Alexei Filippov57ccafb2018-08-14 20:59:051161 context.fillStyle = 'rgba(0, 0, 0, 0)';
1162 context.fillRect(0, 0, width, height);
Blink Reformat4c46d092018-04-07 15:32:371163 context.translate(0, -top);
Tim van der Lippebb769172020-02-12 15:32:441164 const defaultFont = '11px ' + Host.Platform.fontFamily();
Blink Reformat4c46d092018-04-07 15:32:371165 context.font = defaultFont;
1166
Paul Lewis49b16822020-02-21 14:22:031167 const candyStripePattern = context.createPattern(this._candyStripeCanvas, 'repeat');
1168
Blink Reformat4c46d092018-04-07 15:32:371169 const entryTotalTimes = timelineData.entryTotalTimes;
1170 const entryStartTimes = timelineData.entryStartTimes;
1171 const entryLevels = timelineData.entryLevels;
1172 const timeToPixel = this._chartViewport.timeToPixel();
1173
1174 const titleIndices = [];
1175 const markerIndices = [];
1176 const textPadding = this._textPadding;
Mathias Bynens23ee1aa2020-03-02 12:06:381177 const minTextWidth = 2 * textPadding + UI.UIUtils.measureTextWidth(context, '…');
Alexei Filippov57ccafb2018-08-14 20:59:051178 const minTextWidthDuration = this._chartViewport.pixelToTimeOffset(minTextWidth);
Paul Lewiscf1f8a42020-11-04 21:05:241179 const minVisibleBarLevel = Math.max(visibleLevelOffsets.upperBound(top) - 1, 0);
Alexei Filippov6c622e92018-11-10 02:13:591180 this._markerPositions.clear();
Blink Reformat4c46d092018-04-07 15:32:371181
Paul Lewis49b16822020-02-21 14:22:031182 let mainThreadTopLevel = -1;
1183
1184 // Find the main thread so that we can mark tasks longer than 50ms.
1185 if ('groups' in timelineData && Array.isArray(timelineData.groups)) {
Tim van der Lippe5adc1e02020-11-20 12:30:121186 const mainThread = timelineData.groups.find(group => {
1187 if (!group.track) {
Paul Lewis49b16822020-02-21 14:22:031188 return false;
1189 }
1190
Tim van der Lippe5adc1e02020-11-20 12:30:121191 return group.track.name === 'CrRendererMain';
Paul Lewis49b16822020-02-21 14:22:031192 });
1193
1194 if (mainThread) {
1195 mainThreadTopLevel = mainThread.startLevel;
1196 }
1197 }
1198
Paul Lewis06985d42020-04-30 14:51:161199 /** @type {!Map<string, {indexes: !Array<number>}>} */
Blink Reformat4c46d092018-04-07 15:32:371200 const colorBuckets = new Map();
1201 for (let level = minVisibleBarLevel; level < this._dataProvider.maxStackDepth(); ++level) {
Tim van der Lippe1d6e57a2019-09-30 11:55:341202 if (this._levelToOffset(level) > top + height) {
Blink Reformat4c46d092018-04-07 15:32:371203 break;
Tim van der Lippe1d6e57a2019-09-30 11:55:341204 }
Paul Lewiscf1f8a42020-11-04 21:05:241205 if (!this._visibleLevels || !this._visibleLevels[level]) {
1206 continue;
1207 }
1208 if (!this._timelineLevels) {
Blink Reformat4c46d092018-04-07 15:32:371209 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341210 }
Blink Reformat4c46d092018-04-07 15:32:371211
1212 // Entries are ordered by start time within a level, so find the last visible entry.
Paul Lewiscf1f8a42020-11-04 21:05:241213 /** @type {!Array.<number>} */
Blink Reformat4c46d092018-04-07 15:32:371214 const levelIndexes = this._timelineLevels[level];
1215 const rightIndexOnLevel =
1216 levelIndexes.lowerBound(
1217 this._chartViewport.windowRightTime(), (time, entryIndex) => time - entryStartTimes[entryIndex]) -
1218 1;
1219 let lastDrawOffset = Infinity;
1220 for (let entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) {
1221 const entryIndex = levelIndexes[entryIndexOnLevel];
Alexei Filippov72d792d2018-11-06 07:15:041222 const duration = entryTotalTimes[entryIndex];
1223 if (isNaN(duration)) {
Alexei Filippov57ccafb2018-08-14 20:59:051224 markerIndices.push(entryIndex);
Alexei Filippov72d792d2018-11-06 07:15:041225 continue;
1226 }
Paul Lewiscf1f8a42020-11-04 21:05:241227 if (duration >= minTextWidthDuration ||
1228 (this._forceDecorationCache && this._forceDecorationCache[entryIndex])) {
Alexei Filippov57ccafb2018-08-14 20:59:051229 titleIndices.push(entryIndex);
Tim van der Lippe1d6e57a2019-09-30 11:55:341230 }
Alexei Filippov57ccafb2018-08-14 20:59:051231
Blink Reformat4c46d092018-04-07 15:32:371232 const entryStartTime = entryStartTimes[entryIndex];
Alexei Filippov57ccafb2018-08-14 20:59:051233 const entryOffsetRight = entryStartTime + duration;
Tim van der Lippe1d6e57a2019-09-30 11:55:341234 if (entryOffsetRight <= this._chartViewport.windowLeftTime()) {
Blink Reformat4c46d092018-04-07 15:32:371235 break;
Tim van der Lippe1d6e57a2019-09-30 11:55:341236 }
1237 if (this._useWebGL) {
Alexei Filippov57ccafb2018-08-14 20:59:051238 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341239 }
Blink Reformat4c46d092018-04-07 15:32:371240
1241 const barX = this._timeToPositionClipped(entryStartTime);
1242 // Check if the entry entirely fits into an already drawn pixel, we can just skip drawing it.
Tim van der Lippe1d6e57a2019-09-30 11:55:341243 if (barX >= lastDrawOffset) {
Blink Reformat4c46d092018-04-07 15:32:371244 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341245 }
Blink Reformat4c46d092018-04-07 15:32:371246 lastDrawOffset = barX;
1247
Paul Lewiscf1f8a42020-11-04 21:05:241248 if (this._entryColorsCache) {
1249 const color = this._entryColorsCache[entryIndex];
1250 let bucket = colorBuckets.get(color);
1251 if (!bucket) {
1252 bucket = {indexes: []};
1253 colorBuckets.set(color, bucket);
1254 }
1255 bucket.indexes.push(entryIndex);
Blink Reformat4c46d092018-04-07 15:32:371256 }
Blink Reformat4c46d092018-04-07 15:32:371257 }
1258 }
1259
Alexei Filippov57ccafb2018-08-14 20:59:051260 if (this._useWebGL) {
1261 this._drawGL();
1262 } else {
1263 context.save();
Alexei Filippov5f6b11d2018-08-18 03:30:281264 this._forEachGroupInViewport((offset, index, group, isFirst, groupHeight) => {
Anubha Mathur72dd5822019-06-13 23:05:191265 if (this._isGroupFocused(index)) {
Alexei Filippov57ccafb2018-08-14 20:59:051266 context.fillStyle = this._selectedGroupBackroundColor;
1267 context.fillRect(0, offset, width, groupHeight - group.style.padding);
Blink Reformat4c46d092018-04-07 15:32:371268 }
Alexei Filippov57ccafb2018-08-14 20:59:051269 });
1270 context.restore();
1271
Paul Lewis06985d42020-04-30 14:51:161272 for (const [color, {indexes}] of colorBuckets) {
Alexei Filippov57ccafb2018-08-14 20:59:051273 context.beginPath();
1274 for (let i = 0; i < indexes.length; ++i) {
1275 const entryIndex = indexes[i];
Alexei Filippov72d792d2018-11-06 07:15:041276 const duration = entryTotalTimes[entryIndex];
Tim van der Lippe1d6e57a2019-09-30 11:55:341277 if (isNaN(duration)) {
Alexei Filippov72d792d2018-11-06 07:15:041278 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341279 }
Alexei Filippov57ccafb2018-08-14 20:59:051280 const entryStartTime = entryStartTimes[entryIndex];
1281 const barX = this._timeToPositionClipped(entryStartTime);
Alexei Filippov57ccafb2018-08-14 20:59:051282 const barLevel = entryLevels[entryIndex];
1283 const barHeight = this._levelHeight(barLevel);
1284 const barY = this._levelToOffset(barLevel);
Alexei Filippov57ccafb2018-08-14 20:59:051285 const barRight = this._timeToPositionClipped(entryStartTime + duration);
1286 const barWidth = Math.max(barRight - barX, 1);
Alexei Filippov72d792d2018-11-06 07:15:041287 context.rect(barX, barY, barWidth - 0.4, barHeight - 1);
Alexei Filippov57ccafb2018-08-14 20:59:051288 }
Alexei Filippov57ccafb2018-08-14 20:59:051289 context.fillStyle = color;
1290 context.fill();
Paul Lewis49b16822020-02-21 14:22:031291
1292 // Draw long task regions.
1293 context.beginPath();
1294 for (let i = 0; i < indexes.length; ++i) {
1295 const entryIndex = indexes[i];
1296 const duration = entryTotalTimes[entryIndex];
Paul Lewis06985d42020-04-30 14:51:161297 const showLongDurations = entryLevels[entryIndex] === mainThreadTopLevel;
Paul Lewis49b16822020-02-21 14:22:031298
1299 if (!showLongDurations) {
1300 continue;
1301 }
1302
Paul Lewis06985d42020-04-30 14:51:161303 if (isNaN(duration) || duration < 50) {
Paul Lewis49b16822020-02-21 14:22:031304 continue;
1305 }
1306
1307 const entryStartTime = entryStartTimes[entryIndex];
1308 const barX = this._timeToPositionClipped(entryStartTime + 50);
1309 const barLevel = entryLevels[entryIndex];
1310 const barHeight = this._levelHeight(barLevel);
1311 const barY = this._levelToOffset(barLevel);
1312 const barRight = this._timeToPositionClipped(entryStartTime + duration);
1313 const barWidth = Math.max(barRight - barX, 1);
1314 context.rect(barX, barY, barWidth - 0.4, barHeight - 1);
1315 }
1316
Paul Lewiscf1f8a42020-11-04 21:05:241317 if (candyStripePattern) {
1318 context.fillStyle = candyStripePattern;
1319 context.fill();
1320 }
Blink Reformat4c46d092018-04-07 15:32:371321 }
Blink Reformat4c46d092018-04-07 15:32:371322 }
1323
Alexei Filippov72d792d2018-11-06 07:15:041324 context.textBaseline = 'alphabetic';
Blink Reformat4c46d092018-04-07 15:32:371325 context.beginPath();
Alexei Filippov6c622e92018-11-10 02:13:591326 let lastMarkerLevel = -1;
1327 let lastMarkerX = -Infinity;
1328 // Markers are sorted top to bottom, right to left.
Alexei Filippov72d792d2018-11-06 07:15:041329 for (let m = markerIndices.length - 1; m >= 0; --m) {
Blink Reformat4c46d092018-04-07 15:32:371330 const entryIndex = markerIndices[m];
Alexei Filippov72d792d2018-11-06 07:15:041331 const title = this._dataProvider.entryTitle(entryIndex);
Tim van der Lippe1d6e57a2019-09-30 11:55:341332 if (!title) {
Alexei Filippov72d792d2018-11-06 07:15:041333 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341334 }
Alexei Filippov6c622e92018-11-10 02:13:591335 const entryStartTime = entryStartTimes[entryIndex];
1336 const level = entryLevels[entryIndex];
Tim van der Lippe1d6e57a2019-09-30 11:55:341337 if (lastMarkerLevel !== level) {
Alexei Filippov6c622e92018-11-10 02:13:591338 lastMarkerX = -Infinity;
Tim van der Lippe1d6e57a2019-09-30 11:55:341339 }
Alexei Filippov6c622e92018-11-10 02:13:591340 const x = Math.max(this._chartViewport.timeToPosition(entryStartTime), lastMarkerX);
1341 const y = this._levelToOffset(level);
1342 const h = this._levelHeight(level);
1343 const padding = 4;
Tim van der Lippebb769172020-02-12 15:32:441344 const width = Math.ceil(UI.UIUtils.measureTextWidth(context, title)) + 2 * padding;
Alexei Filippov214df612018-11-13 19:14:001345 lastMarkerX = x + width + 1;
Alexei Filippov6c622e92018-11-10 02:13:591346 lastMarkerLevel = level;
1347 this._markerPositions.set(entryIndex, {x, width});
Alexei Filippov72d792d2018-11-06 07:15:041348 context.fillStyle = this._dataProvider.entryColor(entryIndex);
1349 context.fillRect(x, y, width, h - 1);
1350 context.fillStyle = 'white';
Alexei Filippov72d792d2018-11-06 07:15:041351 context.fillText(title, x + padding, y + h - this._textBaseline);
Blink Reformat4c46d092018-04-07 15:32:371352 }
1353 context.strokeStyle = 'rgba(0, 0, 0, 0.2)';
1354 context.stroke();
1355
Blink Reformat4c46d092018-04-07 15:32:371356 for (let i = 0; i < titleIndices.length; ++i) {
1357 const entryIndex = titleIndices[i];
1358 const entryStartTime = entryStartTimes[entryIndex];
1359 const barX = this._timeToPositionClipped(entryStartTime);
1360 const barRight = Math.min(this._timeToPositionClipped(entryStartTime + entryTotalTimes[entryIndex]), width) + 1;
1361 const barWidth = barRight - barX;
1362 const barLevel = entryLevels[entryIndex];
1363 const barY = this._levelToOffset(barLevel);
1364 let text = this._dataProvider.entryTitle(entryIndex);
1365 if (text && text.length) {
1366 context.font = this._dataProvider.entryFont(entryIndex) || defaultFont;
Tim van der Lippebb769172020-02-12 15:32:441367 text = UI.UIUtils.trimTextMiddle(context, text, barWidth - 2 * textPadding);
Blink Reformat4c46d092018-04-07 15:32:371368 }
1369 const unclippedBarX = this._chartViewport.timeToPosition(entryStartTime);
1370 const barHeight = this._levelHeight(barLevel);
1371 if (this._dataProvider.decorateEntry(
Tim van der Lippe1d6e57a2019-09-30 11:55:341372 entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, timeToPixel)) {
Blink Reformat4c46d092018-04-07 15:32:371373 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341374 }
1375 if (!text || !text.length) {
Blink Reformat4c46d092018-04-07 15:32:371376 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341377 }
Blink Reformat4c46d092018-04-07 15:32:371378 context.fillStyle = this._dataProvider.textColor(entryIndex);
1379 context.fillText(text, barX + textPadding, barY + barHeight - this._textBaseline);
1380 }
1381
1382 context.restore();
1383
1384 this._drawGroupHeaders(width, height);
1385 this._drawFlowEvents(context, width, height);
1386 this._drawMarkers();
Paul Lewis2cfa94e2020-01-13 10:30:151387 const dividersData = TimelineGrid.calculateGridOffsets(this);
Paul Lewisead45752020-06-23 09:51:361388 const navStartTimes = Array.from(this._dataProvider.navStartTimes().values());
1389
1390 let navStartTimeIndex = 0;
Paul Lewiscf1f8a42020-11-04 21:05:241391 /**
1392 * @param {number} time
1393 */
Paul Lewisead45752020-06-23 09:51:361394 const drawAdjustedTime = time => {
1395 if (navStartTimes.length === 0) {
1396 return this.formatValue(time, dividersData.precision);
1397 }
1398
1399 // Track when the time crosses the boundary to the next nav start record,
1400 // and when it does, move the nav start array index accordingly.
1401 const hasNextNavStartTime = navStartTimes.length > navStartTimeIndex + 1;
1402 if (hasNextNavStartTime && time > navStartTimes[navStartTimeIndex + 1].startTime) {
1403 navStartTimeIndex++;
1404 }
1405
1406 // Adjust the time by the nearest nav start marker's value.
1407 const nearestMarker = navStartTimes[navStartTimeIndex];
1408 if (nearestMarker) {
1409 time -= nearestMarker.startTime - this.zeroTime();
1410 }
1411
1412 return this.formatValue(time, dividersData.precision);
1413 };
1414
Paul Lewis2cfa94e2020-01-13 10:30:151415 TimelineGrid.drawCanvasGrid(context, dividersData);
Blink Reformat4c46d092018-04-07 15:32:371416 if (this._rulerEnabled) {
Paul Lewisead45752020-06-23 09:51:361417 TimelineGrid.drawCanvasHeaders(context, dividersData, drawAdjustedTime, 3, HeaderHeight);
Blink Reformat4c46d092018-04-07 15:32:371418 }
1419
1420 this._updateElementPosition(this._highlightElement, this._highlightedEntryIndex);
1421 this._updateElementPosition(this._selectedElement, this._selectedEntryIndex);
1422 this._updateMarkerHighlight();
1423 }
1424
Alexei Filippov57ccafb2018-08-14 20:59:051425 _initWebGL() {
1426 const gl = /** @type {?WebGLRenderingContext} */ (this._canvasGL.getContext('webgl'));
1427 if (!gl) {
1428 console.error('Failed to obtain WebGL context.');
1429 this._useWebGL = false; // Fallback to use canvas.
1430 return;
1431 }
1432
1433 const vertexShaderSource = `
1434 attribute vec2 aVertexPosition;
Alexei Filippovaf3ffed2018-08-18 01:56:091435 attribute float aVertexColor;
Alexei Filippov57ccafb2018-08-14 20:59:051436
1437 uniform vec2 uScalingFactor;
1438 uniform vec2 uShiftVector;
1439
Alexei Filippovaf3ffed2018-08-18 01:56:091440 varying mediump vec2 vPalettePosition;
Alexei Filippov57ccafb2018-08-14 20:59:051441
1442 void main() {
1443 vec2 shiftedPosition = aVertexPosition - uShiftVector;
1444 gl_Position = vec4(shiftedPosition * uScalingFactor + vec2(-1.0, 1.0), 0.0, 1.0);
Alexei Filippovaf3ffed2018-08-18 01:56:091445 vPalettePosition = vec2(aVertexColor, 0.5);
Alexei Filippov57ccafb2018-08-14 20:59:051446 }`;
1447
1448 const fragmentShaderSource = `
Alexei Filippovaf3ffed2018-08-18 01:56:091449 varying mediump vec2 vPalettePosition;
1450 uniform sampler2D uSampler;
Alexei Filippov57ccafb2018-08-14 20:59:051451
1452 void main() {
Alexei Filippovaf3ffed2018-08-18 01:56:091453 gl_FragColor = texture2D(uSampler, vPalettePosition);
Alexei Filippov57ccafb2018-08-14 20:59:051454 }`;
1455
1456 /**
1457 * @param {!WebGLRenderingContext} gl
1458 * @param {number} type
1459 * @param {string} source
1460 * @return {?WebGLShader}
1461 */
1462 function loadShader(gl, type, source) {
1463 const shader = gl.createShader(type);
Paul Lewiscf1f8a42020-11-04 21:05:241464 if (!shader) {
1465 return null;
1466 }
1467
Alexei Filippov57ccafb2018-08-14 20:59:051468 gl.shaderSource(shader, source);
1469 gl.compileShader(shader);
Tim van der Lippe1d6e57a2019-09-30 11:55:341470 if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
Alexei Filippov57ccafb2018-08-14 20:59:051471 return shader;
Tim van der Lippe1d6e57a2019-09-30 11:55:341472 }
Alexei Filippov57ccafb2018-08-14 20:59:051473 console.error('Shader compile error: ' + gl.getShaderInfoLog(shader));
1474 gl.deleteShader(shader);
1475 return null;
1476 }
1477
1478 const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
1479 const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
1480
1481 const shaderProgram = gl.createProgram();
Paul Lewiscf1f8a42020-11-04 21:05:241482 if (!shaderProgram || !vertexShader || !fragmentShader) {
1483 return;
1484 }
Alexei Filippov57ccafb2018-08-14 20:59:051485 gl.attachShader(shaderProgram, vertexShader);
1486 gl.attachShader(shaderProgram, fragmentShader);
1487 gl.linkProgram(shaderProgram);
1488
1489 if (gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
1490 this._shaderProgram = shaderProgram;
1491 gl.useProgram(shaderProgram);
1492 } else {
Alexei Filippov57ccafb2018-08-14 20:59:051493 this._shaderProgram = null;
Paul Lewiscf1f8a42020-11-04 21:05:241494 throw new Error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
Alexei Filippov57ccafb2018-08-14 20:59:051495 }
Alexei Filippovaf3ffed2018-08-18 01:56:091496
1497 this._vertexBuffer = gl.createBuffer();
1498 this._colorBuffer = gl.createBuffer();
1499
1500 this._uScalingFactor = gl.getUniformLocation(shaderProgram, 'uScalingFactor');
1501 this._uShiftVector = gl.getUniformLocation(shaderProgram, 'uShiftVector');
1502 const uSampler = gl.getUniformLocation(shaderProgram, 'uSampler');
1503 gl.uniform1i(uSampler, 0);
1504 this._aVertexPosition = gl.getAttribLocation(this._shaderProgram, 'aVertexPosition');
1505 this._aVertexColor = gl.getAttribLocation(this._shaderProgram, 'aVertexColor');
1506 gl.enableVertexAttribArray(this._aVertexPosition);
1507 gl.enableVertexAttribArray(this._aVertexColor);
Alexei Filippov57ccafb2018-08-14 20:59:051508 }
1509
1510 _setupGLGeometry() {
1511 const gl = /** @type {?WebGLRenderingContext} */ (this._canvasGL.getContext('webgl'));
Tim van der Lippe1d6e57a2019-09-30 11:55:341512 if (!gl) {
Alexei Filippov57ccafb2018-08-14 20:59:051513 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341514 }
Alexei Filippov57ccafb2018-08-14 20:59:051515
1516 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:341517 if (!timelineData) {
Alexei Filippov57ccafb2018-08-14 20:59:051518 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341519 }
Alexei Filippov57ccafb2018-08-14 20:59:051520
1521 const entryTotalTimes = timelineData.entryTotalTimes;
1522 const entryStartTimes = timelineData.entryStartTimes;
1523 const entryLevels = timelineData.entryLevels;
1524
Alexei Filippovaf3ffed2018-08-18 01:56:091525 const verticesPerBar = 6;
1526 const vertexArray = new Float32Array(entryTotalTimes.length * verticesPerBar * 2);
1527 let colorArray = new Uint8Array(entryTotalTimes.length * verticesPerBar);
Alexei Filippov57ccafb2018-08-14 20:59:051528 let vertex = 0;
Alexei Filippovaf3ffed2018-08-18 01:56:091529 /** @type {!Map<string, number>} */
Alexei Filippovc34372c2018-08-16 20:37:391530 const parsedColorCache = new Map();
Alexei Filippovaf3ffed2018-08-18 01:56:091531 /** @type {!Array<number>} */
1532 const colors = [];
1533
Paul Lewiscf1f8a42020-11-04 21:05:241534 const visibleLevels = this._visibleLevels || [];
1535 const rawTimelineData = this._rawTimelineData || {groups: []};
1536
1537 const collapsedOverviewLevels = new Array(visibleLevels.length);
1538 const groups = rawTimelineData.groups || [];
Alexei Filippov5f6b11d2018-08-18 03:30:281539 this._forEachGroup((offset, index, group) => {
Tim van der Lippe1d6e57a2019-09-30 11:55:341540 if (group.style.useFirstLineForOverview || !this._isGroupCollapsible(index) || group.expanded) {
Alexei Filippov5f6b11d2018-08-18 03:30:281541 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341542 }
Alexei Filippov5f6b11d2018-08-18 03:30:281543 let nextGroup = index + 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341544 while (nextGroup < groups.length && groups[nextGroup].style.nestingLevel > group.style.nestingLevel) {
Alexei Filippov5f6b11d2018-08-18 03:30:281545 ++nextGroup;
Tim van der Lippe1d6e57a2019-09-30 11:55:341546 }
Alexei Filippov5f6b11d2018-08-18 03:30:281547 const endLevel = nextGroup < groups.length ? groups[nextGroup].startLevel : this._dataProvider.maxStackDepth();
Tim van der Lippe1d6e57a2019-09-30 11:55:341548 for (let i = group.startLevel; i < endLevel; ++i) {
Alexei Filippov5f6b11d2018-08-18 03:30:281549 collapsedOverviewLevels[i] = offset;
Tim van der Lippe1d6e57a2019-09-30 11:55:341550 }
Alexei Filippov5f6b11d2018-08-18 03:30:281551 });
1552
Alexei Filippov57ccafb2018-08-14 20:59:051553 for (let i = 0; i < entryTotalTimes.length; ++i) {
1554 const level = entryLevels[i];
Alexei Filippov5f6b11d2018-08-18 03:30:281555 const collapsedGroupOffset = collapsedOverviewLevels[level];
Paul Lewiscf1f8a42020-11-04 21:05:241556 if (!visibleLevels[level] && !collapsedGroupOffset) {
Alexei Filippov57ccafb2018-08-14 20:59:051557 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341558 }
Paul Lewiscf1f8a42020-11-04 21:05:241559 if (!this._entryColorsCache) {
1560 continue;
1561 }
1562
Alexei Filippove5197622018-08-18 03:04:331563 const color = this._entryColorsCache[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:341564 if (!color) {
Alexei Filippov57ccafb2018-08-14 20:59:051565 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341566 }
Alexei Filippovaf3ffed2018-08-18 01:56:091567 let colorIndex = parsedColorCache.get(color);
1568 if (colorIndex === undefined) {
Paul Lewiscf1f8a42020-11-04 21:05:241569 const parsedColor = Common.Color.Color.parse(color);
1570 if (parsedColor) {
1571 const rgba = parsedColor.canonicalRGBA();
1572 rgba[3] = Math.round(rgba[3] * 255);
1573 colorIndex = colors.length / 4;
1574 colors.push(...rgba);
1575 if (colorIndex === 256) {
1576 colorArray = new Uint8Array(colorArray);
1577 }
Tim van der Lippe1d6e57a2019-09-30 11:55:341578 }
Paul Lewiscf1f8a42020-11-04 21:05:241579
1580 if (colorIndex) {
1581 parsedColorCache.set(color, colorIndex);
1582 }
Alexei Filippovc34372c2018-08-16 20:37:391583 }
Tim van der Lippe1d6e57a2019-09-30 11:55:341584 for (let j = 0; j < verticesPerBar; ++j) {
Paul Lewiscf1f8a42020-11-04 21:05:241585 if (colorIndex) {
1586 colorArray[vertex + j] = colorIndex;
1587 }
Tim van der Lippe1d6e57a2019-09-30 11:55:341588 }
Alexei Filippov57ccafb2018-08-14 20:59:051589
1590 const vpos = vertex * 2;
1591 const x0 = entryStartTimes[i] - this._minimumBoundary;
1592 const x1 = x0 + entryTotalTimes[i];
Alexei Filippov5f6b11d2018-08-18 03:30:281593 const y0 = collapsedGroupOffset || this._levelToOffset(level);
Alexei Filippov57ccafb2018-08-14 20:59:051594 const y1 = y0 + this._levelHeight(level) - 1;
1595 vertexArray[vpos + 0] = x0;
1596 vertexArray[vpos + 1] = y0;
1597 vertexArray[vpos + 2] = x1;
1598 vertexArray[vpos + 3] = y0;
1599 vertexArray[vpos + 4] = x0;
1600 vertexArray[vpos + 5] = y1;
1601 vertexArray[vpos + 6] = x0;
1602 vertexArray[vpos + 7] = y1;
1603 vertexArray[vpos + 8] = x1;
1604 vertexArray[vpos + 9] = y0;
1605 vertexArray[vpos + 10] = x1;
1606 vertexArray[vpos + 11] = y1;
1607
Alexei Filippovaf3ffed2018-08-18 01:56:091608 vertex += verticesPerBar;
Alexei Filippov57ccafb2018-08-14 20:59:051609 }
Alexei Filippov57ccafb2018-08-14 20:59:051610 this._vertexCount = vertex;
1611
Alexei Filippovaf3ffed2018-08-18 01:56:091612 const paletteTexture = gl.createTexture();
1613 gl.bindTexture(gl.TEXTURE_2D, paletteTexture);
1614 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
1615 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
1616 gl.activeTexture(gl.TEXTURE0);
1617
1618 const numColors = colors.length / 4;
1619 const useShortForColors = numColors >= 256;
1620 const width = !useShortForColors ? 256 : Math.min(1 << 16, gl.getParameter(gl.MAX_TEXTURE_SIZE));
1621 console.assert(numColors <= width, 'Too many colors');
1622 const height = 1;
1623 const colorIndexType = useShortForColors ? gl.UNSIGNED_SHORT : gl.UNSIGNED_BYTE;
1624 if (useShortForColors) {
1625 const factor = (1 << 16) / width;
Tim van der Lippe1d6e57a2019-09-30 11:55:341626 for (let i = 0; i < vertex; ++i) {
Alexei Filippovaf3ffed2018-08-18 01:56:091627 colorArray[i] *= factor;
Tim van der Lippe1d6e57a2019-09-30 11:55:341628 }
Alexei Filippovaf3ffed2018-08-18 01:56:091629 }
1630
1631 const pixels = new Uint8Array(width * 4);
1632 pixels.set(colors);
1633 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
1634
Paul Lewiscf1f8a42020-11-04 21:05:241635 if (this._vertexBuffer && this._aVertexPosition) {
1636 gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
1637 gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW);
1638 gl.vertexAttribPointer(this._aVertexPosition, /* vertexComponents */ 2, gl.FLOAT, false, 0, 0);
1639 }
Alexei Filippov57ccafb2018-08-14 20:59:051640
Paul Lewiscf1f8a42020-11-04 21:05:241641 if (this._colorBuffer && this._aVertexColor) {
1642 gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
1643 gl.bufferData(gl.ARRAY_BUFFER, colorArray, gl.STATIC_DRAW);
1644 gl.vertexAttribPointer(this._aVertexColor, /* colorComponents */ 1, colorIndexType, true, 0, 0);
1645 }
Alexei Filippov57ccafb2018-08-14 20:59:051646 }
1647
1648 _drawGL() {
1649 const gl = /** @type {?WebGLRenderingContext} */ (this._canvasGL.getContext('webgl'));
Tim van der Lippe1d6e57a2019-09-30 11:55:341650 if (!gl) {
Alexei Filippov57ccafb2018-08-14 20:59:051651 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341652 }
Alexei Filippov57ccafb2018-08-14 20:59:051653 const timelineData = this._timelineData();
Tim van der Lippe1d6e57a2019-09-30 11:55:341654 if (!timelineData) {
Alexei Filippov57ccafb2018-08-14 20:59:051655 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341656 }
Alexei Filippov57ccafb2018-08-14 20:59:051657
Alexei Filippov57ccafb2018-08-14 20:59:051658 if (!this._prevTimelineData || timelineData.entryTotalTimes !== this._prevTimelineData.entryTotalTimes) {
1659 this._prevTimelineData = timelineData;
1660 this._setupGLGeometry();
1661 }
1662
Alexei Filippovc34372c2018-08-16 20:37:391663 gl.viewport(0, 0, this._canvasGL.width, this._canvasGL.height);
Alexei Filippov57ccafb2018-08-14 20:59:051664
Tim van der Lippe1d6e57a2019-09-30 11:55:341665 if (!this._vertexCount) {
Alexei Filippov57ccafb2018-08-14 20:59:051666 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341667 }
Alexei Filippov57ccafb2018-08-14 20:59:051668
Alexei Filippovc34372c2018-08-16 20:37:391669 const viewportScale = [2.0 / this.boundarySpan(), -2.0 * window.devicePixelRatio / this._canvasGL.height];
1670 const viewportShift = [this.minimumBoundary() - this.zeroTime(), this._chartViewport.scrollOffset()];
Paul Lewiscf1f8a42020-11-04 21:05:241671 if (this._uScalingFactor) {
1672 gl.uniform2fv(this._uScalingFactor, viewportScale);
1673 }
1674
1675 if (this._uShiftVector) {
1676 gl.uniform2fv(this._uShiftVector, viewportShift);
1677 }
Alexei Filippov57ccafb2018-08-14 20:59:051678
1679 gl.drawArrays(gl.TRIANGLES, 0, this._vertexCount);
1680 }
1681
Blink Reformat4c46d092018-04-07 15:32:371682 /**
1683 * @param {number} width
1684 * @param {number} height
1685 */
1686 _drawGroupHeaders(width, height) {
1687 const context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
1688 const top = this._chartViewport.scrollOffset();
1689 const ratio = window.devicePixelRatio;
Paul Lewiscf1f8a42020-11-04 21:05:241690 if (!this._rawTimelineData) {
1691 return;
1692 }
1693
Blink Reformat4c46d092018-04-07 15:32:371694 const groups = this._rawTimelineData.groups || [];
Tim van der Lippe1d6e57a2019-09-30 11:55:341695 if (!groups.length) {
Blink Reformat4c46d092018-04-07 15:32:371696 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341697 }
Blink Reformat4c46d092018-04-07 15:32:371698
1699 const groupOffsets = this._groupOffsets;
Tim van der Lippe779b46b2021-01-13 13:40:221700 if (groupOffsets === null || groupOffsets === undefined) {
1701 return;
1702 }
1703 const lastGroupOffset = groupOffsets[groupOffsets.length - 1];
Paul Lewisca569a52020-09-09 16:11:511704 const colorUsage = ThemeSupport.ThemeSupport.ColorUsage;
Blink Reformat4c46d092018-04-07 15:32:371705
1706 context.save();
1707 context.scale(ratio, ratio);
1708 context.translate(0, -top);
Tim van der Lippebb769172020-02-12 15:32:441709 const defaultFont = '11px ' + Host.Platform.fontFamily();
Blink Reformat4c46d092018-04-07 15:32:371710 context.font = defaultFont;
1711
Paul Lewisca569a52020-09-09 16:11:511712 context.fillStyle = ThemeSupport.ThemeSupport.instance().patchColorText('#fff', colorUsage.Background);
Alexei Filippov5f6b11d2018-08-18 03:30:281713 this._forEachGroupInViewport((offset, index, group) => {
Blink Reformat4c46d092018-04-07 15:32:371714 const paddingHeight = group.style.padding;
Tim van der Lippe1d6e57a2019-09-30 11:55:341715 if (paddingHeight < 5) {
Blink Reformat4c46d092018-04-07 15:32:371716 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341717 }
Blink Reformat4c46d092018-04-07 15:32:371718 context.fillRect(0, offset - paddingHeight + 2, width, paddingHeight - 4);
1719 });
Tim van der Lippe1d6e57a2019-09-30 11:55:341720 if (groups.length && lastGroupOffset < top + height) {
Blink Reformat4c46d092018-04-07 15:32:371721 context.fillRect(0, lastGroupOffset + 2, width, top + height - lastGroupOffset);
Tim van der Lippe1d6e57a2019-09-30 11:55:341722 }
Blink Reformat4c46d092018-04-07 15:32:371723
Paul Lewisca569a52020-09-09 16:11:511724 context.strokeStyle = ThemeSupport.ThemeSupport.instance().patchColorText('#eee', colorUsage.Background);
Blink Reformat4c46d092018-04-07 15:32:371725 context.beginPath();
Alexei Filippov5f6b11d2018-08-18 03:30:281726 this._forEachGroupInViewport((offset, index, group, isFirst) => {
Tim van der Lippe1d6e57a2019-09-30 11:55:341727 if (isFirst || group.style.padding < 4) {
Blink Reformat4c46d092018-04-07 15:32:371728 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341729 }
Blink Reformat4c46d092018-04-07 15:32:371730 hLine(offset - 2.5);
1731 });
1732 hLine(lastGroupOffset + 1.5);
1733 context.stroke();
1734
Alexei Filippov5f6b11d2018-08-18 03:30:281735 this._forEachGroupInViewport((offset, index, group) => {
Tim van der Lippe1d6e57a2019-09-30 11:55:341736 if (group.style.useFirstLineForOverview) {
Blink Reformat4c46d092018-04-07 15:32:371737 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341738 }
Blink Reformat4c46d092018-04-07 15:32:371739 if (!this._isGroupCollapsible(index) || group.expanded) {
Anubha Mathur72dd5822019-06-13 23:05:191740 if (!group.style.shareHeaderLine && this._isGroupFocused(index)) {
Blink Reformat4c46d092018-04-07 15:32:371741 context.fillStyle = group.style.backgroundColor;
1742 context.fillRect(0, offset, width, group.style.height);
1743 }
1744 return;
1745 }
Tim van der Lippe1d6e57a2019-09-30 11:55:341746 if (this._useWebGL) {
Alexei Filippov5f6b11d2018-08-18 03:30:281747 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341748 }
Blink Reformat4c46d092018-04-07 15:32:371749 let nextGroup = index + 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:341750 while (nextGroup < groups.length && groups[nextGroup].style.nestingLevel > group.style.nestingLevel) {
Blink Reformat4c46d092018-04-07 15:32:371751 nextGroup++;
Tim van der Lippe1d6e57a2019-09-30 11:55:341752 }
Blink Reformat4c46d092018-04-07 15:32:371753 const endLevel = nextGroup < groups.length ? groups[nextGroup].startLevel : this._dataProvider.maxStackDepth();
Alexei Filippov23428d42018-04-28 01:09:071754 this._drawCollapsedOverviewForGroup(group, offset, endLevel);
Blink Reformat4c46d092018-04-07 15:32:371755 });
1756
1757 context.save();
Alexei Filippov5f6b11d2018-08-18 03:30:281758 this._forEachGroupInViewport((offset, index, group) => {
Blink Reformat4c46d092018-04-07 15:32:371759 context.font = group.style.font;
1760 if (this._isGroupCollapsible(index) && !group.expanded || group.style.shareHeaderLine) {
1761 const width = this._labelWidthForGroup(context, group) + 2;
Tim van der Lippe1d6e57a2019-09-30 11:55:341762 if (this._isGroupFocused(index)) {
Blink Reformat4c46d092018-04-07 15:32:371763 context.fillStyle = this._selectedGroupBackroundColor;
Tim van der Lippe1d6e57a2019-09-30 11:55:341764 } else {
Paul Lewiscf1f8a42020-11-04 21:05:241765 const parsedColor = Common.Color.Color.parse(group.style.backgroundColor);
1766 if (parsedColor) {
1767 context.fillStyle = /** @type {string} */ (parsedColor.setAlpha(0.8).asString(null));
1768 }
Tim van der Lippe1d6e57a2019-09-30 11:55:341769 }
Blink Reformat4c46d092018-04-07 15:32:371770
1771 context.fillRect(
1772 this._headerLeftPadding - this._headerLabelXPadding, offset + this._headerLabelYPadding, width,
1773 group.style.height - 2 * this._headerLabelYPadding);
1774 }
1775 context.fillStyle = group.style.color;
1776 context.fillText(
1777 group.name, Math.floor(this._expansionArrowIndent * (group.style.nestingLevel + 1) + this._arrowSide),
1778 offset + group.style.height - this._textBaseline);
1779 });
1780 context.restore();
1781
Paul Lewisca569a52020-09-09 16:11:511782 context.fillStyle = ThemeSupport.ThemeSupport.instance().patchColorText('#6e6e6e', colorUsage.Foreground);
Blink Reformat4c46d092018-04-07 15:32:371783 context.beginPath();
Alexei Filippov5f6b11d2018-08-18 03:30:281784 this._forEachGroupInViewport((offset, index, group) => {
Blink Reformat4c46d092018-04-07 15:32:371785 if (this._isGroupCollapsible(index)) {
1786 drawExpansionArrow.call(
1787 this, this._expansionArrowIndent * (group.style.nestingLevel + 1),
Tim van der Lipped7cfd142021-01-07 12:17:241788 offset + group.style.height - this._textBaseline - this._arrowSide / 2, Boolean(group.expanded));
Blink Reformat4c46d092018-04-07 15:32:371789 }
1790 });
1791 context.fill();
1792
Paul Lewisca569a52020-09-09 16:11:511793 context.strokeStyle = ThemeSupport.ThemeSupport.instance().patchColorText('#ddd', colorUsage.Background);
Blink Reformat4c46d092018-04-07 15:32:371794 context.beginPath();
1795 context.stroke();
1796
Alexei Filippov5f6b11d2018-08-18 03:30:281797 this._forEachGroupInViewport((offset, index, group, isFirst, groupHeight) => {
Anubha Mathur72dd5822019-06-13 23:05:191798 if (this._isGroupFocused(index)) {
Alexei Filippov23428d42018-04-28 01:09:071799 const lineWidth = 2;
1800 const bracketLength = 10;
Blink Reformat4c46d092018-04-07 15:32:371801 context.fillStyle = this._selectedGroupBorderColor;
Alexei Filippov23428d42018-04-28 01:09:071802 context.fillRect(0, offset - lineWidth, lineWidth, groupHeight - group.style.padding + 2 * lineWidth);
1803 context.fillRect(0, offset - lineWidth, bracketLength, lineWidth);
1804 context.fillRect(0, offset + groupHeight - group.style.padding, bracketLength, lineWidth);
Blink Reformat4c46d092018-04-07 15:32:371805 }
1806 });
1807
1808 context.restore();
1809
1810 /**
1811 * @param {number} y
1812 */
1813 function hLine(y) {
1814 context.moveTo(0, y);
1815 context.lineTo(width, y);
1816 }
1817
1818 /**
1819 * @param {number} x
1820 * @param {number} y
1821 * @param {boolean} expanded
Tim van der Lippefd2b2ce2020-01-03 15:05:181822 * @this {FlameChart}
Blink Reformat4c46d092018-04-07 15:32:371823 */
1824 function drawExpansionArrow(x, y, expanded) {
1825 const arrowHeight = this._arrowSide * Math.sqrt(3) / 2;
1826 const arrowCenterOffset = Math.round(arrowHeight / 2);
1827 context.save();
1828 context.translate(x, y);
1829 context.rotate(expanded ? Math.PI / 2 : 0);
1830 context.moveTo(-arrowCenterOffset, -this._arrowSide / 2);
1831 context.lineTo(-arrowCenterOffset, this._arrowSide / 2);
1832 context.lineTo(arrowHeight - arrowCenterOffset, 0);
1833 context.restore();
1834 }
1835 }
1836
1837 /**
Paul Lewiscf1f8a42020-11-04 21:05:241838 * @param {function(number, number, !Group, boolean, number):void} callback
Blink Reformat4c46d092018-04-07 15:32:371839 */
1840 _forEachGroup(callback) {
Paul Lewiscf1f8a42020-11-04 21:05:241841 if (!this._rawTimelineData) {
1842 return;
1843 }
Blink Reformat4c46d092018-04-07 15:32:371844 const groups = this._rawTimelineData.groups || [];
Tim van der Lippe1d6e57a2019-09-30 11:55:341845 if (!groups.length) {
Blink Reformat4c46d092018-04-07 15:32:371846 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341847 }
Blink Reformat4c46d092018-04-07 15:32:371848 const groupOffsets = this._groupOffsets;
Paul Lewiscf1f8a42020-11-04 21:05:241849 if (!groupOffsets) {
1850 return;
1851 }
1852
1853 /** @type {!Array<{nestingLevel: number, visible: boolean}>} */
Blink Reformat4c46d092018-04-07 15:32:371854 const groupStack = [{nestingLevel: -1, visible: true}];
1855 for (let i = 0; i < groups.length; ++i) {
1856 const groupTop = groupOffsets[i];
1857 const group = groups[i];
Blink Reformat4c46d092018-04-07 15:32:371858 let firstGroup = true;
Tim van der Lippe779b46b2021-01-13 13:40:221859 let last = groupStack[groupStack.length - 1];
Paul Lewiscf1f8a42020-11-04 21:05:241860 while (last && last.nestingLevel >= group.style.nestingLevel) {
Blink Reformat4c46d092018-04-07 15:32:371861 groupStack.pop();
1862 firstGroup = false;
Tim van der Lippe779b46b2021-01-13 13:40:221863 last = groupStack[groupStack.length - 1];
Blink Reformat4c46d092018-04-07 15:32:371864 }
Tim van der Lippe779b46b2021-01-13 13:40:221865 last = groupStack[groupStack.length - 1];
Paul Lewiscf1f8a42020-11-04 21:05:241866 const parentGroupVisible = last ? last.visible : false;
Blink Reformat4c46d092018-04-07 15:32:371867 const thisGroupVisible = parentGroupVisible && (!this._isGroupCollapsible(i) || group.expanded);
Tim van der Lipped7cfd142021-01-07 12:17:241868 groupStack.push({nestingLevel: group.style.nestingLevel, visible: Boolean(thisGroupVisible)});
Alexei Filippov23428d42018-04-28 01:09:071869 const nextOffset = i === groups.length - 1 ? groupOffsets[i + 1] + group.style.padding : groupOffsets[i + 1];
Tim van der Lippe1d6e57a2019-09-30 11:55:341870 if (!parentGroupVisible) {
Blink Reformat4c46d092018-04-07 15:32:371871 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341872 }
Blink Reformat4c46d092018-04-07 15:32:371873 callback(groupTop, i, group, firstGroup, nextOffset - groupTop);
1874 }
1875 }
1876
1877 /**
Paul Lewiscf1f8a42020-11-04 21:05:241878 * @param {function(number, number, !Group, boolean, number):void} callback
Alexei Filippov5f6b11d2018-08-18 03:30:281879 */
1880 _forEachGroupInViewport(callback) {
1881 const top = this._chartViewport.scrollOffset();
1882 this._forEachGroup((groupTop, index, group, firstGroup, height) => {
Tim van der Lippe1d6e57a2019-09-30 11:55:341883 if (groupTop - group.style.padding > top + this._offsetHeight) {
Alexei Filippov5f6b11d2018-08-18 03:30:281884 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341885 }
1886 if (groupTop + height < top) {
Alexei Filippov5f6b11d2018-08-18 03:30:281887 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:341888 }
Alexei Filippov5f6b11d2018-08-18 03:30:281889 callback(groupTop, index, group, firstGroup, height);
1890 });
1891 }
1892
1893 /**
Blink Reformat4c46d092018-04-07 15:32:371894 * @param {!CanvasRenderingContext2D} context
Tim van der Lippe8ef250c2020-02-20 16:29:251895 * @param {!Group} group
Blink Reformat4c46d092018-04-07 15:32:371896 * @return {number}
1897 */
1898 _labelWidthForGroup(context, group) {
Tim van der Lippebb769172020-02-12 15:32:441899 return UI.UIUtils.measureTextWidth(context, group.name) +
1900 this._expansionArrowIndent * (group.style.nestingLevel + 1) + 2 * this._headerLabelXPadding;
Blink Reformat4c46d092018-04-07 15:32:371901 }
1902
1903 /**
Tim van der Lippe8ef250c2020-02-20 16:29:251904 * @param {!Group} group
Blink Reformat4c46d092018-04-07 15:32:371905 * @param {number} y
1906 * @param {number} endLevel
1907 */
1908 _drawCollapsedOverviewForGroup(group, y, endLevel) {
Tim van der Lippebb769172020-02-12 15:32:441909 const range = new Common.SegmentedRange.SegmentedRange(mergeCallback);
Blink Reformat4c46d092018-04-07 15:32:371910 const timeWindowLeft = this._chartViewport.windowLeftTime();
1911 const timeWindowRight = this._chartViewport.windowRightTime();
1912 const context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
1913 const barHeight = group.style.height;
Paul Lewiscf1f8a42020-11-04 21:05:241914 if (!this._rawTimelineData) {
1915 return;
1916 }
Blink Reformat4c46d092018-04-07 15:32:371917 const entryStartTimes = this._rawTimelineData.entryStartTimes;
1918 const entryTotalTimes = this._rawTimelineData.entryTotalTimes;
1919 const timeToPixel = this._chartViewport.timeToPixel();
1920
1921 for (let level = group.startLevel; level < endLevel; ++level) {
Paul Lewiscf1f8a42020-11-04 21:05:241922 /** @type {!Array.<number>} */
1923 const levelIndexes = this._timelineLevels ? this._timelineLevels[level] : [];
Blink Reformat4c46d092018-04-07 15:32:371924 const rightIndexOnLevel =
1925 levelIndexes.lowerBound(timeWindowRight, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1;
1926 let lastDrawOffset = Infinity;
1927
1928 for (let entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) {
1929 const entryIndex = levelIndexes[entryIndexOnLevel];
1930 const entryStartTime = entryStartTimes[entryIndex];
1931 const barX = this._timeToPositionClipped(entryStartTime);
1932 const entryEndTime = entryStartTime + entryTotalTimes[entryIndex];
Tim van der Lippe1d6e57a2019-09-30 11:55:341933 if (isNaN(entryEndTime) || barX >= lastDrawOffset) {
Blink Reformat4c46d092018-04-07 15:32:371934 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:341935 }
1936 if (entryEndTime <= timeWindowLeft) {
Blink Reformat4c46d092018-04-07 15:32:371937 break;
Tim van der Lippe1d6e57a2019-09-30 11:55:341938 }
Blink Reformat4c46d092018-04-07 15:32:371939 lastDrawOffset = barX;
Paul Lewiscf1f8a42020-11-04 21:05:241940 const color = this._entryColorsCache ? this._entryColorsCache[entryIndex] : '';
Blink Reformat4c46d092018-04-07 15:32:371941 const endBarX = this._timeToPositionClipped(entryEndTime);
1942 if (group.style.useDecoratorsForOverview && this._dataProvider.forceDecoration(entryIndex)) {
1943 const unclippedBarX = this._chartViewport.timeToPosition(entryStartTime);
1944 const barWidth = endBarX - barX;
1945 context.beginPath();
1946 context.fillStyle = color;
1947 context.fillRect(barX, y, barWidth, barHeight - 1);
1948 this._dataProvider.decorateEntry(
1949 entryIndex, context, '', barX, y, barWidth, barHeight, unclippedBarX, timeToPixel);
1950 continue;
1951 }
Tim van der Lippebb769172020-02-12 15:32:441952 range.append(new Common.SegmentedRange.Segment(barX, endBarX, color));
Blink Reformat4c46d092018-04-07 15:32:371953 }
1954 }
1955
1956 const segments = range.segments().slice().sort((a, b) => a.data.localeCompare(b.data));
1957 let lastColor;
1958 context.beginPath();
1959 for (let i = 0; i < segments.length; ++i) {
1960 const segment = segments[i];
1961 if (lastColor !== segments[i].data) {
1962 context.fill();
1963 context.beginPath();
1964 lastColor = segments[i].data;
1965 context.fillStyle = lastColor;
1966 }
Alexei Filippov23428d42018-04-28 01:09:071967 context.rect(segment.begin, y, segment.end - segment.begin, barHeight);
Blink Reformat4c46d092018-04-07 15:32:371968 }
1969 context.fill();
1970
1971 /**
Tim van der Lippebb769172020-02-12 15:32:441972 * @param {!Common.SegmentedRange.Segment} a
1973 * @param {!Common.SegmentedRange.Segment} b
1974 * @return {?Common.SegmentedRange.Segment}
Blink Reformat4c46d092018-04-07 15:32:371975 */
1976 function mergeCallback(a, b) {
1977 return a.data === b.data && a.end + 0.4 > b.end ? a : null;
1978 }
1979 }
1980
1981 /**
1982 * @param {!CanvasRenderingContext2D} context
1983 * @param {number} height
1984 * @param {number} width
1985 */
1986 _drawFlowEvents(context, width, height) {
1987 context.save();
1988 const ratio = window.devicePixelRatio;
1989 const top = this._chartViewport.scrollOffset();
1990 const arrowWidth = 6;
1991 context.scale(ratio, ratio);
1992 context.translate(0, -top);
1993
1994 context.fillStyle = '#7f5050';
1995 context.strokeStyle = '#7f5050';
1996 const td = this._timelineData();
Paul Lewiscf1f8a42020-11-04 21:05:241997 if (!td) {
1998 return;
1999 }
2000
Blink Reformat4c46d092018-04-07 15:32:372001 const endIndex = td.flowStartTimes.lowerBound(this._chartViewport.windowRightTime());
2002
2003 context.lineWidth = 0.5;
2004 for (let i = 0; i < endIndex; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:342005 if (!td.flowEndTimes[i] || td.flowEndTimes[i] < this._chartViewport.windowLeftTime()) {
Blink Reformat4c46d092018-04-07 15:32:372006 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:342007 }
Blink Reformat4c46d092018-04-07 15:32:372008 const startX = this._chartViewport.timeToPosition(td.flowStartTimes[i]);
2009 const endX = this._chartViewport.timeToPosition(td.flowEndTimes[i]);
2010 const startLevel = td.flowStartLevels[i];
2011 const endLevel = td.flowEndLevels[i];
2012 const startY = this._levelToOffset(startLevel) + this._levelHeight(startLevel) / 2;
2013 const endY = this._levelToOffset(endLevel) + this._levelHeight(endLevel) / 2;
2014
2015
2016 const segment = Math.min((endX - startX) / 4, 40);
2017 const distanceTime = td.flowEndTimes[i] - td.flowStartTimes[i];
2018 const distanceY = (endY - startY) / 10;
2019 const spread = 30;
2020 const lineY = distanceTime < 1 ? startY : spread + Math.max(0, startY + distanceY * (i % spread));
2021
2022 const p = [];
2023 p.push({x: startX, y: startY});
2024 p.push({x: startX + arrowWidth, y: startY});
2025 p.push({x: startX + segment + 2 * arrowWidth, y: startY});
2026 p.push({x: startX + segment, y: lineY});
2027 p.push({x: startX + segment * 2, y: lineY});
2028 p.push({x: endX - segment * 2, y: lineY});
2029 p.push({x: endX - segment, y: lineY});
2030 p.push({x: endX - segment - 2 * arrowWidth, y: endY});
2031 p.push({x: endX - arrowWidth, y: endY});
2032
2033 context.beginPath();
2034 context.moveTo(p[0].x, p[0].y);
2035 context.lineTo(p[1].x, p[1].y);
2036 context.bezierCurveTo(p[2].x, p[2].y, p[3].x, p[3].y, p[4].x, p[4].y);
2037 context.lineTo(p[5].x, p[5].y);
2038 context.bezierCurveTo(p[6].x, p[6].y, p[7].x, p[7].y, p[8].x, p[8].y);
2039 context.stroke();
2040
2041 context.beginPath();
2042 context.arc(startX, startY, 2, -Math.PI / 2, Math.PI / 2, false);
2043 context.fill();
2044
2045 context.beginPath();
2046 context.moveTo(endX, endY);
2047 context.lineTo(endX - arrowWidth, endY - 3);
2048 context.lineTo(endX - arrowWidth, endY + 3);
2049 context.fill();
2050 }
2051 context.restore();
2052 }
2053
2054 _drawMarkers() {
Paul Lewiscf1f8a42020-11-04 21:05:242055 const timelineData = this._timelineData();
2056 if (!timelineData) {
2057 return;
2058 }
2059 const markers = timelineData.markers;
Blink Reformat4c46d092018-04-07 15:32:372060 const left = this._markerIndexBeforeTime(this.minimumBoundary());
2061 const rightBoundary = this.maximumBoundary();
2062 const timeToPixel = this._chartViewport.timeToPixel();
2063
2064 const context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
2065 context.save();
2066 const ratio = window.devicePixelRatio;
2067 context.scale(ratio, ratio);
2068 context.translate(0, 3);
Tim van der Lippefd2b2ce2020-01-03 15:05:182069 const height = HeaderHeight - 1;
Blink Reformat4c46d092018-04-07 15:32:372070 for (let i = left; i < markers.length; i++) {
2071 const timestamp = markers[i].startTime();
Tim van der Lippe1d6e57a2019-09-30 11:55:342072 if (timestamp > rightBoundary) {
Blink Reformat4c46d092018-04-07 15:32:372073 break;
Tim van der Lippe1d6e57a2019-09-30 11:55:342074 }
Blink Reformat4c46d092018-04-07 15:32:372075 markers[i].draw(context, this._chartViewport.timeToPosition(timestamp), height, timeToPixel);
2076 }
2077 context.restore();
2078 }
2079
2080 _updateMarkerHighlight() {
2081 const element = this._markerHighlighElement;
Tim van der Lippe1d6e57a2019-09-30 11:55:342082 if (element.parentElement) {
Blink Reformat4c46d092018-04-07 15:32:372083 element.remove();
Tim van der Lippe1d6e57a2019-09-30 11:55:342084 }
Blink Reformat4c46d092018-04-07 15:32:372085 const markerIndex = this._highlightedMarkerIndex;
Tim van der Lippe1d6e57a2019-09-30 11:55:342086 if (markerIndex === -1) {
Blink Reformat4c46d092018-04-07 15:32:372087 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:342088 }
Paul Lewiscf1f8a42020-11-04 21:05:242089 const timelineData = this._timelineData();
2090 if (!timelineData) {
2091 return;
2092 }
2093 const marker = timelineData.markers[markerIndex];
Blink Reformat4c46d092018-04-07 15:32:372094 const barX = this._timeToPositionClipped(marker.startTime());
Tim van der Lippe70842f32020-11-23 16:56:572095 UI.Tooltip.Tooltip.install(element, marker.title() || '');
Blink Reformat4c46d092018-04-07 15:32:372096 const style = element.style;
2097 style.left = barX + 'px';
2098 style.backgroundColor = marker.color();
2099 this._viewportElement.appendChild(element);
2100 }
2101
2102 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:182103 * @param {?TimelineData} timelineData
Blink Reformat4c46d092018-04-07 15:32:372104 */
2105 _processTimelineData(timelineData) {
2106 if (!timelineData) {
2107 this._timelineLevels = null;
2108 this._visibleLevelOffsets = null;
2109 this._visibleLevels = null;
2110 this._groupOffsets = null;
2111 this._rawTimelineData = null;
Alexei Filippov57ccafb2018-08-14 20:59:052112 this._forceDecorationCache = null;
Alexei Filippove5197622018-08-18 03:04:332113 this._entryColorsCache = null;
Blink Reformat4c46d092018-04-07 15:32:372114 this._rawTimelineDataLength = 0;
2115 this._selectedGroup = -1;
Anubha Mathur72dd5822019-06-13 23:05:192116 this._keyboardFocusedGroup = -1;
Blink Reformat4c46d092018-04-07 15:32:372117 this._flameChartDelegate.updateSelectedGroup(this, null);
2118 return;
2119 }
2120
2121 this._rawTimelineData = timelineData;
2122 this._rawTimelineDataLength = timelineData.entryStartTimes.length;
Alexei Filippov57ccafb2018-08-14 20:59:052123 this._forceDecorationCache = new Int8Array(this._rawTimelineDataLength);
Alexei Filippove5197622018-08-18 03:04:332124 this._entryColorsCache = new Array(this._rawTimelineDataLength);
2125 for (let i = 0; i < this._rawTimelineDataLength; ++i) {
Alexei Filippov57ccafb2018-08-14 20:59:052126 this._forceDecorationCache[i] = this._dataProvider.forceDecoration(i) ? 1 : 0;
Alexei Filippove5197622018-08-18 03:04:332127 this._entryColorsCache[i] = this._dataProvider.entryColor(i);
2128 }
Blink Reformat4c46d092018-04-07 15:32:372129
2130 const entryCounters = new Uint32Array(this._dataProvider.maxStackDepth() + 1);
Tim van der Lippe1d6e57a2019-09-30 11:55:342131 for (let i = 0; i < timelineData.entryLevels.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:372132 ++entryCounters[timelineData.entryLevels[i]];
Tim van der Lippe1d6e57a2019-09-30 11:55:342133 }
Blink Reformat4c46d092018-04-07 15:32:372134 const levelIndexes = new Array(entryCounters.length);
2135 for (let i = 0; i < levelIndexes.length; ++i) {
2136 levelIndexes[i] = new Uint32Array(entryCounters[i]);
2137 entryCounters[i] = 0;
2138 }
Anubha Mathur72dd5822019-06-13 23:05:192139
Blink Reformat4c46d092018-04-07 15:32:372140 for (let i = 0; i < timelineData.entryLevels.length; ++i) {
2141 const level = timelineData.entryLevels[i];
2142 levelIndexes[level][entryCounters[level]++] = i;
2143 }
2144 this._timelineLevels = levelIndexes;
2145 const groups = this._rawTimelineData.groups || [];
2146 for (let i = 0; i < groups.length; ++i) {
2147 const expanded = this._groupExpansionState[groups[i].name];
Tim van der Lippe1d6e57a2019-09-30 11:55:342148 if (expanded !== undefined) {
Blink Reformat4c46d092018-04-07 15:32:372149 groups[i].expanded = expanded;
Tim van der Lippe1d6e57a2019-09-30 11:55:342150 }
Blink Reformat4c46d092018-04-07 15:32:372151 }
2152 this._updateLevelPositions();
2153 this._updateHeight();
2154
2155 this._selectedGroup = timelineData.selectedGroup ? groups.indexOf(timelineData.selectedGroup) : -1;
Anubha Mathur72dd5822019-06-13 23:05:192156 this._keyboardFocusedGroup = this._selectedGroup;
Blink Reformat4c46d092018-04-07 15:32:372157 this._flameChartDelegate.updateSelectedGroup(this, timelineData.selectedGroup);
2158 }
2159
2160 _updateLevelPositions() {
2161 const levelCount = this._dataProvider.maxStackDepth();
Paul Lewiscf1f8a42020-11-04 21:05:242162 const groups = this._rawTimelineData ? (this._rawTimelineData.groups || []) : [];
Blink Reformat4c46d092018-04-07 15:32:372163 this._visibleLevelOffsets = new Uint32Array(levelCount + 1);
2164 this._visibleLevelHeights = new Uint32Array(levelCount);
2165 this._visibleLevels = new Uint16Array(levelCount);
2166 this._groupOffsets = new Uint32Array(groups.length + 1);
2167
2168 let groupIndex = -1;
Tim van der Lippefd2b2ce2020-01-03 15:05:182169 let currentOffset = this._rulerEnabled ? HeaderHeight + 2 : 2;
Blink Reformat4c46d092018-04-07 15:32:372170 let visible = true;
Paul Lewiscf1f8a42020-11-04 21:05:242171 /** @type {!Array<{nestingLevel: number, visible: boolean}>} */
Blink Reformat4c46d092018-04-07 15:32:372172 const groupStack = [{nestingLevel: -1, visible: true}];
Paul Lewiscf1f8a42020-11-04 21:05:242173 const lastGroupLevel =
Tim van der Lippe779b46b2021-01-13 13:40:222174 Math.max(levelCount, groups.length ? /** @type {!Group} */ (groups[groups.length - 1]).startLevel + 1 : 0);
Blink Reformat4c46d092018-04-07 15:32:372175 let level;
2176 for (level = 0; level < lastGroupLevel; ++level) {
2177 let parentGroupIsVisible = true;
2178 let style;
2179 while (groupIndex < groups.length - 1 && level === groups[groupIndex + 1].startLevel) {
2180 ++groupIndex;
2181 style = groups[groupIndex].style;
2182 let nextLevel = true;
Tim van der Lippe779b46b2021-01-13 13:40:222183 let last = groupStack[groupStack.length - 1];
Paul Lewiscf1f8a42020-11-04 21:05:242184 while (last && last.nestingLevel >= style.nestingLevel) {
Blink Reformat4c46d092018-04-07 15:32:372185 groupStack.pop();
2186 nextLevel = false;
Tim van der Lippe779b46b2021-01-13 13:40:222187 last = groupStack[groupStack.length - 1];
Blink Reformat4c46d092018-04-07 15:32:372188 }
2189 const thisGroupIsVisible =
2190 groupIndex >= 0 && this._isGroupCollapsible(groupIndex) ? groups[groupIndex].expanded : true;
Paul Lewiscf1f8a42020-11-04 21:05:242191
Tim van der Lippe779b46b2021-01-13 13:40:222192 last = groupStack[groupStack.length - 1];
Paul Lewiscf1f8a42020-11-04 21:05:242193 parentGroupIsVisible = last ? last.visible : false;
Tim van der Lipped7cfd142021-01-07 12:17:242194 visible = Boolean(thisGroupIsVisible) && parentGroupIsVisible;
Blink Reformat4c46d092018-04-07 15:32:372195 groupStack.push({nestingLevel: style.nestingLevel, visible: visible});
Tim van der Lippe1d6e57a2019-09-30 11:55:342196 if (parentGroupIsVisible) {
Blink Reformat4c46d092018-04-07 15:32:372197 currentOffset += nextLevel ? 0 : style.padding;
Tim van der Lippe1d6e57a2019-09-30 11:55:342198 }
Blink Reformat4c46d092018-04-07 15:32:372199 this._groupOffsets[groupIndex] = currentOffset;
Tim van der Lippe1d6e57a2019-09-30 11:55:342200 if (parentGroupIsVisible && !style.shareHeaderLine) {
Blink Reformat4c46d092018-04-07 15:32:372201 currentOffset += style.height;
Tim van der Lippe1d6e57a2019-09-30 11:55:342202 }
Blink Reformat4c46d092018-04-07 15:32:372203 }
Tim van der Lippe1d6e57a2019-09-30 11:55:342204 if (level >= levelCount) {
Yang Guo8fb3ac52019-07-31 20:17:502205 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:342206 }
Blink Reformat4c46d092018-04-07 15:32:372207 const isFirstOnLevel = groupIndex >= 0 && level === groups[groupIndex].startLevel;
2208 const thisLevelIsVisible =
2209 parentGroupIsVisible && (visible || isFirstOnLevel && groups[groupIndex].style.useFirstLineForOverview);
Yang Guo8fb3ac52019-07-31 20:17:502210 let height;
2211 if (groupIndex >= 0) {
2212 const group = groups[groupIndex];
2213 const styleB = group.style;
2214 height = isFirstOnLevel && !styleB.shareHeaderLine || (styleB.collapsible && !group.expanded) ?
2215 styleB.height :
2216 (styleB.itemsHeight || this._barHeight);
2217 } else {
2218 height = this._barHeight;
Blink Reformat4c46d092018-04-07 15:32:372219 }
Paul Lewiscf1f8a42020-11-04 21:05:242220 this._visibleLevels[level] = thisLevelIsVisible ? 1 : 0;
Yang Guo8fb3ac52019-07-31 20:17:502221 this._visibleLevelOffsets[level] = currentOffset;
2222 this._visibleLevelHeights[level] = height;
Tim van der Lippe1d6e57a2019-09-30 11:55:342223 if (thisLevelIsVisible || (parentGroupIsVisible && style && style.shareHeaderLine && isFirstOnLevel)) {
Blink Reformat4c46d092018-04-07 15:32:372224 currentOffset += this._visibleLevelHeights[level];
Tim van der Lippe1d6e57a2019-09-30 11:55:342225 }
Blink Reformat4c46d092018-04-07 15:32:372226 }
Tim van der Lippe1d6e57a2019-09-30 11:55:342227 if (groupIndex >= 0) {
Blink Reformat4c46d092018-04-07 15:32:372228 this._groupOffsets[groupIndex + 1] = currentOffset;
Tim van der Lippe1d6e57a2019-09-30 11:55:342229 }
Blink Reformat4c46d092018-04-07 15:32:372230 this._visibleLevelOffsets[level] = currentOffset;
Tim van der Lippe1d6e57a2019-09-30 11:55:342231 if (this._useWebGL) {
Alexei Filippov57ccafb2018-08-14 20:59:052232 this._setupGLGeometry();
Tim van der Lippe1d6e57a2019-09-30 11:55:342233 }
Blink Reformat4c46d092018-04-07 15:32:372234 }
2235
2236 /**
2237 * @param {number} index
2238 */
2239 _isGroupCollapsible(index) {
Paul Lewiscf1f8a42020-11-04 21:05:242240 if (!this._rawTimelineData) {
2241 return;
2242 }
2243
Blink Reformat4c46d092018-04-07 15:32:372244 const groups = this._rawTimelineData.groups || [];
2245 const style = groups[index].style;
Tim van der Lippe1d6e57a2019-09-30 11:55:342246 if (!style.shareHeaderLine || !style.collapsible) {
Tim van der Lipped7cfd142021-01-07 12:17:242247 return Boolean(style.collapsible);
Tim van der Lippe1d6e57a2019-09-30 11:55:342248 }
Blink Reformat4c46d092018-04-07 15:32:372249 const isLastGroup = index + 1 >= groups.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:342250 if (!isLastGroup && groups[index + 1].style.nestingLevel > style.nestingLevel) {
Blink Reformat4c46d092018-04-07 15:32:372251 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:342252 }
Blink Reformat4c46d092018-04-07 15:32:372253 const nextGroupLevel = isLastGroup ? this._dataProvider.maxStackDepth() : groups[index + 1].startLevel;
Tim van der Lippe1d6e57a2019-09-30 11:55:342254 if (nextGroupLevel !== groups[index].startLevel + 1) {
Blink Reformat4c46d092018-04-07 15:32:372255 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:342256 }
Blink Reformat4c46d092018-04-07 15:32:372257 // For groups that only have one line and share header line, pretend these are not collapsible
2258 // unless the itemsHeight does not match the headerHeight
2259 return style.height !== style.itemsHeight;
2260 }
2261
2262 /**
2263 * @param {number} entryIndex
2264 */
2265 setSelectedEntry(entryIndex) {
Tim van der Lippe1d6e57a2019-09-30 11:55:342266 if (this._selectedEntryIndex === entryIndex) {
Blink Reformat4c46d092018-04-07 15:32:372267 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:342268 }
2269 if (entryIndex !== -1) {
Blink Reformat4c46d092018-04-07 15:32:372270 this._chartViewport.hideRangeSelection();
Tim van der Lippe1d6e57a2019-09-30 11:55:342271 }
Blink Reformat4c46d092018-04-07 15:32:372272 this._selectedEntryIndex = entryIndex;
2273 this._revealEntry(entryIndex);
2274 this._updateElementPosition(this._selectedElement, this._selectedEntryIndex);
2275 }
2276
2277 /**
2278 * @param {!Element} element
2279 * @param {number} entryIndex
2280 */
2281 _updateElementPosition(element, entryIndex) {
Alexei Filippov72d792d2018-11-06 07:15:042282 const elementMinWidthPx = 2;
2283 element.classList.add('hidden');
Tim van der Lippe1d6e57a2019-09-30 11:55:342284 if (entryIndex === -1) {
Blink Reformat4c46d092018-04-07 15:32:372285 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:342286 }
Blink Reformat4c46d092018-04-07 15:32:372287 const timelineData = this._timelineData();
Paul Lewiscf1f8a42020-11-04 21:05:242288 if (!timelineData) {
2289 return;
2290 }
2291
Blink Reformat4c46d092018-04-07 15:32:372292 const startTime = timelineData.entryStartTimes[entryIndex];
Alexei Filippov72d792d2018-11-06 07:15:042293 const duration = timelineData.entryTotalTimes[entryIndex];
Alexei Filippov65106502018-11-29 05:16:192294 let barX = 0;
2295 let barWidth = 0;
2296 let visible = true;
Alexei Filippov6c622e92018-11-10 02:13:592297 if (Number.isNaN(duration)) {
2298 const position = this._markerPositions.get(entryIndex);
Alexei Filippov65106502018-11-29 05:16:192299 if (position) {
2300 barX = position.x;
2301 barWidth = position.width;
2302 } else {
2303 visible = false;
2304 }
Alexei Filippov6c622e92018-11-10 02:13:592305 } else {
2306 barX = this._chartViewport.timeToPosition(startTime);
2307 barWidth = duration * this._chartViewport.timeToPixel();
2308 }
Tim van der Lippe1d6e57a2019-09-30 11:55:342309 if (barX + barWidth <= 0 || barX >= this._offsetWidth) {
Blink Reformat4c46d092018-04-07 15:32:372310 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:342311 }
Blink Reformat4c46d092018-04-07 15:32:372312 const barCenter = barX + barWidth / 2;
2313 barWidth = Math.max(barWidth, elementMinWidthPx);
2314 barX = barCenter - barWidth / 2;
2315 const entryLevel = timelineData.entryLevels[entryIndex];
2316 const barY = this._levelToOffset(entryLevel) - this._chartViewport.scrollOffset();
2317 const barHeight = this._levelHeight(entryLevel);
Paul Lewiscf1f8a42020-11-04 21:05:242318 const style = /** @type {!HTMLElement} */ (element).style;
Blink Reformat4c46d092018-04-07 15:32:372319 style.left = barX + 'px';
2320 style.top = barY + 'px';
2321 style.width = barWidth + 'px';
2322 style.height = barHeight - 1 + 'px';
Alexei Filippov65106502018-11-29 05:16:192323 element.classList.toggle('hidden', !visible);
Blink Reformat4c46d092018-04-07 15:32:372324 this._viewportElement.appendChild(element);
2325 }
2326
2327 /**
2328 * @param {number} time
2329 * @return {number}
2330 */
2331 _timeToPositionClipped(time) {
Jack Franklin1be909c2020-03-04 08:57:412332 return Platform.NumberUtilities.clamp(this._chartViewport.timeToPosition(time), 0, this._offsetWidth);
Blink Reformat4c46d092018-04-07 15:32:372333 }
2334
2335 /**
2336 * @param {number} level
2337 * @return {number}
2338 */
2339 _levelToOffset(level) {
Paul Lewiscf1f8a42020-11-04 21:05:242340 if (!this._visibleLevelOffsets) {
2341 throw new Error('No visible level offsets');
2342 }
Blink Reformat4c46d092018-04-07 15:32:372343 return this._visibleLevelOffsets[level];
2344 }
2345
2346 /**
2347 * @param {number} level
2348 * @return {number}
2349 */
2350 _levelHeight(level) {
Paul Lewiscf1f8a42020-11-04 21:05:242351 if (!this._visibleLevelHeights) {
2352 throw new Error('No visible level heights');
2353 }
Blink Reformat4c46d092018-04-07 15:32:372354 return this._visibleLevelHeights[level];
2355 }
2356
2357 _updateBoundaries() {
2358 this._totalTime = this._dataProvider.totalTime();
2359 this._minimumBoundary = this._dataProvider.minimumBoundary();
2360 this._chartViewport.setBoundaries(this._minimumBoundary, this._totalTime);
2361 }
2362
2363 _updateHeight() {
Alexei Filippov23428d42018-04-28 01:09:072364 const height = this._levelToOffset(this._dataProvider.maxStackDepth()) + 2;
Blink Reformat4c46d092018-04-07 15:32:372365 this._chartViewport.setContentHeight(height);
2366 }
2367
2368 /**
2369 * @override
2370 */
2371 onResize() {
2372 this.scheduleUpdate();
2373 }
2374
2375 /**
2376 * @override
2377 */
2378 update() {
Tim van der Lippe1d6e57a2019-09-30 11:55:342379 if (!this._timelineData()) {
Blink Reformat4c46d092018-04-07 15:32:372380 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:342381 }
Blink Reformat4c46d092018-04-07 15:32:372382 this._resetCanvas();
2383 this._updateHeight();
2384 this._updateBoundaries();
2385 this._draw();
Tim van der Lippe1d6e57a2019-09-30 11:55:342386 if (!this._chartViewport.isDragging()) {
Blink Reformat4c46d092018-04-07 15:32:372387 this._updateHighlight();
Tim van der Lippe1d6e57a2019-09-30 11:55:342388 }
Blink Reformat4c46d092018-04-07 15:32:372389 }
2390
2391 reset() {
2392 this._chartViewport.reset();
2393 this._rawTimelineData = null;
2394 this._rawTimelineDataLength = 0;
2395 this._highlightedMarkerIndex = -1;
2396 this._highlightedEntryIndex = -1;
2397 this._selectedEntryIndex = -1;
2398 /** @type {!Map<string,!Map<string,number>>} */
2399 this._textWidth = new Map();
2400 this._chartViewport.scheduleUpdate();
2401 }
2402
2403 scheduleUpdate() {
2404 this._chartViewport.scheduleUpdate();
2405 }
2406
2407 _enabled() {
2408 return this._rawTimelineDataLength !== 0;
2409 }
2410
2411 /**
2412 * @override
2413 * @param {number} time
2414 * @return {number}
2415 */
2416 computePosition(time) {
2417 return this._chartViewport.timeToPosition(time);
2418 }
2419
2420 /**
2421 * @override
2422 * @param {number} value
2423 * @param {number=} precision
2424 * @return {string}
2425 */
2426 formatValue(value, precision) {
2427 return this._dataProvider.formatValue(value - this.zeroTime(), precision);
2428 }
2429
2430 /**
2431 * @override
2432 * @return {number}
2433 */
2434 maximumBoundary() {
2435 return this._chartViewport.windowRightTime();
2436 }
2437
2438 /**
2439 * @override
2440 * @return {number}
2441 */
2442 minimumBoundary() {
2443 return this._chartViewport.windowLeftTime();
2444 }
2445
2446 /**
2447 * @override
2448 * @return {number}
2449 */
2450 zeroTime() {
2451 return this._dataProvider.minimumBoundary();
2452 }
2453
2454 /**
2455 * @override
2456 * @return {number}
2457 */
2458 boundarySpan() {
Alexei Filippov2578eb02018-04-11 08:15:052459 return this.maximumBoundary() - this.minimumBoundary();
Blink Reformat4c46d092018-04-07 15:32:372460 }
Tim van der Lippefd2b2ce2020-01-03 15:05:182461}
Blink Reformat4c46d092018-04-07 15:32:372462
Tim van der Lippefd2b2ce2020-01-03 15:05:182463export const HeaderHeight = 15;
2464export const MinimalTimeWindowMs = 0.5;
Blink Reformat4c46d092018-04-07 15:32:372465
Tim van der Lippefd2b2ce2020-01-03 15:05:182466export class TimelineData {
Blink Reformat4c46d092018-04-07 15:32:372467 /**
2468 * @param {!Array<number>|!Uint16Array} entryLevels
2469 * @param {!Array<number>|!Float32Array} entryTotalTimes
2470 * @param {!Array<number>|!Float64Array} entryStartTimes
Tim van der Lippe8ef250c2020-02-20 16:29:252471 * @param {?Array<!Group>} groups
Blink Reformat4c46d092018-04-07 15:32:372472 */
2473 constructor(entryLevels, entryTotalTimes, entryStartTimes, groups) {
2474 this.entryLevels = entryLevels;
2475 this.entryTotalTimes = entryTotalTimes;
2476 this.entryStartTimes = entryStartTimes;
Tim van der Lippedfbb48f2020-11-19 14:49:152477 this.groups = groups || [];
Tim van der Lippefd2b2ce2020-01-03 15:05:182478 /** @type {!Array.<!FlameChartMarker>} */
Blink Reformat4c46d092018-04-07 15:32:372479 this.markers = [];
Paul Lewiscf1f8a42020-11-04 21:05:242480 /** @type {!Array.<number>} */
Blink Reformat4c46d092018-04-07 15:32:372481 this.flowStartTimes = [];
Paul Lewiscf1f8a42020-11-04 21:05:242482 /** @type {!Array.<number>} */
Blink Reformat4c46d092018-04-07 15:32:372483 this.flowStartLevels = [];
Paul Lewiscf1f8a42020-11-04 21:05:242484 /** @type {!Array.<number>} */
Blink Reformat4c46d092018-04-07 15:32:372485 this.flowEndTimes = [];
Paul Lewiscf1f8a42020-11-04 21:05:242486 /** @type {!Array.<number>} */
Blink Reformat4c46d092018-04-07 15:32:372487 this.flowEndLevels = [];
Tim van der Lippe8ef250c2020-02-20 16:29:252488 /** @type {?Group} */
Blink Reformat4c46d092018-04-07 15:32:372489 this.selectedGroup = null;
2490 }
Tim van der Lippefd2b2ce2020-01-03 15:05:182491}
Blink Reformat4c46d092018-04-07 15:32:372492
Tim van der Lippefd2b2ce2020-01-03 15:05:182493/**
2494 * @interface
2495 */
2496export class FlameChartDataProvider {
Blink Reformat4c46d092018-04-07 15:32:372497 /**
2498 * @return {number}
2499 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182500 minimumBoundary() {
Paul Lewiscf1f8a42020-11-04 21:05:242501 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182502 }
Blink Reformat4c46d092018-04-07 15:32:372503
2504 /**
2505 * @return {number}
2506 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182507 totalTime() {
Paul Lewiscf1f8a42020-11-04 21:05:242508 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182509 }
Blink Reformat4c46d092018-04-07 15:32:372510
2511 /**
2512 * @param {number} value
2513 * @param {number=} precision
2514 * @return {string}
2515 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182516 formatValue(value, precision) {
Paul Lewiscf1f8a42020-11-04 21:05:242517 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182518 }
Blink Reformat4c46d092018-04-07 15:32:372519
2520 /**
2521 * @return {number}
2522 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182523 maxStackDepth() {
Paul Lewiscf1f8a42020-11-04 21:05:242524 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182525 }
Blink Reformat4c46d092018-04-07 15:32:372526
2527 /**
Tim van der Lippefd2b2ce2020-01-03 15:05:182528 * @return {?TimelineData}
Blink Reformat4c46d092018-04-07 15:32:372529 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182530 timelineData() {
Paul Lewiscf1f8a42020-11-04 21:05:242531 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182532 }
Blink Reformat4c46d092018-04-07 15:32:372533
2534 /**
2535 * @param {number} entryIndex
2536 * @return {?Element}
2537 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182538 prepareHighlightedEntryInfo(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242539 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182540 }
Blink Reformat4c46d092018-04-07 15:32:372541
2542 /**
2543 * @param {number} entryIndex
2544 * @return {boolean}
2545 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182546 canJumpToEntry(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242547 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182548 }
Blink Reformat4c46d092018-04-07 15:32:372549
2550 /**
2551 * @param {number} entryIndex
2552 * @return {?string}
2553 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182554 entryTitle(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242555 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182556 }
Blink Reformat4c46d092018-04-07 15:32:372557
2558 /**
2559 * @param {number} entryIndex
2560 * @return {?string}
2561 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182562 entryFont(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242563 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182564 }
Blink Reformat4c46d092018-04-07 15:32:372565
2566 /**
2567 * @param {number} entryIndex
2568 * @return {string}
2569 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182570 entryColor(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242571 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182572 }
Blink Reformat4c46d092018-04-07 15:32:372573
2574 /**
2575 * @param {number} entryIndex
2576 * @param {!CanvasRenderingContext2D} context
2577 * @param {?string} text
2578 * @param {number} barX
2579 * @param {number} barY
2580 * @param {number} barWidth
2581 * @param {number} barHeight
2582 * @param {number} unclippedBarX
2583 * @param {number} timeToPixelRatio
2584 * @return {boolean}
2585 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182586 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, timeToPixelRatio) {
Paul Lewiscf1f8a42020-11-04 21:05:242587 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182588 }
Blink Reformat4c46d092018-04-07 15:32:372589
2590 /**
2591 * @param {number} entryIndex
2592 * @return {boolean}
2593 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182594 forceDecoration(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242595 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182596 }
Blink Reformat4c46d092018-04-07 15:32:372597
2598 /**
2599 * @param {number} entryIndex
2600 * @return {string}
2601 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182602 textColor(entryIndex) {
Paul Lewiscf1f8a42020-11-04 21:05:242603 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182604 }
Paul Lewisead45752020-06-23 09:51:362605
2606 /**
2607 * @return {!Map<string, !SDK.TracingModel.Event>}
2608 */
2609 navStartTimes() {
Paul Lewiscf1f8a42020-11-04 21:05:242610 throw new Error('Not implemented');
Paul Lewisead45752020-06-23 09:51:362611 }
Tim van der Lippefd2b2ce2020-01-03 15:05:182612}
Blink Reformat4c46d092018-04-07 15:32:372613
2614/**
2615 * @interface
2616 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182617export class FlameChartMarker {
Blink Reformat4c46d092018-04-07 15:32:372618 /**
2619 * @return {number}
2620 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182621 startTime() {
Paul Lewiscf1f8a42020-11-04 21:05:242622 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182623 }
Blink Reformat4c46d092018-04-07 15:32:372624
2625 /**
2626 * @return {string}
2627 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182628 color() {
Paul Lewiscf1f8a42020-11-04 21:05:242629 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182630 }
Blink Reformat4c46d092018-04-07 15:32:372631
2632 /**
Alexei Filippov72d792d2018-11-06 07:15:042633 * @return {?string}
Blink Reformat4c46d092018-04-07 15:32:372634 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182635 title() {
Paul Lewiscf1f8a42020-11-04 21:05:242636 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182637 }
Blink Reformat4c46d092018-04-07 15:32:372638
2639 /**
2640 * @param {!CanvasRenderingContext2D} context
2641 * @param {number} x
2642 * @param {number} height
2643 * @param {number} pixelsPerMillisecond
2644 */
Tim van der Lippefd2b2ce2020-01-03 15:05:182645 draw(context, x, height, pixelsPerMillisecond) {
Paul Lewiscf1f8a42020-11-04 21:05:242646 throw new Error('Not implemented');
Tim van der Lippefd2b2ce2020-01-03 15:05:182647 }
2648}
Blink Reformat4c46d092018-04-07 15:32:372649
2650/** @enum {symbol} */
Tim van der Lippefd2b2ce2020-01-03 15:05:182651export const Events = {
Michael Liao712bbc22019-10-15 19:21:512652 CanvasFocused: Symbol('CanvasFocused'),
2653 EntryInvoked: Symbol('EntryInvoked'),
Blink Reformat4c46d092018-04-07 15:32:372654 EntrySelected: Symbol('EntrySelected'),
2655 EntryHighlighted: Symbol('EntryHighlighted')
2656};
2657
Tim van der Lippefd2b2ce2020-01-03 15:05:182658export const Colors = {
Blink Reformat4c46d092018-04-07 15:32:372659 SelectedGroupBackground: 'hsl(215, 85%, 98%)',
2660 SelectedGroupBorder: 'hsl(216, 68%, 54%)',
2661};
Tim van der Lippecec9b762020-02-13 15:31:222662
2663/**
2664 * @typedef {!{
Tim van der Lippe3b1f7452021-01-08 11:25:132665 * name: !Platform.UIString.LocalizedString,
Tim van der Lippecec9b762020-02-13 15:31:222666 * startLevel: number,
2667 * expanded: (boolean|undefined),
2668 * selectable: (boolean|undefined),
Tim van der Lippedfbb48f2020-11-19 14:49:152669 * style: !GroupStyle,
2670 * track: (?TimelineModel.TimelineModel.Track|undefined),
Tim van der Lippecec9b762020-02-13 15:31:222671 * }}
2672 */
Paul Lewiscf1f8a42020-11-04 21:05:242673// @ts-ignore Typedef
Tim van der Lippecec9b762020-02-13 15:31:222674export let Group;
2675
2676/**
2677 * @typedef {!{
2678 * height: number,
2679 * padding: number,
2680 * collapsible: boolean,
2681 * font: string,
2682 * color: string,
2683 * backgroundColor: string,
2684 * nestingLevel: number,
2685 * itemsHeight: (number|undefined),
2686 * shareHeaderLine: (boolean|undefined),
2687 * useFirstLineForOverview: (boolean|undefined),
2688 * useDecoratorsForOverview: (boolean|undefined)
2689 * }}
2690 */
Paul Lewiscf1f8a42020-11-04 21:05:242691// @ts-ignore Typedef
Tim van der Lippecec9b762020-02-13 15:31:222692export let GroupStyle;