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.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("Timestamp"), 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     var title = WebInspector.TimelineUIUtilsImpl.recordStyle(record).title;
    275     if (record.type() === WebInspector.TimelineModel.RecordType.TimeStamp || record.type() === WebInspector.TimelineModel.RecordType.ConsoleTime)
    276         return WebInspector.UIString("%s: %s", title, recordData["message"]);
    277     if (record.type() === WebInspector.TimelineModel.RecordType.JSFrame)
    278         return recordData["functionName"];
    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 }
    685