Web Inspector: Support passing extra arguments to ContentViews during construction
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / LayoutTimelineView.js
1 /*
2  * Copyright (C) 2014, 2015 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.LayoutTimelineView = function(timeline, extraArguments)
27 {
28     WebInspector.TimelineView.call(this, timeline, extraArguments);
29
30     console.assert(timeline.type === WebInspector.TimelineRecord.Type.Layout);
31
32     this.navigationSidebarTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
33     this.navigationSidebarTreeOutline.element.classList.add(WebInspector.LayoutTimelineView.TreeOutlineStyleClassName);
34
35     var columns = {eventType: {}, location: {}, width: {}, height: {}, startTime: {}, totalTime: {}};
36
37     columns.eventType.title = WebInspector.UIString("Type");
38     columns.eventType.width = "15%";
39
40     var typeToLabelMap = new Map;
41     for (var key in WebInspector.LayoutTimelineRecord.EventType) {
42         var value = WebInspector.LayoutTimelineRecord.EventType[key];
43         typeToLabelMap.set(value, WebInspector.LayoutTimelineRecord.displayNameForEventType(value));
44     }
45
46     columns.eventType.scopeBar = WebInspector.TimelineDataGrid.createColumnScopeBar("layout", typeToLabelMap);
47     columns.eventType.hidden = true;
48
49     columns.location.title = WebInspector.UIString("Initiator");
50     columns.location.width = "25%";
51
52     columns.width.title = WebInspector.UIString("Width");
53     columns.width.width = "8%";
54
55     columns.height.title = WebInspector.UIString("Height");
56     columns.height.width = "8%";
57
58     columns.startTime.title = WebInspector.UIString("Start Time");
59     columns.startTime.width = "8%";
60     columns.startTime.aligned = "right";
61
62     columns.totalTime.title = WebInspector.UIString("Duration");
63     columns.totalTime.width = "8%";
64     columns.totalTime.aligned = "right";
65
66     for (var column in columns)
67         columns[column].sortable = true;
68
69     this._dataGrid = new WebInspector.LayoutTimelineDataGrid(this.navigationSidebarTreeOutline, columns);
70     this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
71     this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
72
73     this._dataGrid.sortColumnIdentifier = "startTime";
74     this._dataGrid.sortOrder = WebInspector.DataGrid.SortOrder.Ascending;
75
76     this._hoveredTreeElement = null;
77     this._hoveredDataGridNode = null;
78     this._showingHighlight = false;
79     this._showingHighlightForRecord = null;
80
81     this._dataGrid.element.addEventListener("mouseover", this._mouseOverDataGrid.bind(this));
82     this._dataGrid.element.addEventListener("mouseleave", this._mouseLeaveDataGrid.bind(this));
83     this.navigationSidebarTreeOutline.element.addEventListener("mouseover", this._mouseOverTreeOutline.bind(this));
84     this.navigationSidebarTreeOutline.element.addEventListener("mouseleave", this._mouseLeaveTreeOutline.bind(this));
85
86     this.element.classList.add(WebInspector.LayoutTimelineView.StyleClassName);
87     this.element.appendChild(this._dataGrid.element);
88
89     timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._layoutTimelineRecordAdded, this);
90
91     this._pendingRecords = [];
92 };
93
94 WebInspector.LayoutTimelineView.StyleClassName = "layout";
95 WebInspector.LayoutTimelineView.TreeOutlineStyleClassName = "layout";
96
97 WebInspector.LayoutTimelineView.prototype = {
98     constructor: WebInspector.LayoutTimelineView,
99     __proto__: WebInspector.TimelineView.prototype,
100
101     // Public
102
103     get navigationSidebarTreeOutlineLabel()
104     {
105         return WebInspector.UIString("Records");
106     },
107
108     shown: function()
109     {
110         WebInspector.ContentView.prototype.shown.call(this);
111
112         this._updateHighlight();
113
114         this._dataGrid.shown();
115     },
116
117     hidden: function()
118     {
119         this._hideHighlightIfNeeded();
120
121         this._dataGrid.hidden();
122
123         WebInspector.ContentView.prototype.hidden.call(this);
124     },
125
126     filterDidChange: function()
127     {
128         WebInspector.TimelineView.prototype.filterDidChange.call(this);
129
130         this._updateHighlight();
131     },
132
133     updateLayout: function()
134     {
135         WebInspector.TimelineView.prototype.updateLayout.call(this);
136
137         this._dataGrid.updateLayout();
138
139         this._processPendingRecords();
140     },
141
142     matchTreeElementAgainstCustomFilters: function(treeElement)
143     {
144         return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement);
145     },
146
147     reset: function()
148     {
149         WebInspector.TimelineView.prototype.reset.call(this);
150
151         this._hideHighlightIfNeeded();
152
153         this._dataGrid.reset();
154     },
155
156     // Protected
157
158     treeElementPathComponentSelected: function(event)
159     {
160         var dataGridNode = this._dataGrid.dataGridNodeForTreeElement(event.data.pathComponent.generalTreeElement);
161         if (!dataGridNode)
162             return;
163         dataGridNode.revealAndSelect();
164     },
165
166     treeElementDeselected: function(treeElement)
167     {
168         WebInspector.TimelineView.prototype.treeElementDeselected.call(this, treeElement);
169
170         this._updateHighlight();
171     },
172
173     treeElementSelected: function(treeElement, selectedByUser)
174     {
175         if (this._dataGrid.shouldIgnoreSelectionEvent())
176             return;
177
178         WebInspector.TimelineView.prototype.treeElementSelected.call(this, treeElement, selectedByUser);
179
180         this._updateHighlight();
181     },
182
183     // Private
184
185     _processPendingRecords: function()
186     {
187         if (!this._pendingRecords.length)
188             return;
189
190         for (var layoutTimelineRecord of this._pendingRecords) {
191             var treeElement = new WebInspector.TimelineRecordTreeElement(layoutTimelineRecord, WebInspector.SourceCodeLocation.NameStyle.Short);
192             var dataGridNode = new WebInspector.LayoutTimelineDataGridNode(layoutTimelineRecord, this.zeroTime);
193
194             this._dataGrid.addRowInSortOrder(treeElement, dataGridNode);
195         }
196
197         this._pendingRecords = [];
198     },
199
200     _layoutTimelineRecordAdded: function(event)
201     {
202         var layoutTimelineRecord = event.data.record;
203         console.assert(layoutTimelineRecord instanceof WebInspector.LayoutTimelineRecord);
204
205         this._pendingRecords.push(layoutTimelineRecord);
206
207         this.needsLayout();
208     },
209
210     _dataGridFiltersDidChange: function(event)
211     {
212         this.timelineSidebarPanel.updateFilter();
213     },
214
215     _dataGridNodeSelected: function(event)
216     {
217         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
218     },
219
220     _updateHighlight: function()
221     {
222         var record = this._hoveredOrSelectedRecord();
223         if (!record) {
224             this._hideHighlightIfNeeded();
225             return;
226         }
227
228         this._showHighlightForRecord(record);
229     },
230
231     _showHighlightForRecord: function(record)
232     {
233         if (this._showingHighlightForRecord === record)
234             return;
235
236         this._showingHighlightForRecord = record;
237
238         const contentColor = {r: 111, g: 168, b: 220, a: 0.66};
239         const outlineColor = {r: 255, g: 229, b: 153, a: 0.66};
240
241         var quad = record.quad;
242         if (quad && DOMAgent.highlightQuad) {
243             DOMAgent.highlightQuad(quad.toProtocol(), contentColor, outlineColor);
244             this._showingHighlight = true;
245             return;
246         }
247
248         // COMPATIBILITY (iOS 6): iOS 6 included Rect information instead of Quad information. Fallback to highlighting the rect.
249         var rect = record.rect;
250         if (rect) {
251             DOMAgent.highlightRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, contentColor, outlineColor);
252             this._showingHighlight = true;
253             return;
254         }
255
256         // This record doesn't have a highlight, so hide any existing highlight.
257         if (this._showingHighlight) {
258             this._showingHighlight = false;
259             DOMAgent.hideHighlight();
260         }
261     },
262
263     _hideHighlightIfNeeded: function()
264     {
265         this._showingHighlightForRecord = null;
266
267         if (this._showingHighlight) {
268             this._showingHighlight = false;
269             DOMAgent.hideHighlight();
270         }
271     },
272
273     _hoveredOrSelectedRecord: function()
274     {
275         if (this._hoveredDataGridNode)
276             return this._hoveredDataGridNode.record;
277
278         if (this._hoveredTreeElement)
279             return this._hoveredTreeElement.record;
280
281         if (this._dataGrid.selectedNode) {
282             var treeElement = this._dataGrid.treeElementForDataGridNode(this._dataGrid.selectedNode);
283             if (treeElement.revealed())
284                 return this._dataGrid.selectedNode.record;
285         }
286
287         return null;
288     },
289
290     _mouseOverDataGrid: function(event)
291     {
292         var hoveredDataGridNode = this._dataGrid.dataGridNodeFromNode(event.target);
293         if (!hoveredDataGridNode)
294             return;
295
296         this._hoveredDataGridNode = hoveredDataGridNode;
297         this._updateHighlight();
298     },
299
300     _mouseLeaveDataGrid: function(event)
301     {
302         this._hoveredDataGridNode = null;
303         this._updateHighlight();
304     },
305
306     _mouseOverTreeOutline: function(event)
307     {
308         var hoveredTreeElement = this.navigationSidebarTreeOutline.treeElementFromNode(event.target);
309         if (!hoveredTreeElement)
310             return;
311
312         this._hoveredTreeElement = hoveredTreeElement;
313         this._updateHighlight();
314     },
315
316     _mouseLeaveTreeOutline: function(event)
317     {
318         this._hoveredTreeElement = null;
319         this._updateHighlight();
320     }
321 };