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