Home | History | Annotate | Download | only in timeline
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  * Copyright (C) 2012 Intel Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /**
     33  * @constructor
     34  */
     35 WebInspector.TimelineUIUtils = function() { }
     36 
     37 WebInspector.TimelineUIUtils.prototype = {
     38     /**
     39      * @param {!WebInspector.TimelineModel.Record} record
     40      * @return {boolean}
     41      */
     42     isBeginFrame: function(record)
     43     {
     44         throw new Error("Not implemented.");
     45     },
     46     /**
     47      * @param {!WebInspector.TimelineModel.Record} record
     48      * @return {boolean}
     49      */
     50     isProgram: function(record)
     51     {
     52         throw new Error("Not implemented.");
     53     },
     54     /**
     55      * @param {string} recordType
     56      * @return {boolean}
     57      */
     58     isCoalescable: function(recordType)
     59     {
     60         throw new Error("Not implemented.");
     61     },
     62     /**
     63      * @param {!WebInspector.TimelineModel.Record} record
     64      * @return {boolean}
     65      */
     66     isEventDivider: function(record)
     67     {
     68         throw new Error("Not implemented.");
     69     },
     70     /**
     71      * @param {!WebInspector.TimelineModel.Record} record
     72      * @return {?Object}
     73      */
     74     countersForRecord: function(record)
     75     {
     76         throw new Error("Not implemented.");
     77     },
     78     /**
     79      * @param {!WebInspector.TimelineModel.Record} record
     80      * @return {?Object}
     81      */
     82     highlightQuadForRecord: function(record)
     83     {
     84         throw new Error("Not implemented.");
     85     },
     86     /**
     87      * @param {!WebInspector.TimelineModel.Record} record
     88      * @return {string}
     89      */
     90     titleForRecord: function(record)
     91     {
     92         throw new Error("Not implemented.");
     93     },
     94     /**
     95      * @param {!WebInspector.TimelineModel.Record} record
     96      * @param {!WebInspector.Linkifier} linkifier
     97      * @param {boolean} loadedFromFile
     98      * @return {?Node}
     99      */
    100     buildDetailsNode: function(record, linkifier, loadedFromFile)
    101     {
    102         throw new Error("Not implemented.");
    103     },
    104     /**
    105      * @param {!WebInspector.TimelineModel.Record} record
    106      * @param {!WebInspector.TimelineModel} model
    107      * @param {!WebInspector.Linkifier} linkifier
    108      * @param {function(!DocumentFragment)} callback
    109      * @param {boolean} loadedFromFile
    110      */
    111     generateDetailsContent: function(record, model, linkifier, callback, loadedFromFile)
    112     {
    113         throw new Error("Not implemented.");
    114     },
    115     /**
    116      * @return {!Element}
    117      */
    118     createBeginFrameDivider: function()
    119     {
    120         throw new Error("Not implemented.");
    121     },
    122     /**
    123      * @param {string} recordType
    124      * @param {string=} title
    125      * @return {!Element}
    126      */
    127     createEventDivider: function(recordType, title)
    128     {
    129         throw new Error("Not implemented.");
    130     },
    131     /**
    132      * @param {!WebInspector.TimelineModel.Record} record
    133      * @param {!RegExp} regExp
    134      * @return {boolean}
    135      */
    136     testContentMatching: function(record, regExp)
    137     {
    138         throw new Error("Not implemented.");
    139     }
    140 }
    141 
    142 /**
    143  * @return {!Object.<string, !WebInspector.TimelineCategory>}
    144  */
    145 WebInspector.TimelineUIUtils.categories = function()
    146 {
    147     if (WebInspector.TimelineUIUtils._categories)
    148         return WebInspector.TimelineUIUtils._categories;
    149     WebInspector.TimelineUIUtils._categories = {
    150         loading: new WebInspector.TimelineCategory("loading", WebInspector.UIString("Loading"), 0, "hsl(214, 53%, 58%)", "hsl(214, 67%, 90%)", "hsl(214, 67%, 74%)", "hsl(214, 67%, 66%)"),
    151         scripting: new WebInspector.TimelineCategory("scripting", WebInspector.UIString("Scripting"), 1, "hsl(43, 90%, 45%)", "hsl(43, 83%, 90%)", "hsl(43, 83%, 72%)", "hsl(43, 83%, 64%) "),
    152         rendering: new WebInspector.TimelineCategory("rendering", WebInspector.UIString("Rendering"), 2, "hsl(256, 50%, 60%)", "hsl(256, 67%, 90%)", "hsl(256, 67%, 76%)", "hsl(256, 67%, 70%)"),
    153         painting: new WebInspector.TimelineCategory("painting", WebInspector.UIString("Painting"), 2, "hsl(109, 33%, 47%)", "hsl(109, 33%, 90%)", "hsl(109, 33%, 64%)", "hsl(109, 33%, 55%)"),
    154         other: new WebInspector.TimelineCategory("other", WebInspector.UIString("Other"), -1, "hsl(0, 0%, 73%)", "hsl(0, 0%, 90%)", "hsl(0, 0%, 87%)", "hsl(0, 0%, 79%)"),
    155         idle: new WebInspector.TimelineCategory("idle", WebInspector.UIString("Idle"), -1, "hsl(0, 0%, 87%)", "hsl(0, 100%, 100%)", "hsl(0, 100%, 100%)", "hsl(0, 100%, 100%)")
    156     };
    157     return WebInspector.TimelineUIUtils._categories;
    158 };
    159 
    160 /**
    161  * @return {!Object.<string, !{title: string, category: !WebInspector.TimelineCategory}>}
    162  */
    163 WebInspector.TimelineUIUtils._initRecordStyles = function()
    164 {
    165     if (WebInspector.TimelineUIUtils._recordStylesMap)
    166         return WebInspector.TimelineUIUtils._recordStylesMap;
    167 
    168     var recordTypes = WebInspector.TimelineModel.RecordType;
    169     var categories = WebInspector.TimelineUIUtils.categories();
    170 
    171     var recordStyles = {};
    172     recordStyles[recordTypes.Root] = { title: "#root", category: categories["loading"] };
    173     recordStyles[recordTypes.Program] = { title: WebInspector.UIString("Other"), category: categories["other"] };
    174     recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: categories["scripting"] };
    175     recordStyles[recordTypes.BeginFrame] = { title: WebInspector.UIString("Frame Start"), category: categories["rendering"] };
    176     recordStyles[recordTypes.ScheduleStyleRecalculation] = { title: WebInspector.UIString("Schedule Style Recalculation"), category: categories["rendering"] };
    177     recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: categories["rendering"] };
    178     recordStyles[recordTypes.InvalidateLayout] = { title: WebInspector.UIString("Invalidate Layout"), category: categories["rendering"] };
    179     recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: categories["rendering"] };
    180     recordStyles[recordTypes.UpdateLayerTree] = { title: WebInspector.UIString("Update layer tree"), category: categories["rendering"] };
    181     recordStyles[recordTypes.PaintSetup] = { title: WebInspector.UIString("Paint Setup"), category: categories["painting"] };
    182     recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
    183     recordStyles[recordTypes.Rasterize] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
    184     recordStyles[recordTypes.ScrollLayer] = { title: WebInspector.UIString("Scroll"), category: categories["rendering"] };
    185     recordStyles[recordTypes.DecodeImage] = { title: WebInspector.UIString("Image Decode"), category: categories["painting"] };
    186     recordStyles[recordTypes.ResizeImage] = { title: WebInspector.UIString("Image Resize"), category: categories["painting"] };
    187     recordStyles[recordTypes.CompositeLayers] = { title: WebInspector.UIString("Composite Layers"), category: categories["painting"] };
    188     recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse HTML"), category: categories["loading"] };
    189     recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: categories["scripting"] };
    190     recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: categories["scripting"] };
    191     recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: categories["scripting"] };
    192     recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: categories["scripting"] };
    193     recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: categories["scripting"] };
    194     recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: categories["scripting"] };
    195     recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: categories["loading"] };
    196     recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: categories["loading"] };
    197     recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: categories["loading"] };
    198     recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: categories["scripting"] };
    199     recordStyles[recordTypes.ResourceReceivedData] = { title: WebInspector.UIString("Receive Data"), category: categories["loading"] };
    200     recordStyles[recordTypes.GCEvent] = { title: WebInspector.UIString("GC Event"), category: categories["scripting"] };
    201     recordStyles[recordTypes.JSFrame] = { title: WebInspector.UIString("JS Frame"), category: categories["scripting"] };
    202     recordStyles[recordTypes.MarkDOMContent] = { title: WebInspector.UIString("DOMContentLoaded event"), category: categories["scripting"] };
    203     recordStyles[recordTypes.MarkLoad] = { title: WebInspector.UIString("Load event"), category: categories["scripting"] };
    204     recordStyles[recordTypes.MarkFirstPaint] = { title: WebInspector.UIString("First paint"), category: categories["painting"] };
    205     recordStyles[recordTypes.TimeStamp] = { title: WebInspector.UIString("Stamp"), category: categories["scripting"] };
    206     recordStyles[recordTypes.ConsoleTime] = { title: WebInspector.UIString("Console Time"), category: categories["scripting"] };
    207     recordStyles[recordTypes.RequestAnimationFrame] = { title: WebInspector.UIString("Request Animation Frame"), category: categories["scripting"] };
    208     recordStyles[recordTypes.CancelAnimationFrame] = { title: WebInspector.UIString("Cancel Animation Frame"), category: categories["scripting"] };
    209     recordStyles[recordTypes.FireAnimationFrame] = { title: WebInspector.UIString("Animation Frame Fired"), category: categories["scripting"] };
    210     recordStyles[recordTypes.WebSocketCreate] = { title: WebInspector.UIString("Create WebSocket"), category: categories["scripting"] };
    211     recordStyles[recordTypes.WebSocketSendHandshakeRequest] = { title: WebInspector.UIString("Send WebSocket Handshake"), category: categories["scripting"] };
    212     recordStyles[recordTypes.WebSocketReceiveHandshakeResponse] = { title: WebInspector.UIString("Receive WebSocket Handshake"), category: categories["scripting"] };
    213     recordStyles[recordTypes.WebSocketDestroy] = { title: WebInspector.UIString("Destroy WebSocket"), category: categories["scripting"] };
    214     recordStyles[recordTypes.EmbedderCallback] = { title: WebInspector.UIString("Embedder Callback"), category: categories["scripting"] };
    215 
    216     WebInspector.TimelineUIUtils._recordStylesMap = recordStyles;
    217     return recordStyles;
    218 }
    219 
    220 /**
    221  * @param {!WebInspector.TimelineModel.Record} record
    222  * @return {!{title: string, category: !WebInspector.TimelineCategory}}
    223  */
    224 WebInspector.TimelineUIUtils.recordStyle = function(record)
    225 {
    226     var type = record.type();
    227     var recordStyles = WebInspector.TimelineUIUtils._initRecordStyles();
    228     var result = recordStyles[type];
    229     if (!result) {
    230         result = {
    231             title: WebInspector.UIString("Unknown: %s", type),
    232             category: WebInspector.TimelineUIUtils.categories()["other"]
    233         };
    234         recordStyles[type] = result;
    235     }
    236     return result;
    237 }
    238 
    239 /**
    240  * @param {!WebInspector.TimelineModel} model
    241  * @param {!{name: string, tasks: !Array.<!{startTime: number, endTime: number}>, firstTaskIndex: number, lastTaskIndex: number}} info
    242  * @return {!Element}
    243  */
    244 WebInspector.TimelineUIUtils.generateMainThreadBarPopupContent = function(model, info)
    245 {
    246     var firstTaskIndex = info.firstTaskIndex;
    247     var lastTaskIndex = info.lastTaskIndex;
    248     var tasks = info.tasks;
    249     var messageCount = lastTaskIndex - firstTaskIndex + 1;
    250     var cpuTime = 0;
    251 
    252     for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) {
    253         var task = tasks[i];
    254         cpuTime += task.endTime - task.startTime;
    255     }
    256     var startTime = tasks[firstTaskIndex].startTime;
    257     var endTime = tasks[lastTaskIndex].endTime;
    258     var duration = endTime - startTime;
    259 
    260     var contentHelper = new WebInspector.TimelinePopupContentHelper(info.name);
    261     var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(duration, true),
    262         Number.millisToString(startTime - model.minimumRecordTime(), true));
    263     contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
    264     contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(cpuTime, true));
    265     contentHelper.appendTextRow(WebInspector.UIString("Message Count"), messageCount);
    266     return contentHelper.contentTable();
    267 }
    268 
    269 /**
    270  * @param {!Object} total
    271  * @param {!Object} addend
    272  */
    273 WebInspector.TimelineUIUtils.aggregateTimeByCategory = function(total, addend)
    274 {
    275     for (var category in addend)
    276         total[category] = (total[category] || 0) + addend[category];
    277 }
    278 
    279 /**
    280  * @param {!Object} total
    281  * @param {!WebInspector.TimelineModel.Record} record
    282  */
    283 WebInspector.TimelineUIUtils.aggregateTimeForRecord = function(total, record)
    284 {
    285     var childrenTime = 0;
    286     var children = record.children();
    287     for (var i = 0; i < children.length; ++i) {
    288         WebInspector.TimelineUIUtils.aggregateTimeForRecord(total, children[i]);
    289         childrenTime += children[i].endTime() - children[i].startTime();
    290     }
    291     var categoryName = WebInspector.TimelineUIUtils.recordStyle(record).category.name;
    292     var ownTime = record.endTime() - record.startTime() - childrenTime;
    293     total[categoryName] = (total[categoryName] || 0) + ownTime;
    294 }
    295 
    296 /**
    297  * @param {!Object} aggregatedStats
    298  */
    299 WebInspector.TimelineUIUtils._generateAggregatedInfo = function(aggregatedStats)
    300 {
    301     var cell = document.createElement("span");
    302     cell.className = "timeline-aggregated-info";
    303     for (var index in aggregatedStats) {
    304         var label = document.createElement("div");
    305         label.className = "timeline-aggregated-category timeline-" + index;
    306         cell.appendChild(label);
    307         var text = document.createElement("span");
    308         text.textContent = Number.millisToString(aggregatedStats[index], true);
    309         cell.appendChild(text);
    310     }
    311     return cell;
    312 }
    313 
    314 /**
    315  * @param {!Object} aggregatedStats
    316  * @param {!WebInspector.TimelineCategory=} selfCategory
    317  * @param {number=} selfTime
    318  * @return {!Element}
    319  */
    320 WebInspector.TimelineUIUtils.generatePieChart = function(aggregatedStats, selfCategory, selfTime)
    321 {
    322     var element = document.createElement("div");
    323     element.className = "timeline-aggregated-info";
    324 
    325     var total = 0;
    326     for (var categoryName in aggregatedStats)
    327         total += aggregatedStats[categoryName];
    328 
    329     function formatter(value)
    330     {
    331         return Number.millisToString(value, true);
    332     }
    333     var pieChart = new WebInspector.PieChart(total, formatter);
    334     element.appendChild(pieChart.element);
    335     var footerElement = element.createChild("div", "timeline-aggregated-info-legend");
    336 
    337     // In case of self time, first add self, then children of the same category.
    338     if (selfCategory && selfTime) {
    339         // Self.
    340         pieChart.addSlice(selfTime, selfCategory.fillColorStop1);
    341         var rowElement = footerElement.createChild("div");
    342         rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
    343         rowElement.createTextChild(WebInspector.UIString("%s %s (Self)", formatter(selfTime), selfCategory.title));
    344 
    345         // Children of the same category.
    346         var categoryTime = aggregatedStats[selfCategory.name];
    347         var value = categoryTime - selfTime;
    348         if (value > 0) {
    349             pieChart.addSlice(value, selfCategory.fillColorStop0);
    350             rowElement = footerElement.createChild("div");
    351             rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
    352             rowElement.createTextChild(WebInspector.UIString("%s %s (Children)", formatter(value), selfCategory.title));
    353         }
    354     }
    355 
    356     // Add other categories.
    357     for (var categoryName in WebInspector.TimelineUIUtils.categories()) {
    358         var category = WebInspector.TimelineUIUtils.categories()[categoryName];
    359          if (category === selfCategory)
    360              continue;
    361          var value = aggregatedStats[category.name];
    362          if (!value)
    363              continue;
    364          pieChart.addSlice(value, category.fillColorStop0);
    365          var rowElement = footerElement.createChild("div");
    366          rowElement.createChild("div", "timeline-aggregated-category timeline-" + category.name);
    367          rowElement.createTextChild(WebInspector.UIString("%s %s", formatter(value), category.title));
    368     }
    369     return element;
    370 }
    371 
    372 /**
    373  * @param {!WebInspector.TimelineFrameModel} frameModel
    374  * @param {!WebInspector.TimelineFrame} frame
    375  * @return {!Element}
    376  */
    377 WebInspector.TimelineUIUtils.generateDetailsContentForFrame = function(frameModel, frame)
    378 {
    379     var contentHelper = new WebInspector.TimelineDetailsContentHelper(null, null, true);
    380     var durationInMillis = frame.endTime - frame.startTime;
    381     var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(frame.endTime - frame.startTime, true),
    382         Number.millisToString(frame.startTimeOffset, true));
    383     contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
    384     contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1000 / durationInMillis));
    385     contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(frame.cpuTime, true));
    386     contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
    387         WebInspector.TimelineUIUtils._generateAggregatedInfo(frame.timeByCategory));
    388     if (WebInspector.experimentsSettings.layersPanel.isEnabled() && frame.layerTree) {
    389         contentHelper.appendElementRow(WebInspector.UIString("Layer tree"),
    390                                        WebInspector.Linkifier.linkifyUsingRevealer(frame.layerTree, WebInspector.UIString("show")));
    391     }
    392     return contentHelper.element;
    393 }
    394 
    395 /**
    396  * @param {!CanvasRenderingContext2D} context
    397  * @param {number} width
    398  * @param {number} height
    399  * @param {string} color0
    400  * @param {string} color1
    401  * @param {string} color2
    402  * @return {!CanvasGradient}
    403  */
    404 WebInspector.TimelineUIUtils.createFillStyle = function(context, width, height, color0, color1, color2)
    405 {
    406     var gradient = context.createLinearGradient(0, 0, width, height);
    407     gradient.addColorStop(0, color0);
    408     gradient.addColorStop(0.25, color1);
    409     gradient.addColorStop(0.75, color1);
    410     gradient.addColorStop(1, color2);
    411     return gradient;
    412 }
    413 
    414 /**
    415  * @param {!CanvasRenderingContext2D} context
    416  * @param {number} width
    417  * @param {number} height
    418  * @param {!WebInspector.TimelineCategory} category
    419  * @return {!CanvasGradient}
    420  */
    421 WebInspector.TimelineUIUtils.createFillStyleForCategory = function(context, width, height, category)
    422 {
    423     return WebInspector.TimelineUIUtils.createFillStyle(context, width, height, category.fillColorStop0, category.fillColorStop1, category.borderColor);
    424 }
    425 
    426 /**
    427  * @param {!WebInspector.TimelineCategory} category
    428  * @return {string}
    429  */
    430 WebInspector.TimelineUIUtils.createStyleRuleForCategory = function(category)
    431 {
    432     var selector = ".timeline-category-" + category.name + " .timeline-graph-bar, " +
    433         ".panel.timeline .timeline-filters-header .filter-checkbox-filter.filter-checkbox-filter-" + category.name + " .checkbox-filter-checkbox, " +
    434         ".popover .timeline-" + category.name + ", " +
    435         ".timeline-details-view .timeline-" + category.name + ", " +
    436         ".timeline-category-" + category.name + " .timeline-tree-icon"
    437 
    438     return selector + " { background-image: linear-gradient(" +
    439        category.fillColorStop0 + ", " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + ");" +
    440        " border-color: " + category.borderColor +
    441        "}";
    442 }
    443 
    444 /**
    445  * @param {!Array.<number>} quad
    446  * @return {number}
    447  */
    448 WebInspector.TimelineUIUtils._quadWidth = function(quad)
    449 {
    450     return Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
    451 }
    452 
    453 /**
    454  * @param {!Array.<number>} quad
    455  * @return {number}
    456  */
    457 WebInspector.TimelineUIUtils._quadHeight = function(quad)
    458 {
    459     return Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
    460 }
    461 
    462 /**
    463  * @constructor
    464  * @extends {WebInspector.Object}
    465  * @param {string} name
    466  * @param {string} title
    467  * @param {number} overviewStripGroupIndex
    468  * @param {string} borderColor
    469  * @param {string} backgroundColor
    470  * @param {string} fillColorStop0
    471  * @param {string} fillColorStop1
    472  */
    473 WebInspector.TimelineCategory = function(name, title, overviewStripGroupIndex, borderColor, backgroundColor, fillColorStop0, fillColorStop1)
    474 {
    475     this.name = name;
    476     this.title = title;
    477     this.overviewStripGroupIndex = overviewStripGroupIndex;
    478     this.borderColor = borderColor;
    479     this.backgroundColor = backgroundColor;
    480     this.fillColorStop0 = fillColorStop0;
    481     this.fillColorStop1 = fillColorStop1;
    482     this.hidden = false;
    483 }
    484 
    485 WebInspector.TimelineCategory.Events = {
    486     VisibilityChanged: "VisibilityChanged"
    487 };
    488 
    489 WebInspector.TimelineCategory.prototype = {
    490     /**
    491      * @return {boolean}
    492      */
    493     get hidden()
    494     {
    495         return this._hidden;
    496     },
    497 
    498     set hidden(hidden)
    499     {
    500         this._hidden = hidden;
    501         this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged, this);
    502     },
    503 
    504     __proto__: WebInspector.Object.prototype
    505 }
    506 
    507 /**
    508  * @constructor
    509  * @param {string} title
    510  */
    511 WebInspector.TimelinePopupContentHelper = function(title)
    512 {
    513     this._contentTable = document.createElement("table");
    514     var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title");
    515     titleCell.colSpan = 2;
    516     var titleRow = document.createElement("tr");
    517     titleRow.appendChild(titleCell);
    518     this._contentTable.appendChild(titleRow);
    519 }
    520 
    521 WebInspector.TimelinePopupContentHelper.prototype = {
    522     /**
    523      * @return {!Element}
    524      */
    525     contentTable: function()
    526     {
    527         return this._contentTable;
    528     },
    529 
    530     /**
    531      * @param {string|number} content
    532      * @param {string=} styleName
    533      */
    534     _createCell: function(content, styleName)
    535     {
    536         var text = document.createElement("label");
    537         text.appendChild(document.createTextNode(content));
    538         var cell = document.createElement("td");
    539         cell.className = "timeline-details";
    540         if (styleName)
    541             cell.className += " " + styleName;
    542         cell.textContent = content;
    543         return cell;
    544     },
    545 
    546     /**
    547      * @param {string} title
    548      * @param {string|number} content
    549      */
    550     appendTextRow: function(title, content)
    551     {
    552         var row = document.createElement("tr");
    553         row.appendChild(this._createCell(title, "timeline-details-row-title"));
    554         row.appendChild(this._createCell(content, "timeline-details-row-data"));
    555         this._contentTable.appendChild(row);
    556     },
    557 
    558     /**
    559      * @param {string} title
    560      * @param {!Node|string} content
    561      */
    562     appendElementRow: function(title, content)
    563     {
    564         var row = document.createElement("tr");
    565         var titleCell = this._createCell(title, "timeline-details-row-title");
    566         row.appendChild(titleCell);
    567         var cell = document.createElement("td");
    568         cell.className = "details";
    569         if (content instanceof Node)
    570             cell.appendChild(content);
    571         else
    572             cell.createTextChild(content || "");
    573         row.appendChild(cell);
    574         this._contentTable.appendChild(row);
    575     }
    576 }
    577 
    578 /**
    579  * @constructor
    580  * @param {?WebInspector.Target} target
    581  * @param {?WebInspector.Linkifier} linkifier
    582  * @param {boolean} monospaceValues
    583  */
    584 WebInspector.TimelineDetailsContentHelper = function(target, linkifier, monospaceValues)
    585 {
    586     this._linkifier = linkifier;
    587     this._target = target;
    588     this.element = document.createElement("div");
    589     this.element.className = "timeline-details-view-block";
    590     this._monospaceValues = monospaceValues;
    591 }
    592 
    593 WebInspector.TimelineDetailsContentHelper.prototype = {
    594     /**
    595      * @param {string} title
    596      * @param {string|number|boolean} value
    597      */
    598     appendTextRow: function(title, value)
    599     {
    600         var rowElement = this.element.createChild("div", "timeline-details-view-row");
    601         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
    602         rowElement.createChild("span", "timeline-details-view-row-value" + (this._monospaceValues ? " monospace" : "")).textContent = value;
    603     },
    604 
    605     /**
    606      * @param {string} title
    607      * @param {!Node|string} content
    608      */
    609     appendElementRow: function(title, content)
    610     {
    611         var rowElement = this.element.createChild("div", "timeline-details-view-row");
    612         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
    613         var valueElement = rowElement.createChild("span", "timeline-details-view-row-details" + (this._monospaceValues ? " monospace" : ""));
    614         if (content instanceof Node)
    615             valueElement.appendChild(content);
    616         else
    617             valueElement.createTextChild(content || "");
    618     },
    619 
    620     /**
    621      * @param {string} title
    622      * @param {string} url
    623      * @param {number} line
    624      */
    625     appendLocationRow: function(title, url, line)
    626     {
    627         if (!this._linkifier || !this._target)
    628             return;
    629         this.appendElementRow(title, this._linkifier.linkifyLocation(this._target, url, line - 1) || "");
    630     },
    631 
    632     /**
    633      * @param {string} title
    634      * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
    635      */
    636     appendStackTrace: function(title, stackTrace)
    637     {
    638         if (!this._linkifier || !this._target)
    639             return;
    640 
    641         var rowElement = this.element.createChild("div", "timeline-details-view-row");
    642         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
    643         var stackTraceElement = rowElement.createChild("div", "timeline-details-view-row-stack-trace monospace");
    644 
    645         for (var i = 0; i < stackTrace.length; ++i) {
    646             var stackFrame = stackTrace[i];
    647             var row = stackTraceElement.createChild("div");
    648             row.createTextChild(stackFrame.functionName || WebInspector.UIString("(anonymous function)"));
    649             row.createTextChild(" @ ");
    650             var urlElement = this._linkifier.linkifyLocation(this._target, stackFrame.url, stackFrame.lineNumber - 1);
    651             row.appendChild(urlElement);
    652         }
    653     }
    654 }
    655