Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelineUIUtils.js
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      * @return {!WebInspector.TimelineCategory}
97      */
98     categoryForRecord: function(record)
99     {
100         throw new Error("Not implemented.");
101     },
102     /**
103      * @param {!WebInspector.TimelineModel.Record} record
104      * @param {!WebInspector.Linkifier} linkifier
105      * @return {?Node}
106      */
107     buildDetailsNode: function(record, linkifier)
108     {
109         throw new Error("Not implemented.");
110     },
111     /**
112      * @param {!WebInspector.TimelineModel.Record} record
113      * @param {!WebInspector.TimelineModel} model
114      * @param {!WebInspector.Linkifier} linkifier
115      * @param {function(!DocumentFragment)} callback
116      */
117     generateDetailsContent: function(record, model, linkifier, callback)
118     {
119         throw new Error("Not implemented.");
120     },
121     /**
122      * @return {!Element}
123      */
124     createBeginFrameDivider: function()
125     {
126         throw new Error("Not implemented.");
127     },
128     /**
129      * @param {string} recordType
130      * @param {string=} title
131      * @return {!Element}
132      */
133     createEventDivider: function(recordType, title)
134     {
135         throw new Error("Not implemented.");
136     },
137     /**
138      * @param {!WebInspector.TimelineModel.Record} record
139      * @param {!RegExp} regExp
140      * @return {boolean}
141      */
142     testContentMatching: function(record, regExp)
143     {
144         throw new Error("Not implemented.");
145     },
146     /**
147      * @param {!Object} total
148      * @param {!WebInspector.TimelineModel.Record} record
149      */
150     aggregateTimeForRecord: function(total, record)
151     {
152         throw new Error("Not implemented.");
153     },
154     /**
155      * @return {!WebInspector.TimelineModel.Filter}
156      */
157     hiddenRecordsFilter: function()
158     {
159         throw new Error("Not implemented.");
160     },
161     /**
162      * @return {?WebInspector.TimelineModel.Filter}
163      */
164     hiddenEmptyRecordsFilter: function()
165     {
166         return null;
167     }
168 }
169
170 /**
171  * @return {!Object.<string, !WebInspector.TimelineCategory>}
172  */
173 WebInspector.TimelineUIUtils.categories = function()
174 {
175     if (WebInspector.TimelineUIUtils._categories)
176         return WebInspector.TimelineUIUtils._categories;
177     WebInspector.TimelineUIUtils._categories = {
178         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%)"),
179         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%) "),
180         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%)"),
181         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%)"),
182         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%)"),
183         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%)")
184     };
185     return WebInspector.TimelineUIUtils._categories;
186 };
187
188 /**
189  * @param {!WebInspector.TimelineModel} model
190  * @param {!{name: string, tasks: !Array.<!WebInspector.TimelineModel.Record>, firstTaskIndex: number, lastTaskIndex: number}} info
191  * @return {!Element}
192  */
193 WebInspector.TimelineUIUtils.generateMainThreadBarPopupContent = function(model, info)
194 {
195     var firstTaskIndex = info.firstTaskIndex;
196     var lastTaskIndex = info.lastTaskIndex;
197     var tasks = info.tasks;
198     var messageCount = lastTaskIndex - firstTaskIndex + 1;
199     var cpuTime = 0;
200
201     for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) {
202         var task = tasks[i];
203         cpuTime += task.endTime() - task.startTime();
204     }
205     var startTime = tasks[firstTaskIndex].startTime();
206     var endTime = tasks[lastTaskIndex].endTime();
207     var duration = endTime - startTime;
208
209     var contentHelper = new WebInspector.TimelinePopupContentHelper(info.name);
210     var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(duration, true),
211         Number.millisToString(startTime - model.minimumRecordTime(), true));
212     contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
213     contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(cpuTime, true));
214     contentHelper.appendTextRow(WebInspector.UIString("Message Count"), messageCount);
215     return contentHelper.contentTable();
216 }
217
218 /**
219  * @param {!Object} aggregatedStats
220  */
221 WebInspector.TimelineUIUtils._generateAggregatedInfo = function(aggregatedStats)
222 {
223     var cell = document.createElement("span");
224     cell.className = "timeline-aggregated-info";
225     for (var index in aggregatedStats) {
226         var label = document.createElement("div");
227         label.className = "timeline-aggregated-category timeline-" + index;
228         cell.appendChild(label);
229         var text = document.createElement("span");
230         text.textContent = Number.millisToString(aggregatedStats[index], true);
231         cell.appendChild(text);
232     }
233     return cell;
234 }
235
236 /**
237  * @param {!Object} aggregatedStats
238  * @param {!WebInspector.TimelineCategory=} selfCategory
239  * @param {number=} selfTime
240  * @return {!Element}
241  */
242 WebInspector.TimelineUIUtils.generatePieChart = function(aggregatedStats, selfCategory, selfTime)
243 {
244     var element = document.createElement("div");
245     element.className = "timeline-aggregated-info";
246
247     var total = 0;
248     for (var categoryName in aggregatedStats)
249         total += aggregatedStats[categoryName];
250
251     function formatter(value)
252     {
253         return Number.millisToString(value, true);
254     }
255     var pieChart = new WebInspector.PieChart(100, formatter);
256     pieChart.setTotal(total);
257     element.appendChild(pieChart.element);
258     var footerElement = element.createChild("div", "timeline-aggregated-info-legend");
259
260     // In case of self time, first add self, then children of the same category.
261     if (selfCategory && selfTime) {
262         // Self.
263         pieChart.addSlice(selfTime, selfCategory.fillColorStop1);
264         var rowElement = footerElement.createChild("div");
265         rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
266         rowElement.createTextChild(WebInspector.UIString("%s %s (Self)", formatter(selfTime), selfCategory.title));
267
268         // Children of the same category.
269         var categoryTime = aggregatedStats[selfCategory.name];
270         var value = categoryTime - selfTime;
271         if (value > 0) {
272             pieChart.addSlice(value, selfCategory.fillColorStop0);
273             rowElement = footerElement.createChild("div");
274             rowElement.createChild("div", "timeline-aggregated-category timeline-" + selfCategory.name);
275             rowElement.createTextChild(WebInspector.UIString("%s %s (Children)", formatter(value), selfCategory.title));
276         }
277     }
278
279     // Add other categories.
280     for (var categoryName in WebInspector.TimelineUIUtils.categories()) {
281         var category = WebInspector.TimelineUIUtils.categories()[categoryName];
282          if (category === selfCategory)
283              continue;
284          var value = aggregatedStats[category.name];
285          if (!value)
286              continue;
287          pieChart.addSlice(value, category.fillColorStop0);
288          var rowElement = footerElement.createChild("div");
289          rowElement.createChild("div", "timeline-aggregated-category timeline-" + category.name);
290          rowElement.createTextChild(WebInspector.UIString("%s %s", formatter(value), category.title));
291     }
292     return element;
293 }
294
295 /**
296  * @param {!WebInspector.TimelineFrameModel} frameModel
297  * @param {!WebInspector.TimelineFrame} frame
298  * @return {!Element}
299  */
300 WebInspector.TimelineUIUtils.generateDetailsContentForFrame = function(frameModel, frame)
301 {
302     var contentHelper = new WebInspector.TimelineDetailsContentHelper(null, null, true);
303     var durationInMillis = frame.endTime - frame.startTime;
304     var durationText = WebInspector.UIString("%s (at %s)", Number.millisToString(frame.endTime - frame.startTime, true),
305         Number.millisToString(frame.startTimeOffset, true));
306     contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
307     contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1000 / durationInMillis));
308     contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.millisToString(frame.cpuTime, true));
309     contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
310         WebInspector.TimelineUIUtils._generateAggregatedInfo(frame.timeByCategory));
311     if (Runtime.experiments.isEnabled("layersPanel") && frame.layerTree) {
312         contentHelper.appendElementRow(WebInspector.UIString("Layer tree"),
313                                        WebInspector.Linkifier.linkifyUsingRevealer(frame.layerTree, WebInspector.UIString("show")));
314     }
315     return contentHelper.element;
316 }
317
318 /**
319  * @param {!CanvasRenderingContext2D} context
320  * @param {number} width
321  * @param {number} height
322  * @param {string} color0
323  * @param {string} color1
324  * @param {string} color2
325  * @return {!CanvasGradient}
326  */
327 WebInspector.TimelineUIUtils.createFillStyle = function(context, width, height, color0, color1, color2)
328 {
329     var gradient = context.createLinearGradient(0, 0, width, height);
330     gradient.addColorStop(0, color0);
331     gradient.addColorStop(0.25, color1);
332     gradient.addColorStop(0.75, color1);
333     gradient.addColorStop(1, color2);
334     return gradient;
335 }
336
337 /**
338  * @param {!CanvasRenderingContext2D} context
339  * @param {number} width
340  * @param {number} height
341  * @param {!WebInspector.TimelineCategory} category
342  * @return {!CanvasGradient}
343  */
344 WebInspector.TimelineUIUtils.createFillStyleForCategory = function(context, width, height, category)
345 {
346     return WebInspector.TimelineUIUtils.createFillStyle(context, width, height, category.fillColorStop0, category.fillColorStop1, category.borderColor);
347 }
348
349 /**
350  * @param {!WebInspector.TimelineCategory} category
351  * @return {string}
352  */
353 WebInspector.TimelineUIUtils.createStyleRuleForCategory = function(category)
354 {
355     var selector = ".timeline-category-" + category.name + " .timeline-graph-bar, " +
356         ".panel.timeline .timeline-filters-header .filter-checkbox-filter.filter-checkbox-filter-" + category.name + " .checkbox-filter-checkbox, " +
357         ".timeline-details-view .timeline-" + category.name + ", " +
358         ".timeline-category-" + category.name + " .timeline-tree-icon"
359
360     return selector + " { background-image: linear-gradient(" +
361        category.fillColorStop0 + ", " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + ");" +
362        " border-color: " + category.borderColor +
363        "}";
364 }
365
366 /**
367  * @param {!Array.<number>} quad
368  * @return {number}
369  */
370 WebInspector.TimelineUIUtils.quadWidth = function(quad)
371 {
372     return Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
373 }
374
375 /**
376  * @param {!Array.<number>} quad
377  * @return {number}
378  */
379 WebInspector.TimelineUIUtils.quadHeight = function(quad)
380 {
381     return Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
382 }
383
384 /**
385  * @constructor
386  * @extends {WebInspector.Object}
387  * @param {string} name
388  * @param {string} title
389  * @param {number} overviewStripGroupIndex
390  * @param {string} borderColor
391  * @param {string} backgroundColor
392  * @param {string} fillColorStop0
393  * @param {string} fillColorStop1
394  */
395 WebInspector.TimelineCategory = function(name, title, overviewStripGroupIndex, borderColor, backgroundColor, fillColorStop0, fillColorStop1)
396 {
397     this.name = name;
398     this.title = title;
399     this.overviewStripGroupIndex = overviewStripGroupIndex;
400     this.borderColor = borderColor;
401     this.backgroundColor = backgroundColor;
402     this.fillColorStop0 = fillColorStop0;
403     this.fillColorStop1 = fillColorStop1;
404     this.hidden = false;
405 }
406
407 WebInspector.TimelineCategory.Events = {
408     VisibilityChanged: "VisibilityChanged"
409 };
410
411 WebInspector.TimelineCategory.prototype = {
412     /**
413      * @return {boolean}
414      */
415     get hidden()
416     {
417         return this._hidden;
418     },
419
420     set hidden(hidden)
421     {
422         this._hidden = hidden;
423         this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged, this);
424     },
425
426     __proto__: WebInspector.Object.prototype
427 }
428
429 /**
430  * @constructor
431  * @param {string} title
432  */
433 WebInspector.TimelinePopupContentHelper = function(title)
434 {
435     this._contentTable = document.createElement("table");
436     var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title");
437     titleCell.colSpan = 2;
438     var titleRow = document.createElement("tr");
439     titleRow.appendChild(titleCell);
440     this._contentTable.appendChild(titleRow);
441 }
442
443 WebInspector.TimelinePopupContentHelper.prototype = {
444     /**
445      * @return {!Element}
446      */
447     contentTable: function()
448     {
449         return this._contentTable;
450     },
451
452     /**
453      * @param {string|number} content
454      * @param {string=} styleName
455      */
456     _createCell: function(content, styleName)
457     {
458         var text = document.createElement("label");
459         text.createTextChild(String(content));
460         var cell = document.createElement("td");
461         cell.className = "timeline-details";
462         if (styleName)
463             cell.className += " " + styleName;
464         cell.textContent = content;
465         return cell;
466     },
467
468     /**
469      * @param {string} title
470      * @param {string|number} content
471      */
472     appendTextRow: function(title, content)
473     {
474         var row = document.createElement("tr");
475         row.appendChild(this._createCell(title, "timeline-details-row-title"));
476         row.appendChild(this._createCell(content, "timeline-details-row-data"));
477         this._contentTable.appendChild(row);
478     },
479
480     /**
481      * @param {string} title
482      * @param {!Node|string} content
483      */
484     appendElementRow: function(title, content)
485     {
486         var row = document.createElement("tr");
487         var titleCell = this._createCell(title, "timeline-details-row-title");
488         row.appendChild(titleCell);
489         var cell = document.createElement("td");
490         cell.className = "details";
491         if (content instanceof Node)
492             cell.appendChild(content);
493         else
494             cell.createTextChild(content || "");
495         row.appendChild(cell);
496         this._contentTable.appendChild(row);
497     }
498 }
499
500 /**
501  * @constructor
502  * @param {?WebInspector.Target} target
503  * @param {?WebInspector.Linkifier} linkifier
504  * @param {boolean} monospaceValues
505  */
506 WebInspector.TimelineDetailsContentHelper = function(target, linkifier, monospaceValues)
507 {
508     this._linkifier = linkifier;
509     this._target = target;
510     this.element = document.createElement("div");
511     this.element.className = "timeline-details-view-block";
512     this._monospaceValues = monospaceValues;
513 }
514
515 WebInspector.TimelineDetailsContentHelper.prototype = {
516     /**
517      * @param {string} title
518      * @param {string|number|boolean} value
519      */
520     appendTextRow: function(title, value)
521     {
522         var rowElement = this.element.createChild("div", "timeline-details-view-row");
523         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
524         rowElement.createChild("span", "timeline-details-view-row-value" + (this._monospaceValues ? " monospace" : "")).textContent = value;
525     },
526
527     /**
528      * @param {string} title
529      * @param {!Node|string} content
530      */
531     appendElementRow: function(title, content)
532     {
533         var rowElement = this.element.createChild("div", "timeline-details-view-row");
534         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
535         var valueElement = rowElement.createChild("span", "timeline-details-view-row-details" + (this._monospaceValues ? " monospace" : ""));
536         if (content instanceof Node)
537             valueElement.appendChild(content);
538         else
539             valueElement.createTextChild(content || "");
540     },
541
542     /**
543      * @param {string} title
544      * @param {string} url
545      * @param {number} line
546      */
547     appendLocationRow: function(title, url, line)
548     {
549         if (!this._linkifier || !this._target)
550             return;
551         this.appendElementRow(title, this._linkifier.linkifyScriptLocation(this._target, null, url, line - 1) || "");
552     },
553
554     /**
555      * @param {string} title
556      * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace
557      */
558     appendStackTrace: function(title, stackTrace)
559     {
560         if (!this._linkifier || !this._target)
561             return;
562
563         var rowElement = this.element.createChild("div", "timeline-details-view-row");
564         rowElement.createChild("span", "timeline-details-view-row-title").textContent = WebInspector.UIString("%s: ", title);
565         var stackTraceElement = rowElement.createChild("div", "timeline-details-view-row-stack-trace monospace");
566
567         for (var i = 0; i < stackTrace.length; ++i) {
568             var stackFrame = stackTrace[i];
569             var row = stackTraceElement.createChild("div");
570             row.createTextChild(stackFrame.functionName || WebInspector.UIString("(anonymous function)"));
571             row.createTextChild(" @ ");
572             var urlElement = this._linkifier.linkifyScriptLocation(this._target, stackFrame.scriptId, stackFrame.url, stackFrame.lineNumber - 1, stackFrame.columnNumber - 1);
573             row.appendChild(urlElement);
574         }
575     }
576 }