Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / TimelineOverviewPane.js
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.VBox}
34  * @param {!WebInspector.TimelineModel} model
35  */
36 WebInspector.TimelineOverviewPane = function(model)
37 {
38     WebInspector.VBox.call(this);
39     this.element.id = "timeline-overview-pane";
40
41     this._eventDividers = [];
42
43     this._model = model;
44
45     this._overviewGrid = new WebInspector.OverviewGrid("timeline");
46     this.element.appendChild(this._overviewGrid.element);
47
48     this._overviewCalculator = new WebInspector.TimelineOverviewCalculator();
49
50     model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._reset, this);
51     this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
52 }
53
54 WebInspector.TimelineOverviewPane.Events = {
55     WindowChanged: "WindowChanged"
56 };
57
58 WebInspector.TimelineOverviewPane.prototype = {
59     wasShown: function()
60     {
61         this._update();
62     },
63
64     onResize: function()
65     {
66         this._update();
67     },
68
69     /**
70      * @param {!WebInspector.TimelineOverview} overviewControl
71      */
72     setOverviewControl: function(overviewControl)
73     {
74         if (this._overviewControl === overviewControl)
75             return;
76
77         var windowTimes = null;
78
79         if (this._overviewControl) {
80             windowTimes = this._overviewControl.windowTimes(this._overviewGrid.windowLeft(), this._overviewGrid.windowRight());
81             this._overviewControl.detach();
82         }
83         this._overviewControl = overviewControl;
84         this._overviewControl.show(this._overviewGrid.element);
85         this._update();
86         if (windowTimes)
87             this.requestWindowTimes(windowTimes.startTime, windowTimes.endTime);
88     },
89
90     _update: function()
91     {
92         delete this._refreshTimeout;
93
94         this._overviewCalculator._setWindow(this._model.minimumRecordTime(), this._model.maximumRecordTime());
95         this._overviewCalculator._setDisplayWindow(0, this._overviewGrid.clientWidth());
96         if (this._overviewControl)
97             this._overviewControl.update();
98         this._overviewGrid.updateDividers(this._overviewCalculator);
99         this._updateEventDividers();
100         this._updateWindow();
101     },
102
103     _updateEventDividers: function()
104     {
105         var records = this._eventDividers;
106         this._overviewGrid.removeEventDividers();
107         var dividers = [];
108         for (var i = 0; i < records.length; ++i) {
109             var record = records[i];
110             var positions = this._overviewCalculator.computeBarGraphPercentages(record);
111             var dividerPosition = Math.round(positions.start * 10);
112             if (dividers[dividerPosition])
113                 continue;
114             var divider = WebInspector.TimelineUIUtils.createEventDivider(record.type);
115             divider.style.left = positions.start + "%";
116             dividers[dividerPosition] = divider;
117         }
118         this._overviewGrid.addEventDividers(dividers);
119     },
120
121     /**
122      * @param {!WebInspector.TimelineModel.Record} record
123      */
124     addRecord: function(record)
125     {
126         var eventDividers = this._eventDividers;
127         function addEventDividers(record)
128         {
129             if (WebInspector.TimelineUIUtils.isEventDivider(record))
130                 eventDividers.push(record);
131         }
132         WebInspector.TimelineModel.forAllRecords([record], addEventDividers);
133         this._scheduleRefresh();
134     },
135
136     _reset: function()
137     {
138         this._overviewCalculator.reset();
139         this._overviewGrid.reset();
140         this._overviewGrid.setResizeEnabled(false);
141         this._eventDividers = [];
142         this._overviewGrid.updateDividers(this._overviewCalculator);
143         if (this._overviewControl)
144             this._overviewControl.reset();
145         this._update();
146     },
147
148     /**
149      * @param {!WebInspector.Event} event
150      */
151     _onWindowChanged: function(event)
152     {
153         if (this._muteOnWindowChanged)
154             return;
155         var windowTimes = this._overviewControl.windowTimes(this._overviewGrid.windowLeft(), this._overviewGrid.windowRight());
156         this._windowStartTime = windowTimes.startTime;
157         this._windowEndTime = windowTimes.endTime;
158         this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.WindowChanged, windowTimes);
159     },
160
161     /**
162      * @param {number} startTime
163      * @param {number} endTime
164      */
165     requestWindowTimes: function(startTime, endTime)
166     {
167         if (startTime === this._windowStartTime && endTime === this._windowEndTime)
168             return;
169         this._windowStartTime = startTime;
170         this._windowEndTime = endTime;
171         this._updateWindow();
172         this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.WindowChanged, { startTime: startTime, endTime: endTime });
173     },
174
175     _updateWindow: function()
176     {
177         var windowBoundaries = this._overviewControl.windowBoundaries(this._windowStartTime, this._windowEndTime);
178         this._muteOnWindowChanged = true;
179         this._overviewGrid.setWindow(windowBoundaries.left, windowBoundaries.right);
180         this._overviewGrid.setResizeEnabled(!!this._model.records().length);
181         this._muteOnWindowChanged = false;
182     },
183
184     _scheduleRefresh: function()
185     {
186         if (this._refreshTimeout)
187             return;
188         if (!this.isShowing())
189             return;
190         this._refreshTimeout = setTimeout(this._update.bind(this), 300);
191     },
192
193     __proto__: WebInspector.VBox.prototype
194 }
195
196 /**
197  * @constructor
198  * @implements {WebInspector.TimelineGrid.Calculator}
199  */
200 WebInspector.TimelineOverviewCalculator = function()
201 {
202 }
203
204 WebInspector.TimelineOverviewCalculator.prototype = {
205     /**
206      * @return {number}
207      */
208     paddingLeft: function()
209     {
210         return this._paddingLeft;
211     },
212
213     /**
214      * @param {number} time
215      * @return {number}
216      */
217     computePosition: function(time)
218     {
219         return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea + this._paddingLeft;
220     },
221
222     /**
223      * @return {!{start: number, end: number}}
224      */
225     computeBarGraphPercentages: function(record)
226     {
227         var start = (record.startTime - this._minimumBoundary) / this.boundarySpan() * 100;
228         var end = (record.endTime - this._minimumBoundary) / this.boundarySpan() * 100;
229         return {start: start, end: end};
230     },
231
232     /**
233      * @param {number=} minimumRecordTime
234      * @param {number=} maximumRecordTime
235      */
236     _setWindow: function(minimumRecordTime, maximumRecordTime)
237     {
238         this._minimumBoundary = minimumRecordTime;
239         this._maximumBoundary = maximumRecordTime;
240     },
241
242     /**
243      * @param {number} paddingLeft
244      * @param {number} clientWidth
245      */
246     _setDisplayWindow: function(paddingLeft, clientWidth)
247     {
248         this._workingArea = clientWidth - paddingLeft;
249         this._paddingLeft = paddingLeft;
250     },
251
252     reset: function()
253     {
254         this._setWindow(0, 1000);
255     },
256
257     /**
258      * @param {number} value
259      * @param {number=} precision
260      * @return {string}
261      */
262     formatTime: function(value, precision)
263     {
264         return Number.preciseMillisToString(value - this.zeroTime(), precision);
265     },
266
267     /**
268      * @return {number}
269      */
270     maximumBoundary: function()
271     {
272         return this._maximumBoundary;
273     },
274
275     /**
276      * @return {number}
277      */
278     minimumBoundary: function()
279     {
280         return this._minimumBoundary;
281     },
282
283     /**
284      * @return {number}
285      */
286     zeroTime: function()
287     {
288         return this._minimumBoundary;
289     },
290
291     /**
292      * @return {number}
293      */
294     boundarySpan: function()
295     {
296         return this._maximumBoundary - this._minimumBoundary;
297     }
298 }
299
300 /**
301  * @interface
302  */
303 WebInspector.TimelineOverview = function(model)
304 {
305 }
306
307 WebInspector.TimelineOverview.prototype = {
308     /**
309      * @param {?Element} parentElement
310      * @param {!Element=} insertBefore
311      */
312     show: function(parentElement, insertBefore) { },
313
314     update: function() { },
315
316     reset: function() { },
317
318     /**
319      * @param {number} windowLeft
320      * @param {number} windowRight
321      * @return {!{startTime: number, endTime: number}}
322      */
323     windowTimes: function(windowLeft, windowRight) { },
324
325     /**
326      * @param {number} startTime
327      * @param {number} endTime
328      * @return {!{left: number, right: number}}
329      */
330     windowBoundaries: function(startTime, endTime) { }
331 }
332
333 /**
334  * @constructor
335  * @extends {WebInspector.VBox}
336  * @implements {WebInspector.TimelineOverview}
337  * @param {!WebInspector.TimelineModel} model
338  */
339 WebInspector.TimelineOverviewBase = function(model)
340 {
341     WebInspector.VBox.call(this);
342
343     this._model = model;
344     this._canvas = this.element.createChild("canvas", "fill");
345     this._context = this._canvas.getContext("2d");
346 }
347
348 WebInspector.TimelineOverviewBase.prototype = {
349     update: function()
350     {
351         this.resetCanvas();
352     },
353
354     reset: function()
355     {
356     },
357
358     timelineStarted: function() { },
359     timelineStopped: function() { },
360
361     /**
362      * @param {number} windowLeft
363      * @param {number} windowRight
364      * @return {!{startTime: number, endTime: number}}
365      */
366     windowTimes: function(windowLeft, windowRight)
367     {
368         var absoluteMin = this._model.minimumRecordTime();
369         var timeSpan = this._model.maximumRecordTime() - absoluteMin;
370         return {
371             startTime: absoluteMin + timeSpan * windowLeft,
372             endTime: absoluteMin + timeSpan * windowRight
373         };
374     },
375
376     /**
377      * @param {number} startTime
378      * @param {number} endTime
379      * @return {!{left: number, right: number}}
380      */
381     windowBoundaries: function(startTime, endTime)
382     {
383         var absoluteMin = this._model.minimumRecordTime();
384         var timeSpan = this._model.maximumRecordTime() - absoluteMin;
385         var haveRecords = absoluteMin >= 0;
386         return {
387             left: haveRecords && startTime ? Math.min((startTime - absoluteMin) / timeSpan, 1) : 0,
388             right: haveRecords && endTime < Infinity ? (endTime - absoluteMin) / timeSpan : 1
389         }
390     },
391
392     resetCanvas: function()
393     {
394         this._canvas.width = this.element.clientWidth * window.devicePixelRatio;
395         this._canvas.height = this.element.clientHeight * window.devicePixelRatio;
396     },
397
398     __proto__: WebInspector.VBox.prototype
399 }