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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js

Issue 2852173002: DevTools: Show screenshots on the main flamechart (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2014 Google Inc. All rights reserved. 2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 14 matching lines...) Expand all
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 /** 31 /**
32 * @implements {PerfUI.FlameChartDataProvider} 32 * @implements {PerfUI.FlameChartDataProvider}
33 * @unrestricted 33 * @unrestricted
34 */ 34 */
35 Timeline.TimelineFlameChartDataProvider = class { 35 Timeline.TimelineFlameChartDataProvider = class extends Common.Object {
36 /** 36 /**
37 * @param {!Array<!TimelineModel.TimelineModelFilter>} filters 37 * @param {!Array<!TimelineModel.TimelineModelFilter>} filters
38 */ 38 */
39 constructor(filters) { 39 constructor(filters) {
40 super();
40 this.reset(); 41 this.reset();
41 var font = '11px ' + Host.fontFamily(); 42 var font = '11px ' + Host.fontFamily();
42 this._font = font; 43 this._font = font;
43 this._filters = filters; 44 this._filters = filters;
44 /** @type {?PerfUI.FlameChart.TimelineData} */ 45 /** @type {?PerfUI.FlameChart.TimelineData} */
45 this._timelineData = null; 46 this._timelineData = null;
46 this._currentLevel = 0; 47 this._currentLevel = 0;
47 /** @type {?Timeline.PerformanceModel} */ 48 /** @type {?Timeline.PerformanceModel} */
48 this._performanceModel = null; 49 this._performanceModel = null;
49 /** @type {?TimelineModel.TimelineModel} */ 50 /** @type {?TimelineModel.TimelineModel} */
(...skipping 18 matching lines...) Expand all
68 font: font, 69 font: font,
69 nestingLevel: 0, 70 nestingLevel: 0,
70 shareHeaderLine: true 71 shareHeaderLine: true
71 }; 72 };
72 return /** @type {!PerfUI.FlameChart.GroupStyle} */ (Object.assign(default GroupStyle, extra)); 73 return /** @type {!PerfUI.FlameChart.GroupStyle} */ (Object.assign(default GroupStyle, extra));
73 } 74 }
74 75
75 this._headerLevel1 = buildGroupStyle({shareHeaderLine: false}); 76 this._headerLevel1 = buildGroupStyle({shareHeaderLine: false});
76 this._headerLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1, collapsib le: false}); 77 this._headerLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1, collapsib le: false});
77 this._staticHeader = buildGroupStyle({collapsible: false}); 78 this._staticHeader = buildGroupStyle({collapsible: false});
79 this._framesHeader = buildGroupStyle({useFirstLineForOverview: true, shareHe aderLine: true, itemsHeight: 120});
78 this._interactionsHeaderLevel1 = buildGroupStyle({useFirstLineForOverview: t rue}); 80 this._interactionsHeaderLevel1 = buildGroupStyle({useFirstLineForOverview: t rue});
79 this._interactionsHeaderLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1}); 81 this._interactionsHeaderLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1});
80 82
81 /** @type {!Map<string, number>} */ 83 /** @type {!Map<string, number>} */
82 this._flowEventIndexById = new Map(); 84 this._flowEventIndexById = new Map();
83 } 85 }
84 86
85 /** 87 /**
86 * @param {?Timeline.PerformanceModel} performanceModel 88 * @param {?Timeline.PerformanceModel} performanceModel
87 */ 89 */
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 /** @type {!Array<string>} */ 156 /** @type {!Array<string>} */
155 this._entryIndexToTitle = []; 157 this._entryIndexToTitle = [];
156 /** @type {!Array<!Timeline.TimelineFlameChartMarker>} */ 158 /** @type {!Array<!Timeline.TimelineFlameChartMarker>} */
157 this._markers = []; 159 this._markers = [];
158 /** @type {!Map<!Timeline.TimelineCategory, string>} */ 160 /** @type {!Map<!Timeline.TimelineCategory, string>} */
159 this._asyncColorByCategory = new Map(); 161 this._asyncColorByCategory = new Map();
160 /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */ 162 /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */
161 this._asyncColorByInteractionPhase = new Map(); 163 this._asyncColorByInteractionPhase = new Map();
162 /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */ 164 /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */
163 this._extensionInfo = []; 165 this._extensionInfo = [];
166 /** @type {!Map<!TimelineModel.TimelineFrame, ?Image>} */
167 this._frameImageCache = new Map();
164 } 168 }
165 169
166 /** 170 /**
167 * @override 171 * @override
168 * @return {number} 172 * @return {number}
169 */ 173 */
170 maxStackDepth() { 174 maxStackDepth() {
171 return this._currentLevel; 175 return this._currentLevel;
172 } 176 }
173 177
174 /** 178 /**
175 * @override 179 * @override
176 * @return {!PerfUI.FlameChart.TimelineData} 180 * @return {!PerfUI.FlameChart.TimelineData}
177 */ 181 */
178 timelineData() { 182 timelineData() {
179 if (this._timelineData) 183 if (this._timelineData)
180 return this._timelineData; 184 return this._timelineData;
181 185
182 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []); 186 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []);
183 if (!this._model) 187 if (!this._model)
184 return this._timelineData; 188 return this._timelineData;
185 189
186 this._flowEventIndexById.clear(); 190 this._flowEventIndexById.clear();
187 191
188 this._minimumBoundary = this._model.minimumRecordTime(); 192 this._minimumBoundary = this._model.minimumRecordTime();
189 this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTim e() - this._minimumBoundary; 193 this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTim e() - this._minimumBoundary;
190 this._currentLevel = 0; 194 this._currentLevel = 0;
191 195
192 this._appendHeader(Common.UIString('Frames'), this._staticHeader);
193 this._appendFrameBars(this._performanceModel.frames()); 196 this._appendFrameBars(this._performanceModel.frames());
194 197
195 this._appendHeader(Common.UIString('Interactions'), this._interactionsHeader Level1); 198 this._appendHeader(Common.UIString('Interactions'), this._interactionsHeader Level1);
196 this._appendInteractionRecords(); 199 this._appendInteractionRecords();
197 200
198 var eventEntryType = Timeline.TimelineFlameChartEntryType.Event; 201 var eventEntryType = Timeline.TimelineFlameChartEntryType.Event;
199 202
200 var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup; 203 var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup;
201 var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroup s.input); 204 var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroup s.input);
202 if (inputLatencies && inputLatencies.length) { 205 if (inputLatencies && inputLatencies.length) {
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 438
436 _appendInteractionRecords() { 439 _appendInteractionRecords() {
437 this._performanceModel.interactionRecords().forEach(this._appendSegment, thi s); 440 this._performanceModel.interactionRecords().forEach(this._appendSegment, thi s);
438 this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEn tryType.InteractionRecord; 441 this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEn tryType.InteractionRecord;
439 } 442 }
440 443
441 /** 444 /**
442 * @param {!Array.<!TimelineModel.TimelineFrame>} frames 445 * @param {!Array.<!TimelineModel.TimelineFrame>} frames
443 */ 446 */
444 _appendFrameBars(frames) { 447 _appendFrameBars(frames) {
448 var hasFilmStrip = !!this._performanceModel.filmStripModel().frames().length ;
449 this._framesHeader.collapsible = hasFilmStrip;
450 this._appendHeader(Common.UIString('Frames'), this._framesHeader);
451 this._frameGroup = this._timelineData.groups.peekLast();
445 var style = Timeline.TimelineUIUtils.markerStyleForFrame(); 452 var style = Timeline.TimelineUIUtils.markerStyleForFrame();
446 this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEntr yType.Frame; 453 for (var i = 0; i < (hasFilmStrip ? 2 : 1); ++i) {
447 for (var i = 0; i < frames.length; ++i) { 454 this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEn tryType.Frame;
caseq 2017/05/01 22:13:35 Let's add another type for FrameThumbnail.
alph 2017/05/02 18:31:13 Acknowledged.
448 this._markers.push(new Timeline.TimelineFlameChartMarker( 455 for (var frame of frames) {
449 frames[i].startTime, frames[i].startTime - this._model.minimumRecordTi me(), style)); 456 this._markers.push(new Timeline.TimelineFlameChartMarker(
caseq 2017/05/01 22:13:35 Why do we do it twice?
alph 2017/05/02 18:31:13 Done.
450 this._appendFrame(frames[i]); 457 frame.startTime, frame.startTime - this._model.minimumRecordTime(), style));
458 this._appendFrame(frame);
459 }
460 ++this._currentLevel;
451 } 461 }
452 ++this._currentLevel;
453 } 462 }
454 463
455 /** 464 /**
456 * @param {number} entryIndex 465 * @param {number} entryIndex
457 * @return {!Timeline.TimelineFlameChartEntryType} 466 * @return {!Timeline.TimelineFlameChartEntryType}
458 */ 467 */
459 _entryType(entryIndex) { 468 _entryType(entryIndex) {
460 return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]]; 469 return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]];
461 } 470 }
462 471
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) 555 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord)
547 return 'transparent'; 556 return 'transparent';
548 if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) { 557 if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) {
549 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]); 558 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
550 return this._extensionColorGenerator.colorForID(event.name); 559 return this._extensionColorGenerator.colorForID(event.name);
551 } 560 }
552 return ''; 561 return '';
553 } 562 }
554 563
555 /** 564 /**
565 * @param {number} entryIndex
566 * @param {!CanvasRenderingContext2D} context
567 * @param {?string} text
568 * @param {number} barX
569 * @param {number} barY
570 * @param {number} barWidth
571 * @param {number} barHeight
572 */
573 _drawFrame(entryIndex, context, text, barX, barY, barWidth, barHeight) {
caseq 2017/05/01 22:13:35 Let's split it into two, one for drawing thumbnail
alph 2017/05/02 18:31:13 Done it differently
574 var /** @const */ hPadding = 1;
575 var frame = /** @type {!TimelineModel.TimelineFrame} */ (this._entryData[ent ryIndex]);
576 barX += hPadding;
577 barWidth -= 2 * hPadding;
578 context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1' : '#d7f0d1');
579 context.fillRect(barX, barY, barWidth, barHeight);
580
581 var image;
582 if (this._frameImageCache.has(frame)) {
583 image = this._frameImageCache.get(frame);
584 } else {
585 this._frameImageCache.set(frame, null); // Mark the image promise is in f light.
586 var modelFrame = Timeline.TimelineUIUtils.filmStripModelFrame(this._perfor manceModel.filmStripModel(), frame);
587 if (modelFrame) {
588 modelFrame.imageDataPromise()
589 .then(data => UI.loadImage(data ? 'data:image/jpg;base64,' + data : ''))
590 .then(image => this._frameImageCache.set(frame, image))
591 .then(() => this.dispatchEventToListeners(Timeline.TimelineFlameChar tDataProvider.Events.DataChanged));
caseq 2017/05/01 22:13:35 no need for another then, let's do this synchronou
alph 2017/05/02 18:31:13 Done.
592 }
593 }
594 if (barHeight === this._framesHeader.itemsHeight) {
595 if (!image)
596 return;
597 var padding = 2;
598 var imageHeight = barHeight - 2 * padding;
599 var imageX = barX + padding;
600 var imageY = barY + padding;
601 var scale = imageHeight / image.naturalHeight;
602 var imageWidth = image.naturalWidth * scale;
603 context.save();
604 context.beginPath();
605 context.rect(imageX, imageY, barWidth - 2 * padding, imageHeight);
606 context.clip();
607 context.drawImage(image, imageX, imageY, imageWidth, imageHeight);
608 context.restore();
609 } else {
610 var frameDurationText = Number.preciseMillisToString(frame.duration, 1);
611 var textWidth = context.measureText(frameDurationText).width;
612 if (textWidth <= barWidth) {
613 var font = this.entryFont(entryIndex);
614 if (font)
615 context.font = font;
616 context.fillStyle = this.textColor(entryIndex);
617 context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, b arY + barHeight - 4);
618 }
619 }
620 }
621
622 /**
556 * @override 623 * @override
557 * @param {number} entryIndex 624 * @param {number} entryIndex
558 * @param {!CanvasRenderingContext2D} context 625 * @param {!CanvasRenderingContext2D} context
559 * @param {?string} text 626 * @param {?string} text
560 * @param {number} barX 627 * @param {number} barX
561 * @param {number} barY 628 * @param {number} barY
562 * @param {number} barWidth 629 * @param {number} barWidth
563 * @param {number} barHeight 630 * @param {number} barHeight
564 * @param {number} unclippedBarX 631 * @param {number} unclippedBarX
565 * @param {number} timeToPixels 632 * @param {number} timeToPixels
566 * @return {boolean} 633 * @return {boolean}
567 */ 634 */
568 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, uncl ippedBarX, timeToPixels) { 635 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, uncl ippedBarX, timeToPixels) {
569 var data = this._entryData[entryIndex]; 636 var data = this._entryData[entryIndex];
570 var type = this._entryType(entryIndex); 637 var type = this._entryType(entryIndex);
571 if (type === Timeline.TimelineFlameChartEntryType.Frame) { 638 if (type === Timeline.TimelineFlameChartEntryType.Frame) {
572 var /** @const */ vPadding = 1; 639 this._drawFrame(entryIndex, context, text, barX, barY, barWidth, barHeight );
573 var /** @const */ hPadding = 1;
574 var frame = /** {!TimelineModel.TimelineFrame} */ (data);
575 barX += hPadding;
576 barWidth -= 2 * hPadding;
577 barY += vPadding;
578 barHeight -= 2 * vPadding + 1;
579 context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1 ' : '#d7f0d1');
580 context.fillRect(barX, barY, barWidth, barHeight);
581 var frameDurationText = Number.preciseMillisToString(frame.duration, 1);
582 var textWidth = context.measureText(frameDurationText).width;
583 if (barWidth >= textWidth) {
584 context.fillStyle = this.textColor(entryIndex);
585 context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, b arY + barHeight - 3);
586 }
587 return true; 640 return true;
588 } 641 }
589 642
590 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) { 643 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) {
591 var color = Timeline.TimelineUIUtils.interactionPhaseColor( 644 var color = Timeline.TimelineUIUtils.interactionPhaseColor(
592 /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data)); 645 /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data));
593 context.fillStyle = color; 646 context.fillStyle = color;
594 context.fillRect(barX, barY, barWidth - 1, 2); 647 context.fillRect(barX, barY, barWidth - 1, 2);
595 context.fillRect(barX, barY - 3, 2, 3); 648 context.fillRect(barX, barY - 3, 2, 3);
596 context.fillRect(barX + barWidth - 3, barY - 3, 2, 3); 649 context.fillRect(barX + barWidth - 3, barY - 3, 2, 3);
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 */ 966 */
914 eventByIndex(entryIndex) { 967 eventByIndex(entryIndex) {
915 return this._entryType(entryIndex) === Timeline.TimelineFlameChartEntryType. Event ? 968 return this._entryType(entryIndex) === Timeline.TimelineFlameChartEntryType. Event ?
916 /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]) : 969 /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]) :
917 null; 970 null;
918 } 971 }
919 }; 972 };
920 973
921 Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.001; 974 Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.001;
922 Timeline.TimelineFlameChartDataProvider._indexSymbol = Symbol('index'); 975 Timeline.TimelineFlameChartDataProvider._indexSymbol = Symbol('index');
976
977 /** @enum {symbol} */
978 Timeline.TimelineFlameChartDataProvider.Events = {
979 DataChanged: Symbol('DataChanged')
980 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698