44b11e2146dfd55b3a4a04fb0c0d1862f2ecb4b0
[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.View}
34  * @param {!WebInspector.TimelineModel} model
35  */
36 WebInspector.TimelineOverviewPane = function(model)
37 {
38     WebInspector.View.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.RecordAdded, this._onRecordAdded, this);
51     model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._reset, this);
52     this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
53 }
54
55 WebInspector.TimelineOverviewPane.Events = {
56     WindowChanged: "WindowChanged"
57 };
58
59 WebInspector.TimelineOverviewPane.prototype = {
60     wasShown: function()
61     {
62         this._update();
63     },
64
65     onResize: function()
66     {
67         this._update();
68     },
69
70     /**
71      * @param {!WebInspector.TimelineOverviewBase} overviewControl
72      */
73     setOverviewControl: function(overviewControl)
74     {
75         if (this._overviewControl === overviewControl)
76             return;
77         if (this._overviewControl)
78             this._overviewControl.detach();
79         this._overviewControl = overviewControl;
80         this._overviewControl.show(this._overviewGrid.element);
81         this._update();
82     },
83
84     _update: function()
85     {
86         delete this._refreshTimeout;
87
88         this._overviewCalculator.setWindow(this._model.minimumRecordTime(), this._model.maximumRecordTime());
89         this._overviewCalculator.setDisplayWindow(0, this._overviewGrid.clientWidth());
90
91         if (this._overviewControl)
92             this._overviewControl.update();
93         this._overviewGrid.updateDividers(this._overviewCalculator);
94         this._updateEventDividers();
95     },
96
97     _updateEventDividers: function()
98     {
99         var records = this._eventDividers;
100         this._overviewGrid.removeEventDividers();
101         var dividers = [];
102         for (var i = 0; i < records.length; ++i) {
103             var record = records[i];
104             var positions = this._overviewCalculator.computeBarGraphPercentages(record);
105             var dividerPosition = Math.round(positions.start * 10);
106             if (dividers[dividerPosition])
107                 continue;
108             var divider = WebInspector.TimelinePresentationModel.createEventDivider(record.type);
109             divider.style.left = positions.start + "%";
110             dividers[dividerPosition] = divider;
111         }
112         this._overviewGrid.addEventDividers(dividers);
113     },
114
115     _onRecordAdded: function(event)
116     {
117         var record = event.data;
118         var eventDividers = this._eventDividers;
119         function addEventDividers(record)
120         {
121             if (WebInspector.TimelinePresentationModel.isEventDivider(record))
122                 eventDividers.push(record);
123         }
124         WebInspector.TimelinePresentationModel.forAllRecords([record], addEventDividers);
125         this._scheduleRefresh();
126     },
127
128     _reset: function()
129     {
130         this._overviewCalculator.reset();
131         this._overviewGrid.reset();
132         this._overviewGrid.setResizeEnabled(false);
133         this._eventDividers = [];
134         this._overviewGrid.updateDividers(this._overviewCalculator);
135         if (this._overviewControl)
136             this._overviewControl.reset();
137         this._update();
138     },
139
140     /**
141      * @return {number}
142      */
143     windowLeft: function()
144     {
145         return this._overviewGrid.windowLeft();
146     },
147
148     /**
149      * @return {number}
150      */
151     windowRight: function()
152     {
153         return this._overviewGrid.windowRight();
154     },
155
156     _onWindowChanged: function()
157     {
158         if (this._ignoreWindowChangedEvent)
159             return;
160         this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.WindowChanged);
161     },
162
163     /**
164      * @param {number} left
165      * @param {number} right
166      */
167     setWindow: function(left, right)
168     {
169         this._ignoreWindowChangedEvent = true;
170         this._overviewGrid.setWindow(left, right);
171         this._overviewGrid.setResizeEnabled(this._model.records.length);
172         this._ignoreWindowChangedEvent = false;
173     },
174
175     _scheduleRefresh: function()
176     {
177         if (this._refreshTimeout)
178             return;
179         if (!this.isShowing())
180             return;
181         this._refreshTimeout = setTimeout(this._update.bind(this), 300);
182     },
183
184     __proto__: WebInspector.View.prototype
185 }
186
187 /**
188  * @constructor
189  * @implements {WebInspector.TimelineGrid.Calculator}
190  */
191 WebInspector.TimelineOverviewCalculator = function()
192 {
193 }
194
195 WebInspector.TimelineOverviewCalculator.prototype = {
196     /**
197      * @param {number} time
198      * @return {number}
199      */
200     computePosition: function(time)
201     {
202         return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea + this.paddingLeft;
203     },
204
205     /**
206      * @return {!{start: number, end: number}}
207      */
208     computeBarGraphPercentages: function(record)
209     {
210         var start = (WebInspector.TimelineModel.startTimeInSeconds(record) - this._minimumBoundary) / this.boundarySpan() * 100;
211         var end = (WebInspector.TimelineModel.endTimeInSeconds(record) - this._minimumBoundary) / this.boundarySpan() * 100;
212         return {start: start, end: end};
213     },
214
215     /**
216      * @param {number=} minimum
217      * @param {number=} maximum
218      */
219     setWindow: function(minimum, maximum)
220     {
221         this._minimumBoundary = minimum >= 0 ? minimum : undefined;
222         this._maximumBoundary = maximum >= 0 ? maximum : undefined;
223     },
224
225     /**
226      * @param {number} paddingLeft
227      * @param {number} clientWidth
228      */
229     setDisplayWindow: function(paddingLeft, clientWidth)
230     {
231         this._workingArea = clientWidth - paddingLeft;
232         this.paddingLeft = paddingLeft;
233     },
234
235     reset: function()
236     {
237         this.setWindow();
238     },
239
240     /**
241      * @param {number} value
242      * @param {boolean=} hires
243      * @return {string}
244      */
245     formatTime: function(value, hires)
246     {
247         return Number.secondsToString(value, hires);
248     },
249
250     /**
251      * @return {number}
252      */
253     maximumBoundary: function()
254     {
255         return this._maximumBoundary;
256     },
257
258     /**
259      * @return {number}
260      */
261     minimumBoundary: function()
262     {
263         return this._minimumBoundary;
264     },
265
266     /**
267      * @return {number}
268      */
269     zeroTime: function()
270     {
271         return this._minimumBoundary;
272     },
273
274     /**
275      * @return {number}
276      */
277     boundarySpan: function()
278     {
279         return this._maximumBoundary - this._minimumBoundary;
280     }
281 }
282
283 /**
284  * @constructor
285  * @extends {WebInspector.View}
286  * @param {!WebInspector.TimelineModel} model
287  */
288 WebInspector.TimelineOverviewBase = function(model)
289 {
290     WebInspector.View.call(this);
291
292     this._model = model;
293     this._canvas = this.element.createChild("canvas", "fill");
294     this._context = this._canvas.getContext("2d");
295 }
296
297 WebInspector.TimelineOverviewBase.prototype = {
298     update: function() { },
299     reset: function() { },
300
301     /**
302      * @param {number} windowLeft
303      * @param {number} windowRight
304      * @return {!{startTime: number, endTime: number}}
305      */
306     windowTimes: function(windowLeft, windowRight)
307     {
308         var absoluteMin = this._model.minimumRecordTime();
309         var timeSpan = this._model.maximumRecordTime() - absoluteMin;
310         return {
311             startTime: absoluteMin + timeSpan * windowLeft,
312             endTime: absoluteMin + timeSpan * windowRight
313         };
314     },
315
316     /**
317      * @param {number} startTime
318      * @param {number} endTime
319      * @return {!{left: number, right: number}}
320      */
321     windowBoundaries: function(startTime, endTime)
322     {
323         var absoluteMin = this._model.minimumRecordTime();
324         var timeSpan = this._model.maximumRecordTime() - absoluteMin;
325         var haveRecords = absoluteMin >= 0;
326         return {
327             left: haveRecords && startTime ? Math.min((startTime - absoluteMin) / timeSpan, 1) : 0,
328             right: haveRecords && endTime < Infinity ? (endTime - absoluteMin) / timeSpan : 1
329         }
330     },
331
332     resetCanvas: function()
333     {
334         this._canvas.width = this.element.clientWidth * window.devicePixelRatio;
335         this._canvas.height = this.element.clientHeight * window.devicePixelRatio;
336     },
337
338     __proto__: WebInspector.View.prototype
339 }