• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
35WebInspector.TimelineUIUtils = function() { }
36
37WebInspector.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 */
145WebInspector.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 */
163WebInspector.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 */
224WebInspector.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 */
244WebInspector.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 */
273WebInspector.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 */
283WebInspector.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 */
299WebInspector.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 */
320WebInspector.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 */
377WebInspector.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 */
404WebInspector.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 */
421WebInspector.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 */
430WebInspector.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 */
448WebInspector.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 */
457WebInspector.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 */
473WebInspector.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
485WebInspector.TimelineCategory.Events = {
486    VisibilityChanged: "VisibilityChanged"
487};
488
489WebInspector.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 */
511WebInspector.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
521WebInspector.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 */
584WebInspector.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
593WebInspector.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