display:inline on the tbody is causing the width of the iframe to be shrunk to the...
[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 = class LayoutTimelineView extends WebInspector.TimelineView
27 {
28     constructor(timeline, extraArguments)
29     {
30         super(timeline, extraArguments);
31
32         console.assert(timeline.type === WebInspector.TimelineRecord.Type.Layout, timeline);
33
34         let columns = {name: {}, location: {}, width: {}, height: {}, startTime: {}, totalTime: {}};
35
36         columns.name.title = WebInspector.UIString("Type");
37         columns.name.width = "15%";
38
39         var typeToLabelMap = new Map;
40         for (var key in WebInspector.LayoutTimelineRecord.EventType) {
41             var value = WebInspector.LayoutTimelineRecord.EventType[key];
42             typeToLabelMap.set(value, WebInspector.LayoutTimelineRecord.displayNameForEventType(value));
43         }
44
45         columns.name.scopeBar = WebInspector.TimelineDataGrid.createColumnScopeBar("layout", typeToLabelMap);
46         columns.name.disclosure = true;
47         columns.name.icon = true;
48
49         this._scopeBar = columns.name.scopeBar;
50
51         columns.location.title = WebInspector.UIString("Initiator");
52         columns.location.width = "25%";
53
54         columns.width.title = WebInspector.UIString("Width");
55         columns.width.width = "8%";
56
57         columns.height.title = WebInspector.UIString("Height");
58         columns.height.width = "8%";
59
60         columns.startTime.title = WebInspector.UIString("Start Time");
61         columns.startTime.width = "8%";
62         columns.startTime.aligned = "right";
63
64         columns.totalTime.title = WebInspector.UIString("Duration");
65         columns.totalTime.width = "8%";
66         columns.totalTime.aligned = "right";
67
68         for (var column in columns)
69             columns[column].sortable = true;
70
71         this._dataGrid = new WebInspector.LayoutTimelineDataGrid(columns);
72         this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
73         this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
74
75         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("layout-timeline-view-sort", "startTime");
76         this._dataGrid.sortOrderSetting = new WebInspector.Setting("layout-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
77
78         this._hoveredTreeElement = null;
79         this._hoveredDataGridNode = null;
80         this._showingHighlight = false;
81         this._showingHighlightForRecord = null;
82
83         this._dataGrid.element.addEventListener("mouseover", this._mouseOverDataGrid.bind(this));
84         this._dataGrid.element.addEventListener("mouseleave", this._mouseLeaveDataGrid.bind(this));
85
86         this.element.classList.add("layout");
87         this.addSubview(this._dataGrid);
88
89         timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._layoutTimelineRecordAdded, this);
90
91         this._pendingRecords = [];
92     }
93
94     // Public
95
96     get selectionPathComponents()
97     {
98         let dataGridNode = this._dataGrid.selectedNode;
99         if (!dataGridNode || dataGridNode.hidden)
100             return null;
101
102         let pathComponents = [];
103
104         while (dataGridNode && !dataGridNode.root) {
105             console.assert(dataGridNode instanceof WebInspector.TimelineDataGridNode);
106             if (dataGridNode.hidden)
107                 return null;
108
109             let pathComponent = new WebInspector.TimelineDataGridNodePathComponent(dataGridNode);
110             pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this.dataGridNodePathComponentSelected, this);
111             pathComponents.unshift(pathComponent);
112             dataGridNode = dataGridNode.parent;
113         }
114
115         return pathComponents;
116     }
117
118     shown()
119     {
120         super.shown();
121
122         this._updateHighlight();
123
124         this._dataGrid.shown();
125     }
126
127     hidden()
128     {
129         this._hideHighlightIfNeeded();
130
131         this._dataGrid.hidden();
132
133         super.hidden();
134     }
135
136     closed()
137     {
138         console.assert(this.representedObject instanceof WebInspector.Timeline);
139         this.representedObject.removeEventListener(null, null, this);
140
141         this._dataGrid.closed();
142     }
143
144     filterDidChange()
145     {
146         super.filterDidChange();
147
148         this._updateHighlight();
149     }
150
151     matchTreeElementAgainstCustomFilters(treeElement)
152     {
153         return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement);
154     }
155
156     reset()
157     {
158         super.reset();
159
160         this._hideHighlightIfNeeded();
161
162         this._dataGrid.reset();
163
164         this._pendingRecords = [];
165     }
166
167     // Protected
168
169     dataGridNodePathComponentSelected(event)
170     {
171         let dataGridNode = event.data.pathComponent.timelineDataGridNode;
172         console.assert(dataGridNode.dataGrid === this._dataGrid);
173
174         dataGridNode.revealAndSelect();
175     }
176
177     treeElementDeselected(treeElement)
178     {
179         super.treeElementDeselected(treeElement);
180
181         this._updateHighlight();
182     }
183
184     treeElementSelected(treeElement, selectedByUser)
185     {
186         if (this._dataGrid.shouldIgnoreSelectionEvent())
187             return;
188
189         super.treeElementSelected(treeElement, selectedByUser);
190
191         this._updateHighlight();
192     }
193
194     layout()
195     {
196         this._processPendingRecords();
197     }
198
199     // Private
200
201     _processPendingRecords()
202     {
203         if (!this._pendingRecords.length)
204             return;
205
206         for (var layoutTimelineRecord of this._pendingRecords) {
207             let dataGridNode = new WebInspector.LayoutTimelineDataGridNode(layoutTimelineRecord, this.zeroTime);
208
209             this._dataGrid.addRowInSortOrder(null, dataGridNode);
210
211             let stack = [{children: layoutTimelineRecord.children, parentDataGridNode: dataGridNode, index: 0}];
212             while (stack.length) {
213                 let entry = stack.lastValue;
214                 if (entry.index >= entry.children.length) {
215                     stack.pop();
216                     continue;
217                 }
218
219                 let childRecord = entry.children[entry.index];
220                 console.assert(childRecord.type === WebInspector.TimelineRecord.Type.Layout, childRecord);
221
222                 let childDataGridNode = new WebInspector.LayoutTimelineDataGridNode(childRecord, this.zeroTime);
223                 console.assert(entry.parentDataGridNode, "entry without parent!");
224                 this._dataGrid.addRowInSortOrder(null, childDataGridNode, entry.parentDataGridNode);
225
226                 if (childDataGridNode && childRecord.children.length)
227                     stack.push({children: childRecord.children, parentDataGridNode: childDataGridNode, index: 0});
228                 ++entry.index;
229             }
230         }
231
232         this._pendingRecords = [];
233     }
234
235     _layoutTimelineRecordAdded(event)
236     {
237         var layoutTimelineRecord = event.data.record;
238         console.assert(layoutTimelineRecord instanceof WebInspector.LayoutTimelineRecord);
239
240         // Only add top-level records, to avoid processing child records multiple times.
241         if (layoutTimelineRecord.parent instanceof WebInspector.LayoutTimelineRecord)
242             return;
243
244         this._pendingRecords.push(layoutTimelineRecord);
245
246         this.needsLayout();
247     }
248
249     _dataGridFiltersDidChange(event)
250     {
251         // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
252     }
253
254     _dataGridNodeSelected(event)
255     {
256         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
257     }
258
259     _updateHighlight()
260     {
261         var record = this._hoveredOrSelectedRecord();
262         if (!record) {
263             this._hideHighlightIfNeeded();
264             return;
265         }
266
267         this._showHighlightForRecord(record);
268     }
269
270     _showHighlightForRecord(record)
271     {
272         if (this._showingHighlightForRecord === record)
273             return;
274
275         this._showingHighlightForRecord = record;
276
277         const contentColor = {r: 111, g: 168, b: 220, a: 0.66};
278         const outlineColor = {r: 255, g: 229, b: 153, a: 0.66};
279
280         var quad = record.quad;
281         if (quad) {
282             DOMAgent.highlightQuad(quad.toProtocol(), contentColor, outlineColor);
283             this._showingHighlight = true;
284             return;
285         }
286
287         // This record doesn't have a highlight, so hide any existing highlight.
288         if (this._showingHighlight) {
289             this._showingHighlight = false;
290             DOMAgent.hideHighlight();
291         }
292     }
293
294     _hideHighlightIfNeeded()
295     {
296         this._showingHighlightForRecord = null;
297
298         if (this._showingHighlight) {
299             this._showingHighlight = false;
300             DOMAgent.hideHighlight();
301         }
302     }
303
304     _hoveredOrSelectedRecord()
305     {
306         if (this._hoveredDataGridNode)
307             return this._hoveredDataGridNode.record;
308
309         if (this._dataGrid.selectedNode && this._dataGrid.selectedNode.revealed)
310             return this._dataGrid.selectedNode.record;
311
312         return null;
313     }
314
315     _mouseOverDataGrid(event)
316     {
317         var hoveredDataGridNode = this._dataGrid.dataGridNodeFromNode(event.target);
318         if (!hoveredDataGridNode)
319             return;
320
321         this._hoveredDataGridNode = hoveredDataGridNode;
322         this._updateHighlight();
323     }
324
325     _mouseLeaveDataGrid(event)
326     {
327         this._hoveredDataGridNode = null;
328         this._updateHighlight();
329     }
330 };