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, 68%, 53%)", "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.AutosizeText] = { title: WebInspector.UIString("Autosize Text"), 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.MarkDOMContent] = { title: WebInspector.UIString("DOMContentLoaded event"), category: categories["scripting"] };
93 recordStyles[recordTypes.MarkLoad] = { title: WebInspector.UIString("Load event"), category: categories["scripting"] };
94 recordStyles[recordTypes.MarkFirstPaint] = { title: WebInspector.UIString("First paint"), category: categories["painting"] };
95 recordStyles[recordTypes.TimeStamp] = { title: WebInspector.UIString("Stamp"), category: categories["scripting"] };
96 recordStyles[recordTypes.ConsoleTime] = { title: WebInspector.UIString("Console Time"), category: categories["scripting"] };
97 recordStyles[recordTypes.ScheduleResourceRequest] = { title: WebInspector.UIString("Schedule Request"), category: categories["loading"] };
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 var recordStyles = WebInspector.TimelineUIUtils._initRecordStyles();
118 var result = recordStyles[record.type];
121 title: WebInspector.UIString("Unknown: %s", record.type),
122 category: WebInspector.TimelineUIUtils.categories()["other"]
124 recordStyles[record.type] = result;
130 * @param {!WebInspector.TimelineModel.Record} record
131 * @return {!WebInspector.TimelineCategory}
133 WebInspector.TimelineUIUtils.categoryForRecord = function(record)
135 return WebInspector.TimelineUIUtils.recordStyle(record).category;
139 * @param {!WebInspector.TimelineModel.Record} record
141 WebInspector.TimelineUIUtils.isEventDivider = function(record)
143 var recordTypes = WebInspector.TimelineModel.RecordType;
144 if (record.type === recordTypes.TimeStamp)
146 if (record.type === recordTypes.MarkFirstPaint)
148 if (record.type === recordTypes.MarkDOMContent || record.type === recordTypes.MarkLoad) {
149 if (record.data && ((typeof record.data.isMainFrame) === "boolean"))
150 return record.data.isMainFrame;
156 * @param {string=} recordType
159 WebInspector.TimelineUIUtils.needsPreviewElement = function(recordType)
163 const recordTypes = WebInspector.TimelineModel.RecordType;
164 switch (recordType) {
165 case recordTypes.ScheduleResourceRequest:
166 case recordTypes.ResourceSendRequest:
167 case recordTypes.ResourceReceiveResponse:
168 case recordTypes.ResourceReceivedData:
169 case recordTypes.ResourceFinish:
177 * @param {string} recordType
178 * @param {string=} title
180 WebInspector.TimelineUIUtils.createEventDivider = function(recordType, title)
182 var eventDivider = document.createElement("div");
183 eventDivider.className = "resources-event-divider";
184 var recordTypes = WebInspector.TimelineModel.RecordType;
186 if (recordType === recordTypes.MarkDOMContent)
187 eventDivider.className += " resources-blue-divider";
188 else if (recordType === recordTypes.MarkLoad)
189 eventDivider.className += " resources-red-divider";
190 else if (recordType === recordTypes.MarkFirstPaint)
191 eventDivider.className += " resources-green-divider";
192 else if (recordType === recordTypes.TimeStamp)
193 eventDivider.className += " resources-orange-divider";
194 else if (recordType === recordTypes.BeginFrame)
195 eventDivider.className += " timeline-frame-divider";
198 eventDivider.title = title;
205 * @param {!WebInspector.TimelineModel} model
206 * @param {!{name: string, tasks: !Array.<!{startTime: number, endTime: number}>, firstTaskIndex: number, lastTaskIndex: number}} info
209 WebInspector.TimelineUIUtils.generateMainThreadBarPopupContent = function(model, info)
211 var firstTaskIndex = info.firstTaskIndex;
212 var lastTaskIndex = info.lastTaskIndex;
213 var tasks = info.tasks;
214 var messageCount = lastTaskIndex - firstTaskIndex + 1;
217 for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) {
219 cpuTime += task.endTime - task.startTime;
221 var startTime = tasks[firstTaskIndex].startTime;
222 var endTime = tasks[lastTaskIndex].endTime;
223 var duration = endTime - startTime;
225 var contentHelper = new WebInspector.TimelinePopupContentHelper(info.name);
226 var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(duration, true),
227 Number.millisToString(startTime - model.minimumRecordTime(), true));
228 contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
229 contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(cpuTime, true));
230 contentHelper.appendTextRow(WebInspector.UIString("Message Count"), messageCount);
231 return contentHelper.contentTable();
235 * @param {!WebInspector.TimelineModel.Record} record
238 WebInspector.TimelineUIUtils.recordTitle = function(record)
240 if (record.type === WebInspector.TimelineModel.RecordType.TimeStamp)
241 return record.data["message"];
242 if (WebInspector.TimelineUIUtils.isEventDivider(record)) {
243 var startTime = Number.millisToString(record.startTimeOffset);
244 return WebInspector.UIString("%s at %s", WebInspector.TimelineUIUtils.recordStyle(record).title, startTime, true);
246 return WebInspector.TimelineUIUtils.recordStyle(record).title;
250 * @param {!Object} total
251 * @param {!Object} addend
253 WebInspector.TimelineUIUtils.aggregateTimeByCategory = function(total, addend)
255 for (var category in addend)
256 total[category] = (total[category] || 0) + addend[category];
260 * @param {!Object} total
261 * @param {!WebInspector.TimelineModel.Record} record
263 WebInspector.TimelineUIUtils.aggregateTimeForRecord = function(total, record)
265 var childrenTime = 0;
266 var children = record.children;
267 for (var i = 0; i < children.length; ++i) {
268 WebInspector.TimelineUIUtils.aggregateTimeForRecord(total, children[i]);
269 childrenTime += children[i].endTime - children[i].startTime;
271 var categoryName = WebInspector.TimelineUIUtils.recordStyle(record).category.name;
272 var ownTime = record.endTime - record.startTime - childrenTime;
273 total[categoryName] = (total[categoryName] || 0) + ownTime;
277 * @param {!Object} aggregatedStats
279 WebInspector.TimelineUIUtils._generateAggregatedInfo = function(aggregatedStats)
281 var cell = document.createElement("span");
282 cell.className = "timeline-aggregated-info";
283 for (var index in aggregatedStats) {
284 var label = document.createElement("div");
285 label.className = "timeline-aggregated-category timeline-" + index;
286 cell.appendChild(label);
287 var text = document.createElement("span");
288 text.textContent = Number.millisToString(aggregatedStats[index], true);
289 cell.appendChild(text);
295 * @param {!Object} aggregatedStats
296 * @param {!WebInspector.TimelineCategory=} selfCategory
297 * @param {number=} selfTime
300 WebInspector.TimelineUIUtils.generatePieChart = function(aggregatedStats, selfCategory, selfTime)
302 var element = document.createElement("div");
303 element.className = "timeline-aggregated-info";
306 for (var categoryName in aggregatedStats)
307 total += aggregatedStats[categoryName];
309 function formatter(value)
311 return Number.millisToString(value, true);
313 var pieChart = new WebInspector.PieChart(total, formatter);
314 element.appendChild(pieChart.element);
315 var footerElement = element.createChild("div", "timeline-aggregated-info-legend");
317 // In case of self time, first add self, then children of the same category.
318 if (selfCategory && selfTime) {
320 pieChart.addSlice(selfTime, selfCategory.fillColorStop1);
321 var rowElement = footerElement.createChild("div");
322 rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
323 rowElement.createTextChild(WebInspector.UIString("%s %s (Self)", formatter(selfTime), selfCategory.title));
325 // Children of the same category.
326 var categoryTime = aggregatedStats[selfCategory.name];
327 var value = categoryTime - selfTime;
329 pieChart.addSlice(value, selfCategory.fillColorStop0);
330 rowElement = footerElement.createChild("div");
331 rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
332 rowElement.createTextChild(WebInspector.UIString("%s %s (Children)", formatter(value), selfCategory.title));
336 // Add other categories.
337 for (var categoryName in WebInspector.TimelineUIUtils.categories()) {
338 var category = WebInspector.TimelineUIUtils.categories()[categoryName];
339 if (category === selfCategory)
341 var value = aggregatedStats[category.name];
344 pieChart.addSlice(value, category.fillColorStop0);
345 var rowElement = footerElement.createChild("div");
346 rowElement.createChild("div", "timeline-aggregated-category timeline-" + category.name);
347 rowElement.createTextChild(WebInspector.UIString("%s %s", formatter(value), category.title));
352 WebInspector.TimelineUIUtils.generatePopupContentForFrame = function(frame)
354 var contentHelper = new WebInspector.TimelinePopupContentHelper(WebInspector.UIString("Frame"));
355 var durationInMillis = frame.endTime - frame.startTime;
356 var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(frame.endTime - frame.startTime, true),
357 Number.millisToString(frame.startTimeOffset, true));
358 contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
359 contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1000 / durationInMillis));
360 contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(frame.cpuTime, true));
361 contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
362 WebInspector.TimelineUIUtils._generateAggregatedInfo(frame.timeByCategory));
363 if (WebInspector.experimentsSettings.layersPanel.isEnabled() && frame.layerTree) {
364 var layerTreeSnapshot = new WebInspector.LayerTreeSnapshot(frame.layerTree);
365 contentHelper.appendElementRow(WebInspector.UIString("Layer tree"),
366 WebInspector.Linkifier.linkifyUsingRevealer(layerTreeSnapshot, WebInspector.UIString("show")));
368 return contentHelper.contentTable();
372 * @param {!WebInspector.FrameStatistics} statistics
374 WebInspector.TimelineUIUtils.generatePopupContentForFrameStatistics = function(statistics)
377 * @param {number} time
379 function formatTimeAndFPS(time)
381 return WebInspector.UIString("%s (%.0f FPS)", Number.millisToString(time, true), 1 / time);
384 var contentHelper = new WebInspector.TimelineDetailsContentHelper(new WebInspector.Linkifier(), false);
385 contentHelper.appendTextRow(WebInspector.UIString("Minimum Time"), formatTimeAndFPS(statistics.minDuration));
386 contentHelper.appendTextRow(WebInspector.UIString("Average Time"), formatTimeAndFPS(statistics.average));
387 contentHelper.appendTextRow(WebInspector.UIString("Maximum Time"), formatTimeAndFPS(statistics.maxDuration));
388 contentHelper.appendTextRow(WebInspector.UIString("Standard Deviation"), Number.millisToString(statistics.stddev, true));
390 return contentHelper.element;
394 * @param {!CanvasRenderingContext2D} context
395 * @param {number} width
396 * @param {number} height
397 * @param {string} color0
398 * @param {string} color1
399 * @param {string} color2
401 WebInspector.TimelineUIUtils.createFillStyle = function(context, width, height, color0, color1, color2)
403 var gradient = context.createLinearGradient(0, 0, width, height);
404 gradient.addColorStop(0, color0);
405 gradient.addColorStop(0.25, color1);
406 gradient.addColorStop(0.75, color1);
407 gradient.addColorStop(1, color2);
412 * @param {!CanvasRenderingContext2D} context
413 * @param {number} width
414 * @param {number} height
415 * @param {!WebInspector.TimelineCategory} category
417 WebInspector.TimelineUIUtils.createFillStyleForCategory = function(context, width, height, category)
419 return WebInspector.TimelineUIUtils.createFillStyle(context, width, height, category.fillColorStop0, category.fillColorStop1, category.borderColor);
423 * @param {!WebInspector.TimelineCategory} category
425 WebInspector.TimelineUIUtils.createStyleRuleForCategory = function(category)
427 var selector = ".timeline-category-" + category.name + " .timeline-graph-bar, " +
428 ".panel.timeline .timeline-filters-header .filter-checkbox-filter.filter-checkbox-filter-" + category.name + " .checkbox-filter-checkbox, " +
429 ".popover .timeline-" + category.name + ", " +
430 ".timeline-details-view .timeline-" + category.name + ", " +
431 ".timeline-category-" + category.name + " .timeline-tree-icon"
433 return selector + " { background-image: linear-gradient(" +
434 category.fillColorStop0 + ", " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + ");" +
435 " border-color: " + category.borderColor +
440 * @param {!WebInspector.TimelineModel.Record} record
441 * @param {!WebInspector.Linkifier} linkifier
442 * @param {function(!DocumentFragment)} callback
444 WebInspector.TimelineUIUtils.generatePopupContent = function(record, linkifier, callback)
446 var imageElement = /** @type {?Element} */ (record.getUserObject("TimelineUIUtils::preview-element") || null);
447 var relatedNode = null;
449 var barrier = new CallbackBarrier();
450 if (!imageElement && WebInspector.TimelineUIUtils.needsPreviewElement(record.type))
451 WebInspector.DOMPresentationUtils.buildImagePreviewContents(record.url, false, barrier.createCallback(saveImage));
452 if (record.relatedBackendNodeId())
453 WebInspector.domModel.pushNodesByBackendIdsToFrontend([record.relatedBackendNodeId()], barrier.createCallback(setRelatedNode));
454 barrier.callWhenDone(callbackWrapper);
457 * @param {!Element=} element
459 function saveImage(element)
461 imageElement = element || null;
462 record.setUserObject("TimelineUIUtils::preview-element", element);
466 * @param {?Array.<!DOMAgent.NodeId>} nodeIds
468 function setRelatedNode(nodeIds)
471 relatedNode = WebInspector.domModel.nodeForId(nodeIds[0]);
474 function callbackWrapper()
476 callback(WebInspector.TimelineUIUtils._generatePopupContentSynchronously(record, linkifier, imageElement, relatedNode));
481 * @param {!WebInspector.TimelineModel.Record} record
482 * @param {!WebInspector.Linkifier} linkifier
483 * @param {?Element} imagePreviewElement
484 * @param {?WebInspector.DOMNode} relatedNode
485 * @return {!DocumentFragment}
487 WebInspector.TimelineUIUtils._generatePopupContentSynchronously = function(record, linkifier, imagePreviewElement, relatedNode)
489 var fragment = document.createDocumentFragment();
490 if (record.children.length)
491 fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats, record.category, record.selfTime));
493 fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats));
495 const recordTypes = WebInspector.TimelineModel.RecordType;
497 // The messages may vary per record type;
498 var callSiteStackTraceLabel;
500 var relatedNodeLabel;
502 var contentHelper = new WebInspector.TimelineDetailsContentHelper(linkifier, true);
503 contentHelper.appendTextRow(WebInspector.UIString("Self Time"), Number.millisToString(record.selfTime, true));
504 contentHelper.appendTextRow(WebInspector.UIString("Start Time"), Number.millisToString(record.startTimeOffset));
506 switch (record.type) {
507 case recordTypes.GCEvent:
508 contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(record.data["usedHeapSizeDelta"]));
510 case recordTypes.TimerFire:
511 callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
512 // Fall-through intended.
514 case recordTypes.TimerInstall:
515 case recordTypes.TimerRemove:
516 contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), record.data["timerId"]);
517 if (typeof record.timeout === "number") {
518 contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.millisToString(record.timeout));
519 contentHelper.appendTextRow(WebInspector.UIString("Repeats"), !record.singleShot);
522 case recordTypes.FireAnimationFrame:
523 callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
524 contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), record.data["id"]);
526 case recordTypes.FunctionCall:
527 if (record.scriptName)
528 contentHelper.appendLocationRow(WebInspector.UIString("Location"), record.scriptName, record.scriptLine);
530 case recordTypes.ScheduleResourceRequest:
531 case recordTypes.ResourceSendRequest:
532 case recordTypes.ResourceReceiveResponse:
533 case recordTypes.ResourceReceivedData:
534 case recordTypes.ResourceFinish:
535 contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(record.url));
536 if (imagePreviewElement)
537 contentHelper.appendElementRow(WebInspector.UIString("Preview"), imagePreviewElement);
538 if (record.data["requestMethod"])
539 contentHelper.appendTextRow(WebInspector.UIString("Request Method"), record.data["requestMethod"]);
540 if (typeof record.data["statusCode"] === "number")
541 contentHelper.appendTextRow(WebInspector.UIString("Status Code"), record.data["statusCode"]);
542 if (record.data["mimeType"])
543 contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), record.data["mimeType"]);
544 if (record.data["encodedDataLength"])
545 contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", record.data["encodedDataLength"]));
547 case recordTypes.EvaluateScript:
548 if (record.data && record.url)
549 contentHelper.appendLocationRow(WebInspector.UIString("Script"), record.url, record.data["lineNumber"]);
551 case recordTypes.Paint:
552 var clip = record.data["clip"];
554 contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1]));
555 var clipWidth = WebInspector.TimelineUIUtils._quadWidth(clip);
556 var clipHeight = WebInspector.TimelineUIUtils._quadHeight(clip);
557 contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d × %d", clipWidth, clipHeight));
559 // Backward compatibility: older version used x, y, width, height fields directly in data.
560 if (typeof record.data["x"] !== "undefined" && typeof record.data["y"] !== "undefined")
561 contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", record.data["x"], record.data["y"]));
562 if (typeof record.data["width"] !== "undefined" && typeof record.data["height"] !== "undefined")
563 contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d\u2009\u00d7\u2009%d", record.data["width"], record.data["height"]));
565 // Fall-through intended.
567 case recordTypes.PaintSetup:
568 case recordTypes.Rasterize:
569 case recordTypes.ScrollLayer:
570 relatedNodeLabel = WebInspector.UIString("Layer root");
572 case recordTypes.AutosizeText:
573 relatedNodeLabel = WebInspector.UIString("Root node");
575 case recordTypes.DecodeImage:
576 case recordTypes.ResizeImage:
577 relatedNodeLabel = WebInspector.UIString("Image element");
579 contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(record.url));
581 case recordTypes.RecalculateStyles: // We don't want to see default details.
582 if (record.data["elementCount"])
583 contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), record.data["elementCount"]);
584 callStackLabel = WebInspector.UIString("Styles recalculation forced");
586 case recordTypes.Layout:
587 if (record.data["dirtyObjects"])
588 contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), record.data["dirtyObjects"]);
589 if (record.data["totalObjects"])
590 contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), record.data["totalObjects"]);
591 if (typeof record.data["partialLayout"] === "boolean") {
592 contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
593 record.data["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
595 callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
596 callStackLabel = WebInspector.UIString("Layout forced");
597 relatedNodeLabel = WebInspector.UIString("Layout root");
599 case recordTypes.ConsoleTime:
600 contentHelper.appendTextRow(WebInspector.UIString("Message"), record.data["message"]);
602 case recordTypes.WebSocketCreate:
603 case recordTypes.WebSocketSendHandshakeRequest:
604 case recordTypes.WebSocketReceiveHandshakeResponse:
605 case recordTypes.WebSocketDestroy:
606 if (typeof record.webSocketURL !== "undefined")
607 contentHelper.appendTextRow(WebInspector.UIString("URL"), record.webSocketURL);
608 if (typeof record.webSocketProtocol !== "undefined")
609 contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), record.webSocketProtocol);
610 if (typeof record.data["message"] !== "undefined")
611 contentHelper.appendTextRow(WebInspector.UIString("Message"), record.data["message"]);
613 case recordTypes.EmbedderCallback:
614 contentHelper.appendTextRow(WebInspector.UIString("Callback Function"), record.embedderCallbackName);
617 var detailsNode = WebInspector.TimelineUIUtils.buildDetailsNode(record, linkifier);
619 contentHelper.appendElementRow(WebInspector.UIString("Details"), detailsNode);
624 contentHelper.appendElementRow(relatedNodeLabel || WebInspector.UIString("Related node"), WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode));
626 if (record.scriptName && record.type !== recordTypes.FunctionCall)
627 contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), record.scriptName, record.scriptLine);
629 if (record.jsHeapSizeUsed) {
630 if (record.usedHeapSizeDelta) {
631 var sign = record.usedHeapSizeDelta > 0 ? "+" : "-";
632 contentHelper.appendTextRow(WebInspector.UIString("Used JavaScript Heap Size"),
633 WebInspector.UIString("%s (%s%s)", Number.bytesToString(record.jsHeapSizeUsed), sign, Number.bytesToString(Math.abs(record.usedHeapSizeDelta))));
634 } else if (record.category === WebInspector.TimelineUIUtils.categories().scripting)
635 contentHelper.appendTextRow(WebInspector.UIString("Used JavaScript Heap Size"), Number.bytesToString(record.jsHeapSizeUsed));
638 if (record.callSiteStackTrace)
639 contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), record.callSiteStackTrace);
641 if (record.stackTrace)
642 contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), record.stackTrace);
644 if (record.warnings()) {
645 var ul = document.createElement("ul");
646 for (var i = 0; i < record.warnings().length; ++i)
647 ul.createChild("li").textContent = record.warnings()[i];
648 contentHelper.appendElementRow(WebInspector.UIString("Warning"), ul);
650 fragment.appendChild(contentHelper.element);
655 * @param {!Array.<number>} quad
658 WebInspector.TimelineUIUtils._quadWidth = function(quad)
660 return Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
664 * @param {!Array.<number>} quad
667 WebInspector.TimelineUIUtils._quadHeight = function(quad)
669 return Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
673 * @param {!WebInspector.TimelineModel.Record} record
674 * @param {!WebInspector.Linkifier} linkifier
677 WebInspector.TimelineUIUtils.buildDetailsNode = function(record, linkifier)
682 switch (record.type) {
683 case WebInspector.TimelineModel.RecordType.GCEvent:
684 detailsText = WebInspector.UIString("%s collected", Number.bytesToString(record.data["usedHeapSizeDelta"]));
686 case WebInspector.TimelineModel.RecordType.TimerFire:
687 detailsText = record.data["timerId"];
689 case WebInspector.TimelineModel.RecordType.FunctionCall:
690 if (record.scriptName)
691 details = linkifyLocation(record.scriptName, record.scriptLine, 0);
693 case WebInspector.TimelineModel.RecordType.FireAnimationFrame:
694 detailsText = record.data["id"];
696 case WebInspector.TimelineModel.RecordType.EventDispatch:
697 detailsText = record.data ? record.data["type"] : null;
699 case WebInspector.TimelineModel.RecordType.Paint:
700 var width = record.data.clip ? WebInspector.TimelineUIUtils._quadWidth(record.data.clip) : record.data.width;
701 var height = record.data.clip ? WebInspector.TimelineUIUtils._quadHeight(record.data.clip) : record.data.height;
703 detailsText = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height);
705 case WebInspector.TimelineModel.RecordType.TimerInstall:
706 case WebInspector.TimelineModel.RecordType.TimerRemove:
707 details = linkifyTopCallFrame();
708 detailsText = record.data["timerId"];
710 case WebInspector.TimelineModel.RecordType.RequestAnimationFrame:
711 case WebInspector.TimelineModel.RecordType.CancelAnimationFrame:
712 details = linkifyTopCallFrame();
713 detailsText = record.data["id"];
715 case WebInspector.TimelineModel.RecordType.ParseHTML:
716 case WebInspector.TimelineModel.RecordType.RecalculateStyles:
717 details = linkifyTopCallFrame();
719 case WebInspector.TimelineModel.RecordType.EvaluateScript:
720 details = record.url ? linkifyLocation(record.url, record.data["lineNumber"], 0) : null;
722 case WebInspector.TimelineModel.RecordType.XHRReadyStateChange:
723 case WebInspector.TimelineModel.RecordType.XHRLoad:
724 case WebInspector.TimelineModel.RecordType.ScheduleResourceRequest:
725 case WebInspector.TimelineModel.RecordType.ResourceSendRequest:
726 case WebInspector.TimelineModel.RecordType.ResourceReceivedData:
727 case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse:
728 case WebInspector.TimelineModel.RecordType.ResourceFinish:
729 case WebInspector.TimelineModel.RecordType.DecodeImage:
730 case WebInspector.TimelineModel.RecordType.ResizeImage:
731 detailsText = WebInspector.displayNameForURL(record.url);
733 case WebInspector.TimelineModel.RecordType.ConsoleTime:
734 detailsText = record.data["message"];
736 case WebInspector.TimelineModel.RecordType.EmbedderCallback:
737 detailsText = record.data["callbackName"];
740 details = record.scriptName ? linkifyLocation(record.scriptName, record.scriptLine, 0) : linkifyTopCallFrame();
744 if (!details && detailsText)
745 details = document.createTextNode(detailsText);
749 * @param {string} url
750 * @param {number} lineNumber
751 * @param {number=} columnNumber
753 function linkifyLocation(url, lineNumber, columnNumber)
755 // FIXME(62725): stack trace line/column numbers are one-based.
756 columnNumber = columnNumber ? columnNumber - 1 : 0;
757 return linkifier.linkifyLocation(url, lineNumber - 1, columnNumber, "timeline-details");
761 * @param {!ConsoleAgent.CallFrame} callFrame
763 function linkifyCallFrame(callFrame)
765 return linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
771 function linkifyTopCallFrame()
773 if (record.stackTrace)
774 return linkifyCallFrame(record.stackTrace[0]);
775 if (record.callSiteStackTrace)
776 return linkifyCallFrame(record.callSiteStackTrace[0]);
783 function linkifyScriptLocation()
785 return record.scriptName ? linkifyLocation(record.scriptName, record.scriptLine, 0) : null;
791 * @extends {WebInspector.Object}
792 * @param {string} name
793 * @param {string} title
794 * @param {number} overviewStripGroupIndex
795 * @param {string} borderColor
796 * @param {string} backgroundColor
797 * @param {string} fillColorStop0
798 * @param {string} fillColorStop1
800 WebInspector.TimelineCategory = function(name, title, overviewStripGroupIndex, borderColor, backgroundColor, fillColorStop0, fillColorStop1)
804 this.overviewStripGroupIndex = overviewStripGroupIndex;
805 this.borderColor = borderColor;
806 this.backgroundColor = backgroundColor;
807 this.fillColorStop0 = fillColorStop0;
808 this.fillColorStop1 = fillColorStop1;
812 WebInspector.TimelineCategory.Events = {
813 VisibilityChanged: "VisibilityChanged"
816 WebInspector.TimelineCategory.prototype = {
827 this._hidden = hidden;
828 this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged, this);
831 __proto__: WebInspector.Object.prototype
836 * @param {string} title
838 WebInspector.TimelinePopupContentHelper = function(title)
840 this._contentTable = document.createElement("table");
841 var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title");
842 titleCell.colSpan = 2;
843 var titleRow = document.createElement("tr");
844 titleRow.appendChild(titleCell);
845 this._contentTable.appendChild(titleRow);
848 WebInspector.TimelinePopupContentHelper.prototype = {
852 contentTable: function()
854 return this._contentTable;
858 * @param {string=} styleName
860 _createCell: function(content, styleName)
862 var text = document.createElement("label");
863 text.appendChild(document.createTextNode(content));
864 var cell = document.createElement("td");
865 cell.className = "timeline-details";
867 cell.className += " " + styleName;
868 cell.textContent = content;
873 * @param {string} title
874 * @param {string|number|boolean} content
876 appendTextRow: function(title, content)
878 var row = document.createElement("tr");
879 row.appendChild(this._createCell(title, "timeline-details-row-title"));
880 row.appendChild(this._createCell(content, "timeline-details-row-data"));
881 this._contentTable.appendChild(row);
885 * @param {string} title
886 * @param {!Node|string} content
888 appendElementRow: function(title, content)
890 var row = document.createElement("tr");
891 var titleCell = this._createCell(title, "timeline-details-row-title");
892 row.appendChild(titleCell);
893 var cell = document.createElement("td");
894 cell.className = "details";
895 if (content instanceof Node)
896 cell.appendChild(content);
898 cell.createTextChild(content || "");
899 row.appendChild(cell);
900 this._contentTable.appendChild(row);
906 * @param {!WebInspector.Linkifier} linkifier
907 * @param {boolean} monospaceValues
909 WebInspector.TimelineDetailsContentHelper = function(linkifier, monospaceValues)
911 this._linkifier = linkifier;
912 this.element = document.createElement("div");
913 this.element.className = "timeline-details-view-block";
914 this._monospaceValues = monospaceValues;
917 WebInspector.TimelineDetailsContentHelper.prototype = {
919 * @param {string} title
920 * @param {string|number|boolean} value
922 appendTextRow: function(title, value)
924 var rowElement = this.element.createChild("div", "timeline-details-view-row");
925 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
926 rowElement.createChild("span", "timeline-details-view-row-value" + (this._monospaceValues ? " monospace" : "")).textContent = value;
930 * @param {string} title
931 * @param {!Node|string} content
933 appendElementRow: function(title, content)
935 var rowElement = this.element.createChild("div", "timeline-details-view-row");
936 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
937 var valueElement = rowElement.createChild("span", "timeline-details-view-row-details" + (this._monospaceValues ? " monospace" : ""));
938 if (content instanceof Node)
939 valueElement.appendChild(content);
941 valueElement.createTextChild(content || "");
945 * @param {string} title
946 * @param {string} url
947 * @param {number} line
949 appendLocationRow: function(title, url, line)
951 this.appendElementRow(title, this._linkifier.linkifyLocation(url, line - 1) || "");
955 * @param {string} title
956 * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
958 appendStackTrace: function(title, stackTrace)
960 var rowElement = this.element.createChild("div", "timeline-details-view-row");
961 rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
962 var stackTraceElement = rowElement.createChild("div", "timeline-details-view-row-stack-trace monospace");
964 for (var i = 0; i < stackTrace.length; ++i) {
965 var stackFrame = stackTrace[i];
966 var row = stackTraceElement.createChild("div");
967 row.createTextChild(stackFrame.functionName || WebInspector.UIString("(anonymous function)"));
968 row.createTextChild(" @ ");
969 var urlElement = this._linkifier.linkifyLocation(stackFrame.url, stackFrame.lineNumber - 1);
970 row.appendChild(urlElement);