Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelineUIUtilsImpl.js
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @constructor
7  * @extends {WebInspector.TimelineUIUtils}
8  */
9 WebInspector.TimelineUIUtilsImpl = function()
10 {
11     WebInspector.TimelineUIUtils.call(this);
12 }
13
14 WebInspector.TimelineUIUtilsImpl.prototype = {
15     /**
16      * @param {!WebInspector.TimelineModel.Record} record
17      * @return {boolean}
18      */
19     isBeginFrame: function(record)
20     {
21         return record.type() === WebInspector.TimelineModel.RecordType.BeginFrame;
22     },
23     /**
24      * @param {!WebInspector.TimelineModel.Record} record
25      * @return {boolean}
26      */
27     isProgram: function(record)
28     {
29         return record.type() === WebInspector.TimelineModel.RecordType.Program;
30     },
31     /**
32      * @param {string} recordType
33      * @return {boolean}
34      */
35     isCoalescable: function(recordType)
36     {
37         return !!WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[recordType];
38     },
39
40     /**
41      * @param {!WebInspector.TimelineModel.Record} record
42      * @return {boolean}
43      */
44     isEventDivider: function(record)
45     {
46         return WebInspector.TimelineUIUtilsImpl.isEventDivider(record);
47     },
48
49     /**
50      * @param {!WebInspector.TimelineModel.Record} record
51      * @return {?Object}
52      */
53     countersForRecord: function(record)
54     {
55         return record.type() === WebInspector.TimelineModel.RecordType.UpdateCounters ? record.data() : null;
56     },
57
58     /**
59      * @param {!WebInspector.TimelineModel.Record} record
60      * @return {?Object}
61      */
62     highlightQuadForRecord: function(record)
63     {
64         var recordTypes = WebInspector.TimelineModel.RecordType;
65         switch(record.type()) {
66         case recordTypes.Layout:
67             return record.data().root;
68         case recordTypes.Paint:
69             return record.data().clip;
70         default:
71             return null;
72         }
73     },
74
75     /**
76      * @param {!WebInspector.TimelineModel.Record} record
77      * @return {string}
78      */
79     titleForRecord: function(record)
80     {
81         return WebInspector.TimelineUIUtilsImpl._recordTitle(record);
82     },
83
84     /**
85      * @param {!WebInspector.TimelineModel.Record} record
86      * @return {!WebInspector.TimelineCategory}
87      */
88     categoryForRecord: function(record)
89     {
90         return WebInspector.TimelineUIUtilsImpl.recordStyle(record).category;
91     },
92
93     /**
94      * @param {!WebInspector.TimelineModel.Record} record
95      * @param {!WebInspector.Linkifier} linkifier
96      * @return {?Node}
97      */
98     buildDetailsNode: function(record, linkifier)
99     {
100         return WebInspector.TimelineUIUtilsImpl.buildDetailsNode(record, linkifier);
101     },
102
103     /**
104      * @param {!WebInspector.TimelineModel.Record} record
105      * @param {!WebInspector.TimelineModel} model
106      * @param {!WebInspector.Linkifier} linkifier
107      * @param {function(!DocumentFragment)} callback
108      */
109     generateDetailsContent: function(record, model, linkifier, callback)
110     {
111         WebInspector.TimelineUIUtilsImpl.generateDetailsContent(record, model, linkifier, callback);
112     },
113
114     /**
115      * @return {!Element}
116      */
117     createBeginFrameDivider: function()
118     {
119         return this.createEventDivider(WebInspector.TimelineModel.RecordType.BeginFrame);
120     },
121
122     /**
123      * @param {string} recordType
124      * @param {string=} title
125      * @return {!Element}
126      */
127     createEventDivider: function(recordType, title)
128     {
129         return WebInspector.TimelineUIUtilsImpl._createEventDivider(recordType, title);
130     },
131
132     /**
133      * @param {!WebInspector.TimelineModel.Record} record
134      * @param {!RegExp} regExp
135      * @return {boolean}
136      */
137     testContentMatching: function(record, regExp)
138     {
139         var tokens = [WebInspector.TimelineUIUtilsImpl._recordTitle(record)];
140         var data = record.data();
141         for (var key in data)
142             tokens.push(data[key])
143         return regExp.test(tokens.join("|"));
144     },
145
146     /**
147      * @param {!Object} total
148      * @param {!WebInspector.TimelineModel.Record} record
149      */
150     aggregateTimeForRecord: function(total, record)
151     {
152         WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord(total, record);
153     },
154
155     /**
156      * @return {!WebInspector.TimelineModel.Filter}
157      */
158     hiddenRecordsFilter: function()
159     {
160         var recordTypes = WebInspector.TimelineModel.RecordType;
161         var hiddenRecords = [
162             recordTypes.ActivateLayerTree,
163             recordTypes.BeginFrame,
164             recordTypes.DrawFrame,
165             recordTypes.GPUTask,
166             recordTypes.InvalidateLayout,
167             recordTypes.MarkDOMContent,
168             recordTypes.MarkFirstPaint,
169             recordTypes.MarkLoad,
170             recordTypes.RequestMainThreadFrame,
171             recordTypes.ScheduleStyleRecalculation,
172             recordTypes.UpdateCounters
173         ];
174         return new WebInspector.TimelineRecordHiddenTypeFilter(hiddenRecords);
175     },
176
177     __proto__: WebInspector.TimelineUIUtils.prototype
178 }
179
180
181 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes = {};
182 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Layout] = 1;
183 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Paint] = 1;
184 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Rasterize] = 1;
185 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.DecodeImage] = 1;
186 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.ResizeImage] = 1;
187
188 /**
189  * @return {!Object.<string, !{title: string, category: !WebInspector.TimelineCategory}>}
190  */
191 WebInspector.TimelineUIUtils._initRecordStyles = function()
192 {
193     if (WebInspector.TimelineUIUtils._recordStylesMap)
194         return WebInspector.TimelineUIUtils._recordStylesMap;
195
196     var recordTypes = WebInspector.TimelineModel.RecordType;
197     var categories = WebInspector.TimelineUIUtils.categories();
198
199     var recordStyles = {};
200     recordStyles[recordTypes.Root] = { title: "#root", category: categories["loading"] };
201     recordStyles[recordTypes.Program] = { title: WebInspector.UIString("Other"), category: categories["other"] };
202     recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: categories["scripting"] };
203     recordStyles[recordTypes.BeginFrame] = { title: WebInspector.UIString("Frame Start"), category: categories["rendering"] };
204     recordStyles[recordTypes.ScheduleStyleRecalculation] = { title: WebInspector.UIString("Schedule Style Recalculation"), category: categories["rendering"] };
205     recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: categories["rendering"] };
206     recordStyles[recordTypes.InvalidateLayout] = { title: WebInspector.UIString("Invalidate Layout"), category: categories["rendering"] };
207     recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: categories["rendering"] };
208     recordStyles[recordTypes.UpdateLayerTree] = { title: WebInspector.UIString("Update Layer Tree"), category: categories["rendering"] };
209     recordStyles[recordTypes.PaintSetup] = { title: WebInspector.UIString("Paint Setup"), category: categories["painting"] };
210     recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
211     recordStyles[recordTypes.Rasterize] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
212     recordStyles[recordTypes.ScrollLayer] = { title: WebInspector.UIString("Scroll"), category: categories["rendering"] };
213     recordStyles[recordTypes.DecodeImage] = { title: WebInspector.UIString("Image Decode"), category: categories["painting"] };
214     recordStyles[recordTypes.ResizeImage] = { title: WebInspector.UIString("Image Resize"), category: categories["painting"] };
215     recordStyles[recordTypes.CompositeLayers] = { title: WebInspector.UIString("Composite Layers"), category: categories["painting"] };
216     recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse HTML"), category: categories["loading"] };
217     recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: categories["scripting"] };
218     recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: categories["scripting"] };
219     recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: categories["scripting"] };
220     recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: categories["scripting"] };
221     recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: categories["scripting"] };
222     recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: categories["scripting"] };
223     recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: categories["loading"] };
224     recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: categories["loading"] };
225     recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: categories["loading"] };
226     recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: categories["scripting"] };
227     recordStyles[recordTypes.ResourceReceivedData] = { title: WebInspector.UIString("Receive Data"), category: categories["loading"] };
228     recordStyles[recordTypes.GCEvent] = { title: WebInspector.UIString("GC Event"), category: categories["scripting"] };
229     recordStyles[recordTypes.JSFrame] = { title: WebInspector.UIString("JS Frame"), category: categories["scripting"] };
230     recordStyles[recordTypes.MarkDOMContent] = { title: WebInspector.UIString("DOMContentLoaded event"), category: categories["scripting"] };
231     recordStyles[recordTypes.MarkLoad] = { title: WebInspector.UIString("Load event"), category: categories["scripting"] };
232     recordStyles[recordTypes.MarkFirstPaint] = { title: WebInspector.UIString("First paint"), category: categories["painting"] };
233     recordStyles[recordTypes.TimeStamp] = { title: WebInspector.UIString("Stamp"), category: categories["scripting"] };
234     recordStyles[recordTypes.ConsoleTime] = { title: WebInspector.UIString("Console Time"), category: categories["scripting"] };
235     recordStyles[recordTypes.RequestAnimationFrame] = { title: WebInspector.UIString("Request Animation Frame"), category: categories["scripting"] };
236     recordStyles[recordTypes.CancelAnimationFrame] = { title: WebInspector.UIString("Cancel Animation Frame"), category: categories["scripting"] };
237     recordStyles[recordTypes.FireAnimationFrame] = { title: WebInspector.UIString("Animation Frame Fired"), category: categories["scripting"] };
238     recordStyles[recordTypes.WebSocketCreate] = { title: WebInspector.UIString("Create WebSocket"), category: categories["scripting"] };
239     recordStyles[recordTypes.WebSocketSendHandshakeRequest] = { title: WebInspector.UIString("Send WebSocket Handshake"), category: categories["scripting"] };
240     recordStyles[recordTypes.WebSocketReceiveHandshakeResponse] = { title: WebInspector.UIString("Receive WebSocket Handshake"), category: categories["scripting"] };
241     recordStyles[recordTypes.WebSocketDestroy] = { title: WebInspector.UIString("Destroy WebSocket"), category: categories["scripting"] };
242     recordStyles[recordTypes.EmbedderCallback] = { title: WebInspector.UIString("Embedder Callback"), category: categories["scripting"] };
243
244     WebInspector.TimelineUIUtils._recordStylesMap = recordStyles;
245     return recordStyles;
246 }
247
248 /**
249  * @param {!WebInspector.TimelineModel.Record} record
250  * @return {!{title: string, category: !WebInspector.TimelineCategory}}
251  */
252 WebInspector.TimelineUIUtilsImpl.recordStyle = function(record)
253 {
254     var type = record.type();
255     var recordStyles = WebInspector.TimelineUIUtils._initRecordStyles();
256     var result = recordStyles[type];
257     if (!result) {
258         result = {
259             title: WebInspector.UIString("Unknown: %s", type),
260             category: WebInspector.TimelineUIUtils.categories()["other"]
261         };
262         recordStyles[type] = result;
263     }
264     return result;
265 }
266
267 /**
268  * @param {!WebInspector.TimelineModel.Record} record
269  * @return {string}
270  */
271 WebInspector.TimelineUIUtilsImpl._recordTitle = function(record)
272 {
273     var recordData = record.data();
274     if (record.type() === WebInspector.TimelineModel.RecordType.TimeStamp)
275         return recordData["message"];
276     if (record.type() === WebInspector.TimelineModel.RecordType.JSFrame)
277         return recordData["functionName"];
278     var title = WebInspector.TimelineUIUtilsImpl.recordStyle(record).title;
279     if (WebInspector.TimelineUIUtilsImpl.isEventDivider(record)) {
280         var startTime = Number.millisToString(record.startTime() - record._model.minimumRecordTime());
281         return WebInspector.UIString("%s at %s", title, startTime);
282     }
283     return title;
284 }
285
286 /**
287  * @param {!WebInspector.TimelineModel.Record} record
288  * @return {boolean}
289  */
290 WebInspector.TimelineUIUtilsImpl.isEventDivider = function(record)
291 {
292     var recordTypes = WebInspector.TimelineModel.RecordType;
293     if (record.type() === recordTypes.TimeStamp)
294         return true;
295     if (record.type() === recordTypes.MarkFirstPaint)
296         return true;
297     if (record.type() === recordTypes.MarkDOMContent || record.type() === recordTypes.MarkLoad)
298         return record.data()["isMainFrame"];
299     return false;
300 }
301
302 /**
303  * @param {!Object} total
304  * @param {!WebInspector.TimelineModel.Record} record
305  */
306 WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord = function(total, record)
307 {
308     var children = record.children();
309     for (var i = 0; i < children.length; ++i)
310         WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord(total, children[i]);
311     var categoryName = WebInspector.TimelineUIUtilsImpl.recordStyle(record).category.name;
312     total[categoryName] = (total[categoryName] || 0) + record.selfTime();
313 }
314
315 /**
316  * @param {!WebInspector.TimelineModel.Record} record
317  * @param {!WebInspector.Linkifier} linkifier
318  * @return {?Node}
319  */
320 WebInspector.TimelineUIUtilsImpl.buildDetailsNode = function(record, linkifier)
321 {
322     var details;
323     var detailsText;
324     var recordData = record.data();
325     switch (record.type()) {
326     case WebInspector.TimelineModel.RecordType.GCEvent:
327         detailsText = WebInspector.UIString("%s collected", Number.bytesToString(recordData["usedHeapSizeDelta"]));
328         break;
329     case WebInspector.TimelineModel.RecordType.TimerFire:
330         detailsText = recordData["timerId"];
331         break;
332     case WebInspector.TimelineModel.RecordType.FunctionCall:
333         details = linkifyLocation(recordData["scriptId"], recordData["scriptName"], recordData["scriptLine"], 0);
334         break;
335     case WebInspector.TimelineModel.RecordType.FireAnimationFrame:
336         detailsText = recordData["id"];
337         break;
338     case WebInspector.TimelineModel.RecordType.EventDispatch:
339         detailsText = recordData ? recordData["type"] : null;
340         break;
341     case WebInspector.TimelineModel.RecordType.Paint:
342         var width = WebInspector.TimelineUIUtils.quadWidth(recordData.clip);
343         var height = WebInspector.TimelineUIUtils.quadHeight(recordData.clip);
344         if (width && height)
345             detailsText = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height);
346         break;
347     case WebInspector.TimelineModel.RecordType.TimerInstall:
348     case WebInspector.TimelineModel.RecordType.TimerRemove:
349         details = linkifyTopCallFrame();
350         detailsText = recordData["timerId"];
351         break;
352     case WebInspector.TimelineModel.RecordType.RequestAnimationFrame:
353     case WebInspector.TimelineModel.RecordType.CancelAnimationFrame:
354         details = linkifyTopCallFrame();
355         detailsText = recordData["id"];
356         break;
357     case WebInspector.TimelineModel.RecordType.ParseHTML:
358     case WebInspector.TimelineModel.RecordType.RecalculateStyles:
359         details = linkifyTopCallFrame();
360         break;
361     case WebInspector.TimelineModel.RecordType.EvaluateScript:
362         var url = recordData["url"];
363         if (url)
364             details = linkifyLocation("", url, recordData["lineNumber"], 0);
365         break;
366     case WebInspector.TimelineModel.RecordType.XHRReadyStateChange:
367     case WebInspector.TimelineModel.RecordType.XHRLoad:
368     case WebInspector.TimelineModel.RecordType.ResourceSendRequest:
369     case WebInspector.TimelineModel.RecordType.DecodeImage:
370     case WebInspector.TimelineModel.RecordType.ResizeImage:
371         var url = recordData["url"];
372         if (url)
373             detailsText = WebInspector.displayNameForURL(url);
374         break;
375     case WebInspector.TimelineModel.RecordType.ResourceReceivedData:
376     case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse:
377     case WebInspector.TimelineModel.RecordType.ResourceFinish:
378         var initiator = record.initiator();
379         if (initiator) {
380             var url = initiator.data()["url"];
381             if (url)
382                 detailsText = WebInspector.displayNameForURL(url);
383         }
384         break;
385     case WebInspector.TimelineModel.RecordType.ConsoleTime:
386         detailsText = recordData["message"];
387         break;
388     case WebInspector.TimelineModel.RecordType.EmbedderCallback:
389         detailsText = recordData["callbackName"];
390         break;
391     default:
392         details = linkifyTopCallFrame();
393         break;
394     }
395
396     if (!details && detailsText)
397         details = document.createTextNode(detailsText);
398     return details;
399
400     /**
401      * @param {string} scriptId
402      * @param {string} url
403      * @param {number} lineNumber
404      * @param {number=} columnNumber
405      */
406     function linkifyLocation(scriptId, url, lineNumber, columnNumber)
407     {
408         if (!url)
409             return null;
410
411         // FIXME(62725): stack trace line/column numbers are one-based.
412         columnNumber = columnNumber ? columnNumber - 1 : 0;
413         return linkifier.linkifyScriptLocation(record.target(), scriptId, url, lineNumber - 1, columnNumber, "timeline-details");
414     }
415
416     /**
417      * @return {?Element}
418      */
419     function linkifyTopCallFrame()
420     {
421         if (record.stackTrace())
422             return linkifier.linkifyConsoleCallFrame(record.target(), record.stackTrace()[0], "timeline-details");
423         if (record.callSiteStackTrace())
424             return linkifier.linkifyConsoleCallFrame(record.target(), record.callSiteStackTrace()[0], "timeline-details");
425         return null;
426     }
427 }
428
429 /**
430  * @param {string=} recordType
431  * @return {boolean}
432  */
433 WebInspector.TimelineUIUtilsImpl._needsPreviewElement = function(recordType)
434 {
435     if (!recordType)
436         return false;
437     const recordTypes = WebInspector.TimelineModel.RecordType;
438     switch (recordType) {
439     case recordTypes.ResourceSendRequest:
440     case recordTypes.ResourceReceiveResponse:
441     case recordTypes.ResourceReceivedData:
442     case recordTypes.ResourceFinish:
443         return true;
444     default:
445         return false;
446     }
447 }
448
449 /**
450  * @param {!WebInspector.TimelineModel.Record} record
451  * @param {!WebInspector.TimelineModel} model
452  * @param {!WebInspector.Linkifier} linkifier
453  * @param {function(!DocumentFragment)} callback
454  */
455 WebInspector.TimelineUIUtilsImpl.generateDetailsContent = function(record, model, linkifier, callback)
456 {
457     var imageElement = /** @type {?Element} */ (record.getUserObject("TimelineUIUtils::preview-element") || null);
458     var relatedNode = null;
459     var recordData = record.data();
460     var barrier = new CallbackBarrier();
461     var target = record.target();
462     if (!imageElement && WebInspector.TimelineUIUtilsImpl._needsPreviewElement(record.type()) && target)
463         WebInspector.DOMPresentationUtils.buildImagePreviewContents(target, recordData["url"], false, barrier.createCallback(saveImage));
464     if (recordData["backendNodeId"] && target)
465         target.domModel.pushNodesByBackendIdsToFrontend([recordData["backendNodeId"]], barrier.createCallback(setRelatedNode));
466     barrier.callWhenDone(callbackWrapper);
467
468     /**
469      * @param {!Element=} element
470      */
471     function saveImage(element)
472     {
473         imageElement = element || null;
474         record.setUserObject("TimelineUIUtils::preview-element", element);
475     }
476
477     /**
478      * @param {?Array.<!DOMAgent.NodeId>} nodeIds
479      */
480     function setRelatedNode(nodeIds)
481     {
482         if (nodeIds && target)
483             relatedNode = target.domModel.nodeForId(nodeIds[0]);
484     }
485
486     function callbackWrapper()
487     {
488         callback(WebInspector.TimelineUIUtilsImpl._generateDetailsContentSynchronously(record, model, linkifier, imageElement, relatedNode));
489     }
490 }
491
492 /**
493  * @param {!WebInspector.TimelineModel.Record} record
494  * @param {!WebInspector.TimelineModel} model
495  * @param {!WebInspector.Linkifier} linkifier
496  * @param {?Element} imagePreviewElement
497  * @param {?WebInspector.DOMNode} relatedNode
498  * @return {!DocumentFragment}
499  */
500 WebInspector.TimelineUIUtilsImpl._generateDetailsContentSynchronously = function(record, model, linkifier, imagePreviewElement, relatedNode)
501 {
502     var fragment = document.createDocumentFragment();
503     var aggregatedStats = {};
504     WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord(aggregatedStats, record);
505     if (record.children().length)
506         fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(aggregatedStats, WebInspector.TimelineUIUtilsImpl.recordStyle(record).category, record.selfTime()));
507     else
508         fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(aggregatedStats));
509
510     const recordTypes = WebInspector.TimelineModel.RecordType;
511
512     // The messages may vary per record.type();
513     var callSiteStackTraceLabel;
514     var callStackLabel;
515     var relatedNodeLabel;
516
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();
521
522     switch (record.type()) {
523         case recordTypes.GCEvent:
524             contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(recordData["usedHeapSizeDelta"]));
525             break;
526         case recordTypes.TimerFire:
527             callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
528             // Fall-through intended.
529
530         case recordTypes.TimerInstall:
531         case recordTypes.TimerRemove:
532             contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), recordData["timerId"]);
533             if (record.type() === recordTypes.TimerInstall) {
534                 contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.millisToString(recordData["timeout"]));
535                 contentHelper.appendTextRow(WebInspector.UIString("Repeats"), !recordData["singleShot"]);
536             }
537             break;
538         case recordTypes.FireAnimationFrame:
539             callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
540             contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), recordData["id"]);
541             break;
542         case recordTypes.FunctionCall:
543             if (recordData["scriptName"])
544                 contentHelper.appendLocationRow(WebInspector.UIString("Location"), recordData["scriptName"], recordData["scriptLine"]);
545             break;
546         case recordTypes.ResourceSendRequest:
547         case recordTypes.ResourceReceiveResponse:
548         case recordTypes.ResourceReceivedData:
549         case recordTypes.ResourceFinish:
550             var url;
551             if (record.type() === recordTypes.ResourceSendRequest)
552                 url = recordData["url"];
553             else if (record.initiator())
554                 url = record.initiator().data()["url"];
555             if (url)
556                 contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(url));
557             if (imagePreviewElement)
558                 contentHelper.appendElementRow(WebInspector.UIString("Preview"), imagePreviewElement);
559             if (recordData["requestMethod"])
560                 contentHelper.appendTextRow(WebInspector.UIString("Request Method"), recordData["requestMethod"]);
561             if (typeof recordData["statusCode"] === "number")
562                 contentHelper.appendTextRow(WebInspector.UIString("Status Code"), recordData["statusCode"]);
563             if (recordData["mimeType"])
564                 contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), recordData["mimeType"]);
565             if (recordData["encodedDataLength"])
566                 contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", recordData["encodedDataLength"]));
567             break;
568         case recordTypes.EvaluateScript:
569             var url = recordData["url"];
570             if (url)
571                 contentHelper.appendLocationRow(WebInspector.UIString("Script"), url, recordData["lineNumber"]);
572             break;
573         case recordTypes.Paint:
574             var clip = recordData["clip"];
575             contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1]));
576             var clipWidth = WebInspector.TimelineUIUtils.quadWidth(clip);
577             var clipHeight = WebInspector.TimelineUIUtils.quadHeight(clip);
578             contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d × %d", clipWidth, clipHeight));
579             // Fall-through intended.
580
581         case recordTypes.PaintSetup:
582         case recordTypes.Rasterize:
583         case recordTypes.ScrollLayer:
584             relatedNodeLabel = WebInspector.UIString("Layer root");
585             break;
586         case recordTypes.DecodeImage:
587         case recordTypes.ResizeImage:
588             relatedNodeLabel = WebInspector.UIString("Image element");
589             var url = recordData["url"];
590             if (url)
591                 contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(url));
592             break;
593         case recordTypes.RecalculateStyles: // We don't want to see default details.
594             if (recordData["elementCount"])
595                 contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), recordData["elementCount"]);
596             callStackLabel = WebInspector.UIString("Styles recalculation forced");
597             break;
598         case recordTypes.Layout:
599             if (recordData["dirtyObjects"])
600                 contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), recordData["dirtyObjects"]);
601             if (recordData["totalObjects"])
602                 contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), recordData["totalObjects"]);
603             if (typeof recordData["partialLayout"] === "boolean") {
604                 contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
605                    recordData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
606             }
607             callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
608             callStackLabel = WebInspector.UIString("Layout forced");
609             relatedNodeLabel = WebInspector.UIString("Layout root");
610             break;
611         case recordTypes.ConsoleTime:
612             contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
613             break;
614         case recordTypes.WebSocketCreate:
615         case recordTypes.WebSocketSendHandshakeRequest:
616         case recordTypes.WebSocketReceiveHandshakeResponse:
617         case recordTypes.WebSocketDestroy:
618             var initiatorData = record.initiator() ? record.initiator().data() : recordData;
619             if (typeof initiatorData["webSocketURL"] !== "undefined")
620                 contentHelper.appendTextRow(WebInspector.UIString("URL"), initiatorData["webSocketURL"]);
621             if (typeof initiatorData["webSocketProtocol"] !== "undefined")
622                 contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), initiatorData["webSocketProtocol"]);
623             if (typeof recordData["message"] !== "undefined")
624                 contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
625             break;
626         case recordTypes.EmbedderCallback:
627             contentHelper.appendTextRow(WebInspector.UIString("Callback Function"), recordData["callbackName"]);
628             break;
629         default:
630             var detailsNode = WebInspector.TimelineUIUtilsImpl.buildDetailsNode(record, linkifier);
631             if (detailsNode)
632                 contentHelper.appendElementRow(WebInspector.UIString("Details"), detailsNode);
633             break;
634     }
635
636     if (relatedNode)
637         contentHelper.appendElementRow(relatedNodeLabel || WebInspector.UIString("Related node"), WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode));
638
639     if (recordData["scriptName"] && record.type() !== recordTypes.FunctionCall)
640         contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), recordData["scriptName"], recordData["scriptLine"]);
641     var callSiteStackTrace = record.callSiteStackTrace();
642     if (callSiteStackTrace)
643         contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), callSiteStackTrace);
644     var recordStackTrace = record.stackTrace();
645     if (recordStackTrace)
646         contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), recordStackTrace);
647
648     if (record.warnings()) {
649         var ul = document.createElement("ul");
650         for (var i = 0; i < record.warnings().length; ++i)
651             ul.createChild("li").textContent = record.warnings()[i];
652         contentHelper.appendElementRow(WebInspector.UIString("Warning"), ul);
653     }
654     fragment.appendChild(contentHelper.element);
655     return fragment;
656 }
657
658 /**
659  * @param {string} recordType
660  * @param {string=} title
661  * @return {!Element}
662  */
663 WebInspector.TimelineUIUtilsImpl._createEventDivider = function(recordType, title)
664 {
665     var eventDivider = document.createElement("div");
666     eventDivider.className = "resources-event-divider";
667     var recordTypes = WebInspector.TimelineModel.RecordType;
668
669     if (recordType === recordTypes.MarkDOMContent)
670         eventDivider.className += " resources-blue-divider";
671     else if (recordType === recordTypes.MarkLoad)
672         eventDivider.className += " resources-red-divider";
673     else if (recordType === recordTypes.MarkFirstPaint)
674         eventDivider.className += " resources-green-divider";
675     else if (recordType === recordTypes.TimeStamp)
676         eventDivider.className += " resources-orange-divider";
677     else if (recordType === recordTypes.BeginFrame)
678         eventDivider.className += " timeline-frame-divider";
679
680     if (title)
681         eventDivider.title = title;
682
683     return eventDivider;
684 }