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      * @param {!WebInspector.Linkifier} linkifier
     87      * @param {boolean} loadedFromFile
     88      * @return {?Node}
     89      */
     90     buildDetailsNode: function(record, linkifier, loadedFromFile)
     91     {
     92         return WebInspector.TimelineUIUtilsImpl.buildDetailsNode(record, linkifier, loadedFromFile);
     93     },
     94 
     95     /**
     96      * @param {!WebInspector.TimelineModel.Record} record
     97      * @param {!WebInspector.TimelineModel} model
     98      * @param {!WebInspector.Linkifier} linkifier
     99      * @param {function(!DocumentFragment)} callback
    100      * @param {boolean} loadedFromFile
    101      */
    102     generateDetailsContent: function(record, model, linkifier, callback, loadedFromFile)
    103     {
    104         WebInspector.TimelineUIUtilsImpl.generateDetailsContent(record, model, linkifier, callback, loadedFromFile);
    105     },
    106 
    107     /**
    108      * @return {!Element}
    109      */
    110     createBeginFrameDivider: function()
    111     {
    112         return this.createEventDivider(WebInspector.TimelineModel.RecordType.BeginFrame);
    113     },
    114 
    115     /**
    116      * @param {string} recordType
    117      * @param {string=} title
    118      * @return {!Element}
    119      */
    120     createEventDivider: function(recordType, title)
    121     {
    122         return WebInspector.TimelineUIUtilsImpl._createEventDivider(recordType, title);
    123     },
    124 
    125     /**
    126      * @param {!WebInspector.TimelineModel.Record} record
    127      * @param {!RegExp} regExp
    128      * @return {boolean}
    129      */
    130     testContentMatching: function(record, regExp)
    131     {
    132         var tokens = [WebInspector.TimelineUIUtilsImpl.recordTitle(record)];
    133         var data = record.data();
    134         for (var key in data)
    135             tokens.push(data[key])
    136         return regExp.test(tokens.join("|"));
    137     },
    138 
    139     __proto__: WebInspector.TimelineUIUtils.prototype
    140 }
    141 
    142 
    143 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes = {};
    144 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Layout] = 1;
    145 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Paint] = 1;
    146 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.Rasterize] = 1;
    147 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.DecodeImage] = 1;
    148 WebInspector.TimelineUIUtilsImpl._coalescableRecordTypes[WebInspector.TimelineModel.RecordType.ResizeImage] = 1;
    149 
    150 
    151 /**
    152  * @param {!WebInspector.TimelineModel.Record} record
    153  * @return {string}
    154  */
    155 WebInspector.TimelineUIUtilsImpl.recordTitle = function(record)
    156 {
    157     var recordData = record.data();
    158     if (record.type() === WebInspector.TimelineModel.RecordType.TimeStamp)
    159         return recordData["message"];
    160     if (record.type() === WebInspector.TimelineModel.RecordType.JSFrame)
    161         return recordData["functionName"];
    162     if (WebInspector.TimelineUIUtilsImpl.isEventDivider(record)) {
    163         var startTime = Number.millisToString(record.startTime() - record._model.minimumRecordTime());
    164         return WebInspector.UIString("%s at %s", WebInspector.TimelineUIUtils.recordStyle(record).title, startTime, true);
    165     }
    166     return WebInspector.TimelineUIUtils.recordStyle(record).title;
    167 }
    168 
    169 /**
    170  * @param {!WebInspector.TimelineModel.Record} record
    171  * @return {boolean}
    172  */
    173 WebInspector.TimelineUIUtilsImpl.isEventDivider = function(record)
    174 {
    175     var recordTypes = WebInspector.TimelineModel.RecordType;
    176     if (record.type() === recordTypes.TimeStamp)
    177         return true;
    178     if (record.type() === recordTypes.MarkFirstPaint)
    179         return true;
    180     if (record.type() === recordTypes.MarkDOMContent || record.type() === recordTypes.MarkLoad)
    181         return record.data()["isMainFrame"];
    182     return false;
    183 }
    184 
    185 /**
    186  * @param {!WebInspector.TimelineModel.Record} record
    187  * @param {!WebInspector.Linkifier} linkifier
    188  * @param {boolean} loadedFromFile
    189  * @return {?Node}
    190  */
    191 WebInspector.TimelineUIUtilsImpl.buildDetailsNode = function(record, linkifier, loadedFromFile)
    192 {
    193     var details;
    194     var detailsText;
    195     var recordData = record.data();
    196     switch (record.type()) {
    197     case WebInspector.TimelineModel.RecordType.GCEvent:
    198         detailsText = WebInspector.UIString("%s collected", Number.bytesToString(recordData["usedHeapSizeDelta"]));
    199         break;
    200     case WebInspector.TimelineModel.RecordType.TimerFire:
    201         detailsText = recordData["timerId"];
    202         break;
    203     case WebInspector.TimelineModel.RecordType.FunctionCall:
    204         details = linkifyLocation(recordData["scriptId"], recordData["scriptName"], recordData["scriptLine"], 0);
    205         break;
    206     case WebInspector.TimelineModel.RecordType.FireAnimationFrame:
    207         detailsText = recordData["id"];
    208         break;
    209     case WebInspector.TimelineModel.RecordType.EventDispatch:
    210         detailsText = recordData ? recordData["type"] : null;
    211         break;
    212     case WebInspector.TimelineModel.RecordType.Paint:
    213         var width = WebInspector.TimelineUIUtils._quadWidth(recordData.clip);
    214         var height = WebInspector.TimelineUIUtils._quadHeight(recordData.clip);
    215         if (width && height)
    216             detailsText = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height);
    217         break;
    218     case WebInspector.TimelineModel.RecordType.TimerInstall:
    219     case WebInspector.TimelineModel.RecordType.TimerRemove:
    220         details = linkifyTopCallFrame();
    221         detailsText = recordData["timerId"];
    222         break;
    223     case WebInspector.TimelineModel.RecordType.RequestAnimationFrame:
    224     case WebInspector.TimelineModel.RecordType.CancelAnimationFrame:
    225         details = linkifyTopCallFrame();
    226         detailsText = recordData["id"];
    227         break;
    228     case WebInspector.TimelineModel.RecordType.ParseHTML:
    229     case WebInspector.TimelineModel.RecordType.RecalculateStyles:
    230         details = linkifyTopCallFrame();
    231         break;
    232     case WebInspector.TimelineModel.RecordType.EvaluateScript:
    233         var url = recordData["url"];
    234         if (url)
    235             details = linkifyLocation("", url, recordData["lineNumber"], 0);
    236         break;
    237     case WebInspector.TimelineModel.RecordType.XHRReadyStateChange:
    238     case WebInspector.TimelineModel.RecordType.XHRLoad:
    239     case WebInspector.TimelineModel.RecordType.ResourceSendRequest:
    240     case WebInspector.TimelineModel.RecordType.DecodeImage:
    241     case WebInspector.TimelineModel.RecordType.ResizeImage:
    242         var url = recordData["url"];
    243         if (url)
    244             detailsText = WebInspector.displayNameForURL(url);
    245         break;
    246     case WebInspector.TimelineModel.RecordType.ResourceReceivedData:
    247     case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse:
    248     case WebInspector.TimelineModel.RecordType.ResourceFinish:
    249         var initiator = record.initiator();
    250         if (initiator) {
    251             var url = initiator.data()["url"];
    252             if (url)
    253                 detailsText = WebInspector.displayNameForURL(url);
    254         }
    255         break;
    256     case WebInspector.TimelineModel.RecordType.ConsoleTime:
    257         detailsText = recordData["message"];
    258         break;
    259     case WebInspector.TimelineModel.RecordType.EmbedderCallback:
    260         detailsText = recordData["callbackName"];
    261         break;
    262     default:
    263         details = linkifyTopCallFrame();
    264         break;
    265     }
    266 
    267     if (!details && detailsText)
    268         details = document.createTextNode(detailsText);
    269     return details;
    270 
    271     /**
    272      * @param {string} scriptId
    273      * @param {string} url
    274      * @param {number} lineNumber
    275      * @param {number=} columnNumber
    276      */
    277     function linkifyLocation(scriptId, url, lineNumber, columnNumber)
    278     {
    279         if (!loadedFromFile && scriptId !== "0") {
    280             var location = new WebInspector.DebuggerModel.Location(
    281                 record.target(),
    282                 scriptId,
    283                 lineNumber - 1,
    284                 (columnNumber || 1) - 1);
    285             return linkifier.linkifyRawLocation(location, "timeline-details");
    286         }
    287 
    288         if (!url)
    289             return null;
    290 
    291         // FIXME(62725): stack trace line/column numbers are one-based.
    292         columnNumber = columnNumber ? columnNumber - 1 : 0;
    293         return linkifier.linkifyLocation(record.target(), url, lineNumber - 1, columnNumber, "timeline-details");
    294     }
    295 
    296     /**
    297      * @param {!ConsoleAgent.CallFrame} callFrame
    298      */
    299     function linkifyCallFrame(callFrame)
    300     {
    301         return linkifyLocation(callFrame.scriptId, callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
    302     }
    303 
    304     /**
    305      * @return {?Element}
    306      */
    307     function linkifyTopCallFrame()
    308     {
    309         if (record.stackTrace())
    310             return linkifyCallFrame(record.stackTrace()[0]);
    311         if (record.callSiteStackTrace())
    312             return linkifyCallFrame(record.callSiteStackTrace()[0]);
    313         return null;
    314     }
    315 }
    316 
    317 /**
    318  * @param {string=} recordType
    319  * @return {boolean}
    320  */
    321 WebInspector.TimelineUIUtilsImpl._needsPreviewElement = function(recordType)
    322 {
    323     if (!recordType)
    324         return false;
    325     const recordTypes = WebInspector.TimelineModel.RecordType;
    326     switch (recordType) {
    327     case recordTypes.ResourceSendRequest:
    328     case recordTypes.ResourceReceiveResponse:
    329     case recordTypes.ResourceReceivedData:
    330     case recordTypes.ResourceFinish:
    331         return true;
    332     default:
    333         return false;
    334     }
    335 }
    336 
    337 /**
    338  * @param {!WebInspector.TimelineModel.Record} record
    339  * @param {!WebInspector.TimelineModel} model
    340  * @param {!WebInspector.Linkifier} linkifier
    341  * @param {function(!DocumentFragment)} callback
    342  * @param {boolean} loadedFromFile
    343  */
    344 WebInspector.TimelineUIUtilsImpl.generateDetailsContent = function(record, model, linkifier, callback, loadedFromFile)
    345 {
    346     var imageElement = /** @type {?Element} */ (record.getUserObject("TimelineUIUtils::preview-element") || null);
    347     var relatedNode = null;
    348     var recordData = record.data();
    349     var barrier = new CallbackBarrier();
    350     if (!imageElement && WebInspector.TimelineUIUtilsImpl._needsPreviewElement(record.type()))
    351         WebInspector.DOMPresentationUtils.buildImagePreviewContents(record.target(), recordData["url"], false, barrier.createCallback(saveImage));
    352     if (recordData["backendNodeId"])
    353         record.target().domModel.pushNodesByBackendIdsToFrontend([recordData["backendNodeId"]], barrier.createCallback(setRelatedNode));
    354     barrier.callWhenDone(callbackWrapper);
    355 
    356     /**
    357      * @param {!Element=} element
    358      */
    359     function saveImage(element)
    360     {
    361         imageElement = element || null;
    362         record.setUserObject("TimelineUIUtils::preview-element", element);
    363     }
    364 
    365     /**
    366      * @param {?Array.<!DOMAgent.NodeId>} nodeIds
    367      */
    368     function setRelatedNode(nodeIds)
    369     {
    370         if (nodeIds)
    371             relatedNode = record.target().domModel.nodeForId(nodeIds[0]);
    372     }
    373 
    374     function callbackWrapper()
    375     {
    376         callback(WebInspector.TimelineUIUtilsImpl._generateDetailsContentSynchronously(record, model, linkifier, imageElement, relatedNode, loadedFromFile));
    377     }
    378 }
    379 
    380 /**
    381  * @param {!WebInspector.TimelineModel.Record} record
    382  * @param {!WebInspector.TimelineModel} model
    383  * @param {!WebInspector.Linkifier} linkifier
    384  * @param {?Element} imagePreviewElement
    385  * @param {?WebInspector.DOMNode} relatedNode
    386  * @param {boolean} loadedFromFile
    387  * @return {!DocumentFragment}
    388  */
    389 WebInspector.TimelineUIUtilsImpl._generateDetailsContentSynchronously = function(record, model, linkifier, imagePreviewElement, relatedNode, loadedFromFile)
    390 {
    391     var fragment = document.createDocumentFragment();
    392     if (record.children().length)
    393         fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats(), record.category(), record.selfTime()));
    394     else
    395         fragment.appendChild(WebInspector.TimelineUIUtils.generatePieChart(record.aggregatedStats()));
    396 
    397     const recordTypes = WebInspector.TimelineModel.RecordType;
    398 
    399     // The messages may vary per record.type();
    400     var callSiteStackTraceLabel;
    401     var callStackLabel;
    402     var relatedNodeLabel;
    403 
    404     var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.target(), linkifier, true);
    405     contentHelper.appendTextRow(WebInspector.UIString("Self Time"), Number.millisToString(record.selfTime(), true));
    406     contentHelper.appendTextRow(WebInspector.UIString("Start Time"), Number.millisToString(record.startTime() - model.minimumRecordTime()));
    407     var recordData = record.data();
    408 
    409     switch (record.type()) {
    410         case recordTypes.GCEvent:
    411             contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(recordData["usedHeapSizeDelta"]));
    412             break;
    413         case recordTypes.TimerFire:
    414             callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
    415             // Fall-through intended.
    416 
    417         case recordTypes.TimerInstall:
    418         case recordTypes.TimerRemove:
    419             contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), recordData["timerId"]);
    420             if (record.type() === recordTypes.TimerInstall) {
    421                 contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.millisToString(recordData["timeout"]));
    422                 contentHelper.appendTextRow(WebInspector.UIString("Repeats"), !recordData["singleShot"]);
    423             }
    424             break;
    425         case recordTypes.FireAnimationFrame:
    426             callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
    427             contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), recordData["id"]);
    428             break;
    429         case recordTypes.FunctionCall:
    430             if (recordData["scriptName"])
    431                 contentHelper.appendLocationRow(WebInspector.UIString("Location"), recordData["scriptName"], recordData["scriptLine"]);
    432             break;
    433         case recordTypes.ResourceSendRequest:
    434         case recordTypes.ResourceReceiveResponse:
    435         case recordTypes.ResourceReceivedData:
    436         case recordTypes.ResourceFinish:
    437             var url;
    438             if (record.type() === recordTypes.ResourceSendRequest)
    439                 url = recordData["url"];
    440             else if (record.initiator())
    441                 url = record.initiator().data()["url"];
    442             if (url)
    443                 contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(url));
    444             if (imagePreviewElement)
    445                 contentHelper.appendElementRow(WebInspector.UIString("Preview"), imagePreviewElement);
    446             if (recordData["requestMethod"])
    447                 contentHelper.appendTextRow(WebInspector.UIString("Request Method"), recordData["requestMethod"]);
    448             if (typeof recordData["statusCode"] === "number")
    449                 contentHelper.appendTextRow(WebInspector.UIString("Status Code"), recordData["statusCode"]);
    450             if (recordData["mimeType"])
    451                 contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), recordData["mimeType"]);
    452             if (recordData["encodedDataLength"])
    453                 contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", recordData["encodedDataLength"]));
    454             break;
    455         case recordTypes.EvaluateScript:
    456             var url = recordData["url"];
    457             if (url)
    458                 contentHelper.appendLocationRow(WebInspector.UIString("Script"), url, recordData["lineNumber"]);
    459             break;
    460         case recordTypes.Paint:
    461             var clip = recordData["clip"];
    462             contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1]));
    463             var clipWidth = WebInspector.TimelineUIUtils._quadWidth(clip);
    464             var clipHeight = WebInspector.TimelineUIUtils._quadHeight(clip);
    465             contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d  %d", clipWidth, clipHeight));
    466             // Fall-through intended.
    467 
    468         case recordTypes.PaintSetup:
    469         case recordTypes.Rasterize:
    470         case recordTypes.ScrollLayer:
    471             relatedNodeLabel = WebInspector.UIString("Layer root");
    472             break;
    473         case recordTypes.DecodeImage:
    474         case recordTypes.ResizeImage:
    475             relatedNodeLabel = WebInspector.UIString("Image element");
    476             var url = recordData["url"];
    477             if (url)
    478                 contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(url));
    479             break;
    480         case recordTypes.RecalculateStyles: // We don't want to see default details.
    481             if (recordData["elementCount"])
    482                 contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), recordData["elementCount"]);
    483             callStackLabel = WebInspector.UIString("Styles recalculation forced");
    484             break;
    485         case recordTypes.Layout:
    486             if (recordData["dirtyObjects"])
    487                 contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), recordData["dirtyObjects"]);
    488             if (recordData["totalObjects"])
    489                 contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), recordData["totalObjects"]);
    490             if (typeof recordData["partialLayout"] === "boolean") {
    491                 contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
    492                    recordData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
    493             }
    494             callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
    495             callStackLabel = WebInspector.UIString("Layout forced");
    496             relatedNodeLabel = WebInspector.UIString("Layout root");
    497             break;
    498         case recordTypes.ConsoleTime:
    499             contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
    500             break;
    501         case recordTypes.WebSocketCreate:
    502         case recordTypes.WebSocketSendHandshakeRequest:
    503         case recordTypes.WebSocketReceiveHandshakeResponse:
    504         case recordTypes.WebSocketDestroy:
    505             var initiatorData = record.initiator() ? record.initiator().data() : recordData;
    506             if (typeof initiatorData["webSocketURL"] !== "undefined")
    507                 contentHelper.appendTextRow(WebInspector.UIString("URL"), initiatorData["webSocketURL"]);
    508             if (typeof initiatorData["webSocketProtocol"] !== "undefined")
    509                 contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), initiatorData["webSocketProtocol"]);
    510             if (typeof recordData["message"] !== "undefined")
    511                 contentHelper.appendTextRow(WebInspector.UIString("Message"), recordData["message"]);
    512             break;
    513         case recordTypes.EmbedderCallback:
    514             contentHelper.appendTextRow(WebInspector.UIString("Callback Function"), recordData["callbackName"]);
    515             break;
    516         default:
    517             var detailsNode = WebInspector.TimelineUIUtilsImpl.buildDetailsNode(record, linkifier, loadedFromFile);
    518             if (detailsNode)
    519                 contentHelper.appendElementRow(WebInspector.UIString("Details"), detailsNode);
    520             break;
    521     }
    522 
    523     if (relatedNode)
    524         contentHelper.appendElementRow(relatedNodeLabel || WebInspector.UIString("Related node"), WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode));
    525 
    526     if (recordData["scriptName"] && record.type() !== recordTypes.FunctionCall)
    527         contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), recordData["scriptName"], recordData["scriptLine"]);
    528     var callSiteStackTrace = record.callSiteStackTrace();
    529     if (callSiteStackTrace)
    530         contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), callSiteStackTrace);
    531     var recordStackTrace = record.stackTrace();
    532     if (recordStackTrace)
    533         contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), recordStackTrace);
    534 
    535     if (record.warnings()) {
    536         var ul = document.createElement("ul");
    537         for (var i = 0; i < record.warnings().length; ++i)
    538             ul.createChild("li").textContent = record.warnings()[i];
    539         contentHelper.appendElementRow(WebInspector.UIString("Warning"), ul);
    540     }
    541     fragment.appendChild(contentHelper.element);
    542     return fragment;
    543 }
    544 
    545 /**
    546  * @param {string} recordType
    547  * @param {string=} title
    548  * @return {!Element}
    549  */
    550 WebInspector.TimelineUIUtilsImpl._createEventDivider = function(recordType, title)
    551 {
    552     var eventDivider = document.createElement("div");
    553     eventDivider.className = "resources-event-divider";
    554     var recordTypes = WebInspector.TimelineModel.RecordType;
    555 
    556     if (recordType === recordTypes.MarkDOMContent)
    557         eventDivider.className += " resources-blue-divider";
    558     else if (recordType === recordTypes.MarkLoad)
    559         eventDivider.className += " resources-red-divider";
    560     else if (recordType === recordTypes.MarkFirstPaint)
    561         eventDivider.className += " resources-green-divider";
    562     else if (recordType === recordTypes.TimeStamp)
    563         eventDivider.className += " resources-orange-divider";
    564     else if (recordType === recordTypes.BeginFrame)
    565         eventDivider.className += " timeline-frame-divider";
    566 
    567     if (title)
    568         eventDivider.title = title;
    569 
    570     return eventDivider;
    571 }
    572