2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2012 Intel Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 WebInspector.TimelineUIUtils = function() { }
36 WebInspector.TimelineUIUtils.categories = function()
38 if (WebInspector.TimelineUIUtils._categories)
39 return WebInspector.TimelineUIUtils._categories;
40 WebInspector.TimelineUIUtils._categories = {
41 loading: new WebInspector.TimelineCategory("loading", WebInspector.UIString("Loading"), 0, "hsl(214, 53%, 58%)", "hsl(214, 67%, 90%)", "hsl(214, 67%, 74%)", "hsl(214, 67%, 66%)"),
42 scripting: new WebInspector.TimelineCategory("scripting", WebInspector.UIString("Scripting"), 1, "hsl(43, 90%, 45%)", "hsl(43, 83%, 90%)", "hsl(43, 83%, 72%)", "hsl(43, 83%, 64%) "),
43 rendering: new WebInspector.TimelineCategory("rendering", WebInspector.UIString("Rendering"), 2, "hsl(256, 50%, 60%)", "hsl(256, 67%, 90%)", "hsl(256, 67%, 76%)", "hsl(256, 67%, 70%)"),
44 painting: new WebInspector.TimelineCategory("painting", WebInspector.UIString("Painting"), 2, "hsl(109, 33%, 47%)", "hsl(109, 33%, 90%)", "hsl(109, 33%, 64%)", "hsl(109, 33%, 55%)"),
45 other: new WebInspector.TimelineCategory("other", WebInspector.UIString("Other"), -1, "hsl(0, 0%, 73%)", "hsl(0, 0%, 90%)", "hsl(0, 0%, 87%)", "hsl(0, 0%, 79%)"),
46 idle: new WebInspector.TimelineCategory("idle", WebInspector.UIString("Idle"), -1, "hsl(0, 0%, 87%)", "hsl(0, 100%, 100%)", "hsl(0, 100%, 100%)", "hsl(0, 100%, 100%)")
48 return WebInspector.TimelineUIUtils._categories;
52 * @return {!Object.<string, !{title: string, category: !WebInspector.TimelineCategory}>}
54 WebInspector.TimelineUIUtils._initRecordStyles = function()
56 if (WebInspector.TimelineUIUtils._recordStylesMap)
57 return WebInspector.TimelineUIUtils._recordStylesMap;
59 var recordTypes = WebInspector.TimelineModel.RecordType;
60 var categories = WebInspector.TimelineUIUtils.categories();
62 var recordStyles = {};
63 recordStyles[recordTypes.Root] = { title: "#root", category: categories["loading"] };
64 recordStyles[recordTypes.Program] = { title: WebInspector.UIString("Other"), category: categories["other"] };
65 recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: categories["scripting"] };
66 recordStyles[recordTypes.BeginFrame] = { title: WebInspector.UIString("Frame Start"), category: categories["rendering"] };
67 recordStyles[recordTypes.ScheduleStyleRecalculation] = { title: WebInspector.UIString("Schedule Style Recalculation"), category: categories["rendering"] };
68 recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: categories["rendering"] };
69 recordStyles[recordTypes.InvalidateLayout] = { title: WebInspector.UIString("Invalidate Layout"), category: categories["rendering"] };
70 recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: categories["rendering"] };
71 recordStyles[recordTypes.UpdateLayerTree] = { title: WebInspector.UIString("Update layer tree"), category: categories["rendering"] };
72 recordStyles[recordTypes.PaintSetup] = { title: WebInspector.UIString("Paint Setup"), category: categories["painting"] };
73 recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
74 recordStyles[recordTypes.Rasterize] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
75 recordStyles[recordTypes.ScrollLayer] = { title: WebInspector.UIString("Scroll"), category: categories["rendering"] };
76 recordStyles[recordTypes.DecodeImage] = { title: WebInspector.UIString("Image Decode"), category: categories["painting"] };
77 recordStyles[recordTypes.ResizeImage] = { title: WebInspector.UIString("Image Resize"), category: categories["painting"] };
78 recordStyles[recordTypes.CompositeLayers] = { title: WebInspector.UIString("Composite Layers"), category: categories["painting"] };
79 recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse HTML"), category: categories["loading"] };
80 recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: categories["scripting"] };
81 recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: categories["scripting"] };
82 recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: categories["scripting"] };
83 recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: categories["scripting"] };
84 recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: categories["scripting"] };
85 recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: categories["scripting"] };
86 recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: categories["loading"] };
87 recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: categories["loading"] };
88 recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: categories["loading"] };
89 recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: categories["scripting"] };
90 recordStyles[recordTypes.ResourceReceivedData] = { title: WebInspector.UIString("Receive Data"), category: categories["loading"] };
91 recordStyles[recordTypes.GCEvent] = { title: WebInspector.UIString("GC Event"), category: categories["scripting"] };
92 recordStyles[recordTypes.JSFrame] = { title: WebInspector.UIString("JS Frame"), category: categories["scripting"] };
93 recordStyles[recordTypes.MarkDOMContent] = { title: WebInspector.UIString("DOMContentLoaded event"), category: categories["scripting"] };
94 recordStyles[recordTypes.MarkLoad] = { title: WebInspector.UIString("Load event"), category: categories["scripting"] };
95 recordStyles[recordTypes.MarkFirstPaint] = { title: WebInspector.UIString("First paint"), category: categories["painting"] };
96 recordStyles[recordTypes.TimeStamp] = { title: WebInspector.UIString("Stamp"), category: categories["scripting"] };
97 recordStyles[recordTypes.ConsoleTime] = { title: WebInspector.UIString("Console Time"), category: categories["scripting"] };
98 recordStyles[recordTypes.RequestAnimationFrame] = { title: WebInspector.UIString("Request Animation Frame"), category: categories["scripting"] };
99 recordStyles[recordTypes.CancelAnimationFrame] = { title: WebInspector.UIString("Cancel Animation Frame"), category: categories["scripting"] };
100 recordStyles[recordTypes.FireAnimationFrame] = { title: WebInspector.UIString("Animation Frame Fired"), category: categories["scripting"] };
101 recordStyles[recordTypes.WebSocketCreate] = { title: WebInspector.UIString("Create WebSocket"), category: categories["scripting"] };
102 recordStyles[recordTypes.WebSocketSendHandshakeRequest] = { title: WebInspector.UIString("Send WebSocket Handshake"), category: categories["scripting"] };
103 recordStyles[recordTypes.WebSocketReceiveHandshakeResponse] = { title: WebInspector.UIString("Receive WebSocket Handshake"), category: categories["scripting"] };
104 recordStyles[recordTypes.WebSocketDestroy] = { title: WebInspector.UIString("Destroy WebSocket"), category: categories["scripting"] };
105 recordStyles[recordTypes.EmbedderCallback] = { title: WebInspector.UIString("Embedder Callback"), category: categories["scripting"] };
107 WebInspector.TimelineUIUtils._recordStylesMap = recordStyles;
112 * @param {!WebInspector.TimelineModel.Record} record
113 * @return {!{title: string, category: !WebInspector.TimelineCategory}}
115 WebInspector.TimelineUIUtils.recordStyle = function(record)
117 return WebInspector.TimelineUIUtils.styleForTimelineEvent(record.type());
121 * @param {string} type
122 * @return {!{title: string, category: !WebInspector.TimelineCategory}}
124 WebInspector.TimelineUIUtils.styleForTimelineEvent = function(type)
126 var recordStyles = WebInspector.TimelineUIUtils._initRecordStyles();
127 var result = recordStyles[type];
130 title: WebInspector.UIString("Unknown: %s", type),
131 category: WebInspector.TimelineUIUtils.categories()["other"]
133 recordStyles[type] = result;
140 * @param {!WebInspector.TimelineModel.Record} record
141 * @return {!WebInspector.TimelineCategory}
143 WebInspector.TimelineUIUtils.categoryForRecord = function(record)
145 return WebInspector.TimelineUIUtils.recordStyle(record).category;
149 * @param {!WebInspector.TimelineModel.Record} record
151 WebInspector.TimelineUIUtils.isEventDivider = function(record)
153 var recordTypes = WebInspector.TimelineModel.RecordType;
154 if (record.type() === recordTypes.TimeStamp)
156 if (record.type() === recordTypes.MarkFirstPaint)
158 if (record.type() === recordTypes.MarkDOMContent || record.type() === recordTypes.MarkLoad)
159 return record.data()["isMainFrame"];
164 * @param {string=} recordType
167 WebInspector.TimelineUIUtils.needsPreviewElement = function(recordType)
171 const recordTypes = WebInspector.TimelineModel.RecordType;
172 switch (recordType) {
173 case recordTypes.ResourceSendRequest:
174 case recordTypes.ResourceReceiveResponse:
175 case recordTypes.ResourceReceivedData:
176 case recordTypes.ResourceFinish:
184 * @param {string} recordType
185 * @param {string=} title
187 WebInspector.TimelineUIUtils.createEventDivider = function(recordType, title)
189 var eventDivider = document.createElement("div");
190 eventDivider.className = "resources-event-divider";
191 var recordTypes = WebInspector.TimelineModel.RecordType;
193 if (recordType === recordTypes.MarkDOMContent)
194 eventDivider.className += " resources-blue-divider";
195 else if (recordType === recordTypes.MarkLoad)
196 eventDivider.className += " resources-red-divider";
197 else if (recordType === recordTypes.MarkFirstPaint)
198 eventDivider.className += " resources-green-divider";
199 else if (recordType === recordTypes.TimeStamp)
200 eventDivider.className += " resources-orange-divider";
201 else if (recordType === recordTypes.BeginFrame)
202 eventDivider.className += " timeline-frame-divider";
205 eventDivider.title = title;
212 * @param {!WebInspector.TimelineModel} model
213 * @param {!{name: string, tasks: !Array.<!{startTime: number, endTime: number}>, firstTaskIndex: number, lastTaskIndex: number}} info
216 WebInspector.TimelineUIUtils.generateMainThreadBarPopupContent = function(model, info)
218 var firstTaskIndex = info.firstTaskIndex;
219 var lastTaskIndex = info.lastTaskIndex;
220 var tasks = info.tasks;
221 var messageCount = lastTaskIndex - firstTaskIndex + 1;
224 for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) {
226 cpuTime += task.endTime - task.startTime;
228 var startTime = tasks[firstTaskIndex].startTime;
229 var endTime = tasks[lastTaskIndex].endTime;
230 var duration = endTime - startTime;
232 var contentHelper = new WebInspector.TimelinePopupContentHelper(info.name);
233 var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(duration, true),
234 Number.millisToString(startTime - model.minimumRecordTime(), true));
235 contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
236 contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(cpuTime, true));
237 contentHelper.appendTextRow(WebInspector.UIString("Message Count"), messageCount);
238 return contentHelper.contentTable();
242 * @param {!WebInspector.TimelineModel.Record} record
243 * @param {!WebInspector.TimelineModel} model
246 WebInspector.TimelineUIUtils.recordTitle = function(record, model)
248 var recordData = record.data();
249 if (record.type() === WebInspector.TimelineModel.RecordType.TimeStamp)
250 return recordData["message"];
251 if (record.type() === WebInspector.TimelineModel.RecordType.JSFrame)
252 return recordData["functionName"];
253 if (WebInspector.TimelineUIUtils.isEventDivider(record)) {
254 var startTime = Number.millisToString(record.startTime() - model.minimumRecordTime());
255 return WebInspector.UIString("%s at %s", WebInspector.TimelineUIUtils.recordStyle(record).title, startTime, true);
257 return WebInspector.TimelineUIUtils.recordStyle(record).title;
261 * @param {!Object} total
262 * @param {!Object} addend
264 WebInspector.TimelineUIUtils.aggregateTimeByCategory = function(total, addend)
266 for (var category in addend)
267 total[category] = (total[category] || 0) + addend[category];
271 * @param {!Object} total
272 * @param {!WebInspector.TimelineModel.Record} record
274 WebInspector.TimelineUIUtils.aggregateTimeForRecord = function(total, record)
276 var childrenTime = 0;
277 var children = record.children();
278 for (var i = 0; i < children.length; ++i) {
279 WebInspector.TimelineUIUtils.aggregateTimeForRecord(total, children[i]);
280 childrenTime += children[i].endTime() - children[i].startTime();
282 var categoryName = WebInspector.TimelineUIUtils.recordStyle(record).category.name;
283 var ownTime = record.endTime() - record.startTime() - childrenTime;
284 total[categoryName] = (total[categoryName] || 0) + ownTime;
288 * @param {!Object} aggregatedStats
290 WebInspector.TimelineUIUtils._generateAggregatedInfo = function(aggregatedStats)
292 var cell = document.createElement("span");
293 cell.className = "timeline-aggregated-info";
294 for (var index in aggregatedStats) {
295 var label = document.createElement("div");
296 label.className = "timeline-aggregated-category timeline-" + index;
297 cell.appendChild(label);
298 var text = document.createElement("span");
299 text.textContent = Number.millisToString(aggregatedStats[index], true);
300 cell.appendChild(text);
306 * @param {!Object} aggregatedStats
307 * @param {!WebInspector.TimelineCategory=} selfCategory
308 * @param {number=} selfTime
311 WebInspector.TimelineUIUtils.generatePieChart = function(aggregatedStats, selfCategory, selfTime)
313 var element = document.createElement("div");
314 element.className = "timeline-aggregated-info";
317 for (var categoryName in aggregatedStats)
318 total += aggregatedStats[categoryName];
320 function formatter(value)
322 return Number.millisToString(value, true);
324 var pieChart = new WebInspector.PieChart(total, formatter);
325 element.appendChild(pieChart.element);
326 var footerElement = element.createChild("div", "timeline-aggregated-info-legend");
328 // In case of self time, first add self, then children of the same category.
329 if (selfCategory && selfTime) {
331 pieChart.addSlice(selfTime, selfCategory.fillColorStop1);
332 var rowElement = footerElement.createChild("div");
333 rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
334 rowElement.createTextChild(WebInspector.UIString("%s %s (Self)", formatter(selfTime), selfCategory.title));
336 // Children of the same category.
337 var categoryTime = aggregatedStats[selfCategory.name];
338 var value = categoryTime - selfTime;
340 pieChart.addSlice(value, selfCategory.fillColorStop0);
341 rowElement = footerElement.createChild("div");
342 rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
343 rowElement.createTextChild(WebInspector.UIString("%s %s (Children)", formatter(value), selfCategory.title));
347 // Add other categories.
348 for (var categoryName in WebInspector.TimelineUIUtils.categories()) {
349 var category = WebInspector.TimelineUIUtils.categories()[categoryName];
350 if (category === selfCategory)
352 var value = aggregatedStats[category.name];
355 pieChart.addSlice(value, category.fillColorStop0);
356 var rowElement = footerElement.createChild("div");
357 rowElement.createChild("div", "timeline-aggregated-category timeline-" + category.name);
358 rowElement.createTextChild(WebInspector.UIString("%s %s", formatter(value), category.title));
363 WebInspector.TimelineUIUtils.generatePopupContentForFrame = function(frame)
365 var contentHelper = new WebInspector.TimelinePopupContentHelper(WebInspector.UIString("Frame"));
366 var durationInMillis = frame.endTime - frame.startTime;
367 var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(frame.endTime - frame.startTime, true),
368 Number.millisToString(frame.startTimeOffset, true));
369 contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
370 contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1000 / durationInMillis));
371 contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(frame.cpuTime, true));
372 contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
373 WebInspector.TimelineUIUtils._generateAggregatedInfo(frame.timeByCategory));
374 if (WebInspector.experimentsSettings.layersPanel.isEnabled() && frame.layerTree) {
375 var layerTreeSnapshot = new WebInspector.LayerTreeSnapshot(frame.layerTree);
376 contentHelper.appendElementRow(WebInspector.UIString("Layer tree"),
377 WebInspector.Linkifier.linkifyUsingRevealer(layerTreeSnapshot, WebInspector.UIString("show")));
379 return contentHelper.contentTable();
383 * @param {!WebInspector.FrameStatistics} statistics
385 WebInspector.TimelineUIUtils.generatePopupContentForFrameStatistics = function(statistics)
388 * @param {number} time
390 function formatTimeAndFPS(time)
392 return WebInspector.UIString("%s (%.0f FPS)", Number.millisToString(time, true), 1 / time);
395 var contentHelper = new WebInspector.TimelineDetailsContentHelper(null, null, false);
396 contentHelper.appendTextRow(WebInspector.UIString("Minimum Time"), formatTimeAndFPS(statistics.minDuration));
397 contentHelper.appendTextRow(WebInspector.UIString("Average Time"), formatTimeAndFPS(statistics.average));
398 contentHelper.appendTextRow(WebInspector.UIString("Maximum Time"), formatTimeAndFPS(statistics.maxDuration));
399 contentHelper.appendTextRow(WebInspector.UIString("Standard Deviation"), Number.millisToString(statistics.stddev, true));
401 return contentHelper.element;
405 * @param {!CanvasRenderingContext2D} context
406 * @param {number} width
407 * @param {number} height
408 * @param {string} color0
409 * @param {string} color1
410 * @param {string} color2
412 WebInspector.TimelineUIUtils.createFillStyle = function(context, width, height, color0, color1, color2)
414 var gradient = context.createLinearGradient(0, 0, width, height);
415 gradient.addColorStop(0, color0);
416 gradient.addColorStop(0.25, color1);
417 gradient.addColorStop(0.75, color1);
418 gradient.addColorStop(1, color2);
423 * @param {!CanvasRenderingContext2D} context
424 * @param {number} width
425 * @param {number} height
426 * @param {!WebInspector.TimelineCategory} category
428 WebInspector.TimelineUIUtils.createFillStyleForCategory = function(context, width, height, category)
430 return WebInspector.TimelineUIUtils.createFillStyle(context, width, height, category.fillColorStop0, category.fillColorStop1, category.borderColor);
434 * @param {!WebInspector.TimelineCategory} category
436 WebInspector.TimelineUIUtils.createStyleRuleForCategory = function(category)
438 var selector = ".timeline-category-" + category.name + " .timeline-graph-bar, " +
439 ".panel.timeline .timeline-filters-header .filter-checkbox-filter.filter-checkbox-filter-" + category.name + " .checkbox-filter-checkbox, " +
440 ".popover .timeline-" + category.name + ", " +
441 ".timeline-details-view .timeline-" + category.name + ", " +
442 ".timeline-category-" + category.name + " .timeline-tree-icon"
444 return selector + " { background-image: linear-gradient(" +
445 category.fillColorStop0 + ", " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + ");" +
446 " border-color: " + category.borderColor +
451 * @param {!WebInspector.TimelineModel.Record} record
452 * @param {!WebInspector.TimelineModel} model
453 * @param {!WebInspector.Linkifier} linkifier
454 * @param {function(!DocumentFragment)} callback
455 * @param {boolean} loadedFromFile
457 WebInspector.TimelineUIUtils.generatePopupContent = function(record, model, linkifier, callback, loadedFromFile)
459 var imageElement = /** @type {?Element} */ (record.getUserObject("TimelineUIUtils::preview-element") || null);
460 var relatedNode = null;
461 var recordData = record.data();
462 var barrier = new CallbackBarrier();
463 if (!imageElement && WebInspector.TimelineUIUtils.needsPreviewElement(record.type()))
464 WebInspector.DOMPresentationUtils.buildImagePreviewContents(record.target(), recordData["url"], false, barrier.createCallback(saveImage));
465 if (recordData["backendNodeId"])
466 record.target().domModel.pushNodesByBackendIdsToFrontend([recordData["backendNodeId"]], barrier.createCallback(setRelatedNode));
467 barrier.callWhenDone(callbackWrapper);
470 * @param {!Element=} element
472 function saveImage(element)
474 imageElement = element || null;
475 record.setUserObject("TimelineUIUtils::preview-element", element);
479 * @param {?Array.<!DOMAgent.NodeId>} nodeIds
481 function setRelatedNode(nodeIds)
484 relatedNode = record.target().domModel.nodeForId(nodeIds[0]);
487 function callbackWrapper()
489 callback(WebInspector.TimelineUIUtils._generatePopupContentSynchronously(record, model, linkifier, imageElement, relatedNode, loadedFromFile));
494 * @param {!WebInspector.TimelineModel.Record} record
495 * @param {!WebInspector.TimelineModel} model
496 * @param {!WebInspector.Linkifier} linkifier
497 * @param {?Element} imagePreviewElement
498 * @param {?WebInspector.DOMNode} relatedNode
499 * @param {boolean} loadedFromFile
500 * @return {!DocumentFragment}
502 WebInspector.TimelineUIUtils._generatePopupContentSynchronously = function(record, model, linkifier, imagePreviewElement, relatedNode, loadedFromFile)
504 var fragment = document.createDocumentFragment();
505 if (record.children().length)
506 fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats(), record.category(), record.selfTime()));
508 fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats()));
510 const recordTypes = WebInspector.TimelineModel.RecordType;
512 // The messages may vary per record.type();
513 var callSiteStackTraceLabel;
515 var relatedNodeLabel;
517 var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.target(), linkifier, true);
518 contentHelper.appendTextRow(WebInspector.UIString("Self Time"), Number.millisToString(record.selfTime(), true));
519 contentHelper.appendTextRow(WebInspector.UIString("Start Time"), Number.millisToString(record.startTime() - model.minimumRecordTime()));
520 var recordData = record.data();
522 switch (record.type()) {
523 case recordTypes.GCEvent:
524 contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(recordData["usedHeapSizeDelta"]));
526 case recordTypes.TimerFire:
527 callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
528 // Fall-through intended.
530 case recordTypes.TimerInstall:
531 case recordTypes.TimerRemove:
532 contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), recordData["timerId"]);
533 var initiator = record.initiator() || record;
534 if (typeof recordData["timeout"] === "number") {
535 contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.millisToString(recordData["timeout"]));
536 contentHelper.appendTextRow(WebInspector.UIString("Repeats"), recordData["singleShot"]);
539 case recordTypes.FireAnimationFrame:
540 callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
541 contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), recordData["id"]);
543 case recordTypes.FunctionCall:
544 if (recordData["scriptName"])
545 contentHelper.appendLocationRow(WebInspector.UIString("Location"), recordData["scriptName"], recordData["scriptLine"]);
547 case recordTypes.ResourceSendRequest:
548 case recordTypes.ResourceReceiveResponse:
549 case recordTypes.ResourceReceivedData:
550 case recordTypes.ResourceFinish:
551 var url = record.url();
553 contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(url));
554 if (imagePreviewElement)
555 contentHelper.appendElementRow(WebInspector.UIString("Preview"), imagePreviewElement);
556 if (recordData["requestMethod"])
557 contentHelper.appendTextRow(WebInspector.UIString("Request Method"), recordData["requestMethod"]);
558 if (typeof recordData["statusCode"] === "number")
559 contentHelper.appendTextRow(WebInspector.UIString("Status Code"), recordData["statusCode"]);
560 if (recordData["mimeType"])
561 contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), recordData["mimeType"]);
562 if (recordData["encodedDataLength"])
563 contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", recordData["encodedDataLength"]));
565 case recordTypes.EvaluateScript:
566 var url = record.url();
567 if (recordData && url)
568 contentHelper.appendLocationRow(WebInspector.UIString("Script"), url, recordData["lineNumber"]);
570 case recordTypes.Paint:
571 var clip = recordData["clip"];
573 contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1]));
574 var clipWidth = WebInspector.TimelineUIUtils._quadWidth(clip);
575 var clipHeight = WebInspector.TimelineUIUtils._quadHeight(clip);
576 contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d × %d", clipWidth, clipHeight));
578 // Backward compatibility: older version used x, y, width, height fields directly in data.
579 if (typeof recordData["x"] !== "undefined" && typeof recordData["y"] !== "undefined")
580 contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", recordData["x"], recordData["y"]));
581 if (typeof recordData["width"] !== "undefined" && typeof recordData["height"] !== "undefined")
582 contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d\u2009\u00d7\u2009%d", recordData["width"], recordData["height"]));
584 // Fall-through intended.
586 case recordTypes.PaintSetup:
587 case recordTypes.Rasterize:
588 case recordTypes.ScrollLayer:
589 relatedNodeLabel = WebInspector.UIString("Layer root");
591 case recordTypes.DecodeImage:
592 case recordTypes.ResizeImage:
593 relatedNodeLabel = WebInspector.UIString("Image element");
594 var url = record.url();
596 contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(url));
598 case recordTypes.RecalculateStyles: // We don't want to see default details.
599 if (recordData["elementCount"])
600 contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), recordData["elementCount"]);
601 callStackLabel = WebInspector.UIString("Styles recalculation forced");
603 case recordTypes.Layout:
604 if (recordData["dirtyObjects"])
605 contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), recordData["dirtyObjects"]);
606 if (recordData["totalObjects"])
607 contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), recordData["totalObjects"]);
608 if (typeof recordData["partialLayout"] === "boolean") {
609 contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
610 recordData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
612 callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
613 callStackLabel = WebInspector.UIString("Layout forced");
614 relatedNodeLabel = WebInspector.UIString("Layout root");
616 case recordTypes.ConsoleTime:
617 contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
619 case recordTypes.WebSocketCreate:
620 case recordTypes.WebSocketSendHandshakeRequest:
621 case recordTypes.WebSocketReceiveHandshakeResponse:
622 case recordTypes.WebSocketDestroy:
623 var initiatorData = record.initiator() ? record.initiator().data() : recordData;
624 if (typeof initiatorData["webSocketURL"] !== "undefined")
625 contentHelper.appendTextRow(WebInspector.UIString("URL"), initiatorData["webSocketURL"]);
626 if (typeof initiatorData["webSocketProtocol"] !== "undefined")
627 contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), initiatorData["webSocketProtocol"]);
628 if (typeof recordData["message"] !== "undefined")
629 contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
631 case recordTypes.EmbedderCallback:
632 contentHelper.appendTextRow(WebInspector.UIString("Callback Function"), record.embedderCallbackName);
635 var detailsNode = WebInspector.TimelineUIUtils.buildDetailsNode(record, linkifier, loadedFromFile);
637 contentHelper.appendElementRow(WebInspector.UIString("Details"), detailsNode);
642 contentHelper.appendElementRow(relatedNodeLabel || WebInspector.UIString("Related node"), WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode));
644 if (recordData["scriptName"] && record.type() !== recordTypes.FunctionCall)
645 contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), recordData["scriptName"], recordData["scriptLine"]);
646 var callSiteStackTrace = record.callSiteStackTrace();
647 if (callSiteStackTrace)
648 contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), callSiteStackTrace);
649 var recordStackTrace = record.stackTrace();
650 if (recordStackTrace)
651 contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), recordStackTrace);
653 if (record.warnings()) {
654 var ul = document.createElement("ul");
655 for (var i = 0; i < record.warnings().length; ++i)
656 ul.createChild("li").textContent = record.warnings()[i];
657 contentHelper.appendElementRow(WebInspector.UIString("Warning"), ul);
659 fragment.appendChild(contentHelper.element);
664 * @param {!Array.<number>} quad
667 WebInspector.TimelineUIUtils._quadWidth = function(quad)
669 return Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
673 * @param {!Array.<number>} quad
676 WebInspector.TimelineUIUtils._quadHeight = function(quad)
678 return Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
682 * @param {!WebInspector.TimelineModel.Record} record
683 * @param {!WebInspector.Linkifier} linkifier
684 * @param {boolean} loadedFromFile
687 WebInspector.TimelineUIUtils.buildDetailsNode = function(record, linkifier, loadedFromFile)
691 var recordData = record.data();
692 switch (record.type()) {
693 case WebInspector.TimelineModel.RecordType.GCEvent:
694 detailsText = WebInspector.UIString("%s collected", Number.bytesToString(recordData["usedHeapSizeDelta"]));
696 case WebInspector.TimelineModel.RecordType.TimerFire:
697 detailsText = recordData["timerId"];
699 case WebInspector.TimelineModel.RecordType.FunctionCall:
700 details = linkifyLocation(recordData["scriptId"], recordData["scriptName"], recordData["scriptLine"], 0);
702 case WebInspector.TimelineModel.RecordType.FireAnimationFrame:
703 detailsText = recordData["id"];
705 case WebInspector.TimelineModel.RecordType.EventDispatch:
706 detailsText = recordData ? recordData["type"] : null;
708 case WebInspector.TimelineModel.RecordType.Paint:
709 var width = recordData.clip ? WebInspector.TimelineUIUtils._quadWidth(recordData.clip) : recordData.width;
710 var height = recordData.clip ? WebInspector.TimelineUIUtils._quadHeight(recordData.clip) : recordData.height;
712 detailsText = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height);
714 case WebInspector.TimelineModel.RecordType.TimerInstall:
715 case WebInspector.TimelineModel.RecordType.TimerRemove:
716 details = linkifyTopCallFrame();
717 detailsText = recordData["timerId"];
719 case WebInspector.TimelineModel.RecordType.RequestAnimationFrame:
720 case WebInspector.TimelineModel.RecordType.CancelAnimationFrame:
721 details = linkifyTopCallFrame();
722 detailsText = recordData["id"];
724 case WebInspector.TimelineModel.RecordType.ParseHTML:
725 case WebInspector.TimelineModel.RecordType.RecalculateStyles:
726 details = linkifyTopCallFrame();
728 case WebInspector.TimelineModel.RecordType.EvaluateScript:
729 var url = record.url();
730 if (recordData && url)
731 details = linkifyLocation("", url, recordData["lineNumber"], 0);
733 case WebInspector.TimelineModel.RecordType.XHRReadyStateChange:
734 case WebInspector.TimelineModel.RecordType.XHRLoad:
735 case WebInspector.TimelineModel.RecordType.ResourceSendRequest:
736 case WebInspector.TimelineModel.RecordType.ResourceReceivedData:
737 case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse:
738 case WebInspector.TimelineModel.RecordType.ResourceFinish:
739 case WebInspector.TimelineModel.RecordType.DecodeImage:
740 case WebInspector.TimelineModel.RecordType.ResizeImage:
741 var url = record.url();
743 detailsText = WebInspector.displayNameForURL(url);
745 case WebInspector.TimelineModel.RecordType.ConsoleTime:
746 detailsText = recordData["message"];
748 case WebInspector.TimelineModel.RecordType.EmbedderCallback:
749 detailsText = recordData["callbackName"];
752 details = linkifyTopCallFrame();
756 if (!details && detailsText)
757 details = document.createTextNode(detailsText);
761 * @param {string} scriptId
762 * @param {string} url
763 * @param {number} lineNumber
764 * @param {number=} columnNumber
766 function linkifyLocation(scriptId, url, lineNumber, columnNumber)
768 if (!loadedFromFile && scriptId !== "0") {
769 var location = new WebInspector.DebuggerModel.Location(
773 (columnNumber || 1) - 1);
774 return linkifier.linkifyRawLocation(location, "timeline-details");
780 // FIXME(62725): stack trace line/column numbers are one-based.
781 columnNumber = columnNumber ? columnNumber - 1 : 0;
782 return linkifier.linkifyLocation(record.target(), url, lineNumber - 1, columnNumber, "timeline-details");
786 * @param {!ConsoleAgent.CallFrame} callFrame
788 function linkifyCallFrame(callFrame)
790 return linkifyLocation(callFrame.scriptId, callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
796 function linkifyTopCallFrame()
798 if (record.stackTrace())
799 return linkifyCallFrame(record.stackTrace()[0]);
800 if (record.callSiteStackTrace())
801 return linkifyCallFrame(record.callSiteStackTrace()[0]);
808 * @extends {WebInspector.Object}
809 * @param {string} name
810 * @param {string} title
811 * @param {number} overviewStripGroupIndex
812 * @param {string} borderColor
813 * @param {string} backgroundColor
814 * @param {string} fillColorStop0
815 * @param {string} fillColorStop1
817 WebInspector.TimelineCategory = function(name, title, overviewStripGroupIndex, borderColor, backgroundColor, fillColorStop0, fillColorStop1)
821 this.overviewStripGroupIndex = overviewStripGroupIndex;
822 this.borderColor = borderColor;
823 this.backgroundColor = backgroundColor;
824 this.fillColorStop0 = fillColorStop0;
825 this.fillColorStop1 = fillColorStop1;
829 WebInspector.TimelineCategory.Events = {
830 VisibilityChanged: "VisibilityChanged"
833 WebInspector.TimelineCategory.prototype = {
844 this._hidden = hidden;
845 this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged, this);
848 __proto__: WebInspector.Object.prototype
853 * @param {string} title
855 WebInspector.TimelinePopupContentHelper = function(title)
857 this._contentTable = document.createElement("table");
858 var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title");
859 titleCell.colSpan = 2;
860 var titleRow = document.createElement("tr");
861 titleRow.appendChild(titleCell);
862 this._contentTable.appendChild(titleRow);
865 WebInspector.TimelinePopupContentHelper.prototype = {
869 contentTable: function()
871 return this._contentTable;
875 * @param {string=} styleName
877 _createCell: function(content, styleName)
879 var text = document.createElement("label");
880 text.appendChild(document.createTextNode(content));
881 var cell = document.createElement("td");
882 cell.className = "timeline-details";
884 cell.className += " " + styleName;
885 cell.textContent = content;
890 * @param {string} title
891 * @param {string|number|boolean} content
893 appendTextRow: function(title, content)
895 var row = document.createElement("tr");
896 row.appendChild(this._createCell(title, "timeline-details-row-title"));
897 row.appendChild(this._createCell(content, "timeline-details-row-data"));
898 this._contentTable.appendChild(row);
902 * @param {string} title
903 * @param {!Node|string} content
905 appendElementRow: function(title, content)
907 var row = document.createElement("tr");
908 var titleCell = this._createCell(title, "timeline-details-row-title");
909 row.appendChild(titleCell);
910 var cell = document.createElement("td");
911 cell.className = "details";
912 if (content instanceof Node)
913 cell.appendChild(content);
915 cell.createTextChild(content || "");
916 row.appendChild(cell);
917 this._contentTable.appendChild(row);
923 * @param {?WebInspector.Target} target
924 * @param {?WebInspector.Linkifier} linkifier
925 * @param {boolean} monospaceValues
927 WebInspector.TimelineDetailsContentHelper = function(target, linkifier, monospaceValues)
929 this._linkifier = linkifier;
930 this._target = target;
931 this.element = document.createElement("div");
932 this.element.className = "timeline-details-view-block";
933 this._monospaceValues = monospaceValues;
936 WebInspector.TimelineDetailsContentHelper.prototype = {
938 * @param {string} title
939 * @param {string|number|boolean} value
941 appendTextRow: function(title, value)
943 var rowElement = this.element.createChild("div", "timeline-details-view-row");
944 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
945 rowElement.createChild("span", "timeline-details-view-row-value" + (this._monospaceValues ? " monospace" : "")).textContent = value;
949 * @param {string} title
950 * @param {!Node|string} content
952 appendElementRow: function(title, content)
954 var rowElement = this.element.createChild("div", "timeline-details-view-row");
955 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
956 var valueElement = rowElement.createChild("span", "timeline-details-view-row-details" + (this._monospaceValues ? " monospace" : ""));
957 if (content instanceof Node)
958 valueElement.appendChild(content);
960 valueElement.createTextChild(content || "");
964 * @param {string} title
965 * @param {string} url
966 * @param {number} line
968 appendLocationRow: function(title, url, line)
970 if (!this._linkifier || !this._target)
972 this.appendElementRow(title, this._linkifier.linkifyLocation(this._target, url, line - 1) || "");
976 * @param {string} title
977 * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
979 appendStackTrace: function(title, stackTrace)
981 if (!this._linkifier || !this._target)
984 var rowElement = this.element.createChild("div", "timeline-details-view-row");
985 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
986 var stackTraceElement = rowElement.createChild("div", "timeline-details-view-row-stack-trace monospace");
988 for (var i = 0; i < stackTrace.length; ++i) {
989 var stackFrame = stackTrace[i];
990 var row = stackTraceElement.createChild("div");
991 row.createTextChild(stackFrame.functionName || WebInspector.UIString("(anonymous function)"));
992 row.createTextChild(" @ ");
993 var urlElement = this._linkifier.linkifyLocation(this._target, stackFrame.url, stackFrame.lineNumber - 1);
994 row.appendChild(urlElement);