Home | History | Annotate | Download | only in timeline
      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.TracingTimelineUIUtils = function()
     10 {
     11     WebInspector.TimelineUIUtils.call(this);
     12 }
     13 
     14 WebInspector.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  */
    148 WebInspector.TimelineRecordStyle = function(title, category)
    149 {
    150     this.title = title;
    151     this.category = category;
    152 }
    153 
    154 /**
    155  * @return {!Object.<string, !WebInspector.TimelineRecordStyle>}
    156  */
    157 WebInspector.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 
    217 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes = {};
    218 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.Layout] = 1;
    219 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.Paint] = 1;
    220 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.Rasterize] = 1;
    221 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.DecodeImage] = 1;
    222 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.ResizeImage] = 1;
    223 
    224 /**
    225  * @param {!WebInspector.TracingModel.Event} event
    226  * @return {!{title: string, category: !WebInspector.TimelineCategory}}
    227  */
    228 WebInspector.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  */
    237 WebInspector.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  */
    252 WebInspector.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  */
    271 WebInspector.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  */
    420 WebInspector.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  */
    466 WebInspector.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  */
    627 WebInspector.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  */
    663 WebInspector.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  */
    708 WebInspector.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