Web Inspector: Clicking a frame in the Rendering Frames timeline should select the...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / TimelineOverviewGraph.js
1 /*
2  * Copyright (C) 2014 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.TimelineOverviewGraph = function(timeline, timelineOverview)
27 {
28     if (this.constructor === WebInspector.TimelineOverviewGraph) {
29         // When instantiated directly return an instance of a type-based concrete subclass.
30
31         console.assert(timeline && timeline instanceof WebInspector.Timeline);
32
33         var timelineType = timeline.type;
34         if (timelineType === WebInspector.TimelineRecord.Type.Network)
35             return new WebInspector.NetworkTimelineOverviewGraph(timeline, timelineOverview);
36
37         if (timelineType === WebInspector.TimelineRecord.Type.Layout)
38             return new WebInspector.LayoutTimelineOverviewGraph(timeline, timelineOverview);
39
40         if (timelineType === WebInspector.TimelineRecord.Type.Script)
41             return new WebInspector.ScriptTimelineOverviewGraph(timeline, timelineOverview);
42
43         if (timelineType === WebInspector.TimelineRecord.Type.RenderingFrame)
44             return new WebInspector.RenderingFrameTimelineOverviewGraph(timeline, timelineOverview);
45
46         throw Error("Can't make a graph for an unknown timeline.");
47     }
48
49     // Concrete object instantiation.
50     console.assert(this.constructor !== WebInspector.TimelineOverviewGraph && this instanceof WebInspector.TimelineOverviewGraph);
51
52     // FIXME: Convert this to a WebInspector.Object subclass, and call super().
53     // WebInspector.Object.call(this);
54
55     this.element = document.createElement("div");
56     this.element.classList.add("timeline-overview-graph");
57
58     this._zeroTime = 0;
59     this._startTime = 0;
60     this._endTime = 5;
61     this._currentTime = 0;
62     this._timelineOverview = timelineOverview;
63     this._selectedRecord = null;
64     this._selectedRecordChanged = false;
65     this._scheduledLayoutUpdateIdentifier = 0;
66     this._scheduledSelectedRecordLayoutUpdateIdentifier = 0;
67 };
68
69 WebInspector.TimelineOverviewGraph.Event = {
70     RecordSelected: "timeline-overview-graph-record-selected"
71 };
72
73 WebInspector.TimelineOverviewGraph.prototype = {
74     constructor: WebInspector.TimelineOverviewGraph,
75     __proto__: WebInspector.Object.prototype,
76
77     // Public
78
79     get zeroTime()
80     {
81         return this._zeroTime;
82     },
83
84     set zeroTime(x)
85     {
86         if (this._zeroTime === x)
87             return;
88
89         this._zeroTime = x || 0;
90
91         this.needsLayout();
92     },
93
94     get startTime()
95     {
96         return this._startTime;
97     },
98
99     set startTime(x)
100     {
101         if (this._startTime === x)
102             return;
103
104         this._startTime = x || 0;
105
106         this.needsLayout();
107     },
108
109     get endTime()
110     {
111         return this._endTime;
112     },
113
114     set endTime(x)
115     {
116         if (this._endTime === x)
117             return;
118
119         this._endTime = x || 0;
120
121         this.needsLayout();
122     },
123
124     get currentTime()
125     {
126         return this._currentTime;
127     },
128
129     set currentTime(x)
130     {
131         if (this._currentTime === x)
132             return;
133
134         var oldCurrentTime = this._currentTime;
135
136         this._currentTime = x || 0;
137
138         if ((this._startTime <= oldCurrentTime && oldCurrentTime <= this._endTime) || (this._startTime <= this._currentTime && this._currentTime <= this._endTime))
139             this.needsLayout();
140     },
141
142     get timelineOverview()
143     {
144         return this._timelineOverview;
145     },
146
147     get visible()
148     {
149         return this._visible;
150     },
151
152     get selectedRecord()
153     {
154         return this._selectedRecord;
155     },
156
157     set selectedRecord(x)
158     {
159         if (this._selectedRecord === x)
160             return;
161
162         this._selectedRecord = x;
163         this._selectedRecordChanged = true;
164
165         this._needsSelectedRecordLayout();
166     },
167
168     shown: function()
169     {
170         this._visible = true;
171         this.updateLayout();
172     },
173
174     hidden: function()
175     {
176         this._visible = false;
177     },
178
179     reset: function()
180     {
181         // Implemented by sub-classes if needed.
182     },
183
184     updateLayout: function()
185     {
186         if (this._scheduledLayoutUpdateIdentifier) {
187             cancelAnimationFrame(this._scheduledLayoutUpdateIdentifier);
188             this._scheduledLayoutUpdateIdentifier = 0;
189         }
190
191         // Implemented by sub-classes if needed.
192     },
193
194     updateLayoutIfNeeded: function()
195     {
196         if (!this._scheduledLayoutUpdateIdentifier)
197             return;
198         this.updateLayout();
199     },
200
201     // Protected
202
203     needsLayout: function()
204     {
205         if (!this._visible)
206             return;
207
208         if (this._scheduledLayoutUpdateIdentifier)
209             return;
210
211         this._scheduledLayoutUpdateIdentifier = requestAnimationFrame(this.updateLayout.bind(this));
212     },
213
214     dispatchSelectedRecordChangedEvent: function()
215     {
216         if (!this._selectedRecordChanged)
217             return;
218
219         this._selectedRecordChanged = false;
220
221         this.dispatchEventToListeners(WebInspector.TimelineOverviewGraph.Event.RecordSelected, {record: this.selectedRecord});
222     },
223
224     updateSelectedRecord: function()
225     {
226         // Implemented by sub-classes if needed.
227     },
228
229     // Private
230
231     _needsSelectedRecordLayout: function()
232     {
233         // If layout is scheduled, abort since the selected record will be updated when layout happens.
234         if (this._scheduledLayoutUpdateIdentifier)
235             return;
236
237         if (this._scheduledSelectedRecordLayoutUpdateIdentifier)
238             return;
239
240         function update()
241         {
242             this._scheduledSelectedRecordLayoutUpdateIdentifier = 0;
243
244             this.updateSelectedRecord();
245         }
246
247         this._scheduledSelectedRecordLayoutUpdateIdentifier = requestAnimationFrame(update.bind(this));
248     }
249 };