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