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