Fix an alignment issue with operationPushCatchScope on ARMv7
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / LayerTreeDetailsSidebarPanel.js
1 /*
2  * Copyright (C) 2013 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.LayerTreeDetailsSidebarPanel = function()
27 {
28     WebInspector.DOMDetailsSidebarPanel.call(this, "layer-tree", WebInspector.UIString("Layers"), WebInspector.UIString("Layer"), "Images/NavigationItemLayers.svg", "3");
29
30     this._dataGridNodesByLayerId = {};
31
32     this.element.classList.add(WebInspector.LayerTreeDetailsSidebarPanel.StyleClassName);
33
34     WebInspector.showShadowDOMSetting.addEventListener(WebInspector.Setting.Event.Changed, this._showShadowDOMSettingChanged, this);
35
36     window.addEventListener("resize", this._windowResized.bind(this));
37
38     this._buildLayerInfoSection();
39     this._buildDataGridSection();
40     this._buildBottomBar();
41 };
42
43 WebInspector.LayerTreeDetailsSidebarPanel.StyleClassName = "layer-tree";
44
45 WebInspector.LayerTreeDetailsSidebarPanel.prototype = {
46     constructor: WebInspector.LayerTreeDetailsSidebarPanel,
47     __proto__: WebInspector.DOMDetailsSidebarPanel.prototype,
48
49     // DetailsSidebarPanel Overrides.
50
51     shown: function()
52     {
53         WebInspector.layerTreeManager.addEventListener(WebInspector.LayerTreeManager.Event.LayerTreeDidChange, this._layerTreeDidChange, this);
54
55         console.assert(this.parentSidebar);
56
57         this.needsRefresh();
58
59         WebInspector.DOMDetailsSidebarPanel.prototype.shown.call(this);
60     },
61
62     hidden: function()
63     {
64         WebInspector.layerTreeManager.removeEventListener(WebInspector.LayerTreeManager.Event.LayerTreeDidChange, this._layerTreeDidChange, this);
65
66         WebInspector.DOMDetailsSidebarPanel.prototype.hidden.call(this);
67     },
68
69     refresh: function()
70     {
71         if (!this.domNode)
72             return;
73
74         WebInspector.layerTreeManager.layersForNode(this.domNode, function(layerForNode, childLayers) {
75             this._unfilteredChildLayers = childLayers;
76             this._updateDisplayWithLayers(layerForNode, childLayers);
77         }.bind(this));
78     },
79
80     // DOMDetailsSidebarPanel Overrides
81
82     supportsDOMNode: function(nodeToInspect)
83     {
84         return WebInspector.layerTreeManager.supported && nodeToInspect.nodeType() === Node.ELEMENT_NODE;
85     },
86
87     // Private
88
89     _layerTreeDidChange: function(event)
90     {
91         this.needsRefresh();
92     },
93
94     _showShadowDOMSettingChanged: function(event)
95     {
96         if (this.selected)
97             this._updateDisplayWithLayers(this._layerForNode, this._unfilteredChildLayers);
98     },
99
100     _windowResized: function(event)
101     {
102         if (this._popover && this._popover.visible)
103             this._updatePopoverForSelectedNode();
104     },
105
106     _buildLayerInfoSection: function()
107     {
108         var rows = this._layerInfoRows = {};
109         var rowsArray = [];
110
111         rowsArray.push(rows["Width"] = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Width")));
112         rowsArray.push(rows["Height"] = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Height")));
113         rowsArray.push(rows["Paints"] = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Paints")));
114         rowsArray.push(rows["Memory"] = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Memory")));
115
116         this._layerInfoGroup = new WebInspector.DetailsSectionGroup(rowsArray);
117
118         var emptyRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Layer Available"));
119         emptyRow.showEmptyMessage();
120         this._noLayerInformationGroup = new WebInspector.DetailsSectionGroup([emptyRow]);
121
122         this._layerInfoSection = new WebInspector.DetailsSection("layer-info", WebInspector.UIString("Layer Info"), [this._noLayerInformationGroup]);
123
124         this.contentElement.appendChild(this._layerInfoSection.element);
125     },
126
127     _buildDataGridSection: function()
128     {
129         var columns = {name: {}, paintCount: {}, memory: {}};
130
131         columns.name.title = WebInspector.UIString("Node");
132         columns.name.sortable = false;
133
134         columns.paintCount.title = WebInspector.UIString("Paints");
135         columns.paintCount.sortable = true;
136         columns.paintCount.aligned = "right";
137         columns.paintCount.width = "50px";
138
139         columns.memory.title = WebInspector.UIString("Memory");
140         columns.memory.sortable = true;
141         columns.memory.aligned = "right";
142         columns.memory.width = "70px";
143
144         this._dataGrid = new WebInspector.DataGrid(columns);
145         this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, this._sortDataGrid, this);
146         this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._selectedDataGridNodeChanged, this);
147
148         this.sortColumnIdentifier = "memory";
149         this.sortOrder = WebInspector.DataGrid.SortOrder.Descending;
150
151         var element = this._dataGrid.element;
152         element.classList.add("inline");
153         element.addEventListener("focus", this._dataGridGainedFocus.bind(this), false);
154         element.addEventListener("blur", this._dataGridLostFocus.bind(this), false);
155         element.addEventListener("click", this._dataGridWasClicked.bind(this), false);
156
157         this._childLayersRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Child Layers"));
158         var group = new WebInspector.DetailsSectionGroup([this._childLayersRow]);
159         var section = new WebInspector.DetailsSection("layer-children", WebInspector.UIString("Child Layers"), [group], null, true);
160
161         var element = this.contentElement.appendChild(section.element);
162         element.classList.add(section.identifier);
163     },
164
165     _buildBottomBar: function()
166     {
167         var bottomBar = this.element.appendChild(document.createElement("div"));
168         bottomBar.className = "bottom-bar";
169
170         this._layersCountLabel = bottomBar.appendChild(document.createElement("div"));
171         this._layersCountLabel.className = "layers-count-label";
172
173         this._layersMemoryLabel = bottomBar.appendChild(document.createElement("div"));
174         this._layersMemoryLabel.className = "layers-memory-label";
175     },
176
177     _sortDataGrid: function()
178     {
179         var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier;
180
181         function comparator(a, b)
182         {
183             var item1 = a.layer[sortColumnIdentifier] || 0;
184             var item2 = b.layer[sortColumnIdentifier] || 0;
185             return item1 - item2;
186         }
187
188         this._dataGrid.sortNodes(comparator);
189         this._updatePopoverForSelectedNode();
190     },
191
192     _selectedDataGridNodeChanged: function()
193     {
194         if (this._dataGrid.selectedNode) {
195             this._highlightSelectedNode();
196             this._showPopoverForSelectedNode();
197         } else {
198             WebInspector.domTreeManager.hideDOMNodeHighlight();
199             this._hidePopover();
200         }
201     },
202
203     _dataGridGainedFocus: function(event)
204     {
205         this._highlightSelectedNode();
206         this._showPopoverForSelectedNode();
207     },
208
209     _dataGridLostFocus: function(event)
210     {
211         WebInspector.domTreeManager.hideDOMNodeHighlight();
212         this._hidePopover();
213     },
214
215     _dataGridWasClicked: function(event)
216     {
217         if (this._dataGrid.selectedNode && event.target.parentNode.classList.contains("filler"))
218             this._dataGrid.selectedNode.deselect();
219     },
220
221     _highlightSelectedNode: function()
222     {
223         var dataGridNode = this._dataGrid.selectedNode;
224         if (!dataGridNode)
225             return;
226
227         var layer = dataGridNode.layer;
228         if (layer.isGeneratedContent || layer.isReflection || layer.isAnonymous)
229             WebInspector.domTreeManager.highlightRect(layer.bounds, true);
230         else
231             WebInspector.domTreeManager.highlightDOMNode(layer.nodeId);
232     },
233
234     _updateDisplayWithLayers: function(layerForNode, childLayers)
235     {
236         if (!WebInspector.showShadowDOMSetting.value) {
237             childLayers = childLayers.filter(function(layer) {
238                 return !layer.isInShadowTree;
239             });
240         }
241
242         this._updateLayerInfoSection(layerForNode);
243         this._updateDataGrid(layerForNode, childLayers);
244         this._updateMetrics(layerForNode, childLayers);
245
246         this._layerForNode = layerForNode;
247         this._childLayers = childLayers;
248     },
249
250     _updateLayerInfoSection: function(layer)
251     {
252         const emDash = "\u2014";
253
254         this._layerInfoSection.groups = layer ? [this._layerInfoGroup] : [this._noLayerInformationGroup];
255
256         if (!layer)
257             return;
258
259         this._layerInfoRows["Memory"].value = Number.bytesToString(layer.memory);
260         this._layerInfoRows["Width"].value = layer.compositedBounds.width + "px";
261         this._layerInfoRows["Height"].value = layer.compositedBounds.height + "px";
262         this._layerInfoRows["Paints"].value = layer.paintCount + "";
263     },
264
265     _updateDataGrid: function(layerForNode, childLayers)
266     {
267         var dataGrid = this._dataGrid;
268
269         var mutations = WebInspector.layerTreeManager.layerTreeMutations(this._childLayers, childLayers);
270
271         mutations.removals.forEach(function(layer) {
272             var node = this._dataGridNodesByLayerId[layer.layerId];
273             if (node) {
274                 dataGrid.removeChild(node);
275                 delete this._dataGridNodesByLayerId[layer.layerId];
276             }
277         }, this);
278
279         mutations.additions.forEach(function(layer) {
280             var node = this._dataGridNodeForLayer(layer);
281             if (node)
282                 dataGrid.appendChild(node);
283         }, this);
284
285         mutations.preserved.forEach(function(layer) {
286             var node = this._dataGridNodesByLayerId[layer.layerId];
287             if (node)
288                 node.layer = layer;
289         }, this);
290
291         this._sortDataGrid();
292
293         this._childLayersRow.dataGrid = !isEmptyObject(childLayers) ? this._dataGrid : null;
294     },
295
296     _dataGridNodeForLayer: function(layer)
297     {
298         var node = new WebInspector.LayerTreeDataGridNode(layer);
299
300         this._dataGridNodesByLayerId[layer.layerId] = node;
301
302         return node;
303     },
304
305     _updateMetrics: function(layerForNode, childLayers)
306     {
307         var layerCount = 0;
308         var totalMemory = 0;
309
310         if (layerForNode) {
311             layerCount++;
312             totalMemory += layerForNode.memory || 0;
313         }
314
315         childLayers.forEach(function(layer) {
316             layerCount++;
317             totalMemory += layer.memory || 0;
318         });
319
320         this._layersCountLabel.textContent = WebInspector.UIString("Layer Count: %d").format(layerCount);
321         this._layersMemoryLabel.textContent = WebInspector.UIString("Memory: %s").format(Number.bytesToString(totalMemory));
322     },
323
324     _showPopoverForSelectedNode: function()
325     {
326         var dataGridNode = this._dataGrid.selectedNode;
327         if (!dataGridNode)
328             return;
329
330         this._contentForPopover(dataGridNode.layer, function(content) {
331             if (dataGridNode === this._dataGrid.selectedNode)
332                 this._updatePopoverForSelectedNode(content);
333         }.bind(this));
334     },
335
336     _updatePopoverForSelectedNode: function(content)
337     {
338         var dataGridNode = this._dataGrid.selectedNode;
339         if (!dataGridNode)
340             return;
341
342         var popover = this._popover;
343         if (!popover)
344             popover = this._popover = new WebInspector.Popover;
345
346         var targetFrame = WebInspector.Rect.rectFromClientRect(dataGridNode.element.getBoundingClientRect());
347
348         if (content)
349             popover.content = content;
350
351         popover.present(targetFrame.pad(2), [WebInspector.RectEdge.MIN_X]);
352     },
353
354     _hidePopover: function()
355     {
356         if (this._popover)
357             this._popover.dismiss();
358     },
359
360     _contentForPopover: function(layer, callback)
361     {
362         var content = document.createElement("div");
363         content.className = "layer-tree-popover";
364
365         content.appendChild(document.createElement("p")).textContent = WebInspector.UIString("Reasons for compositing:");
366
367         var list = content.appendChild(document.createElement("ul"));
368
369         WebInspector.layerTreeManager.reasonsForCompositingLayer(layer, function(compositingReasons) {
370             if (isEmptyObject(compositingReasons)) {
371                 callback(content);
372                 return;
373             }
374
375             this._populateListOfCompositingReasons(list, compositingReasons);
376
377             callback(content);
378         }.bind(this));
379
380         return content;
381     },
382
383     _populateListOfCompositingReasons: function(list, compositingReasons)
384     {
385         function addReason(reason)
386         {
387             list.appendChild(document.createElement("li")).textContent = reason;
388         }
389
390         if (compositingReasons.transform3D)
391             addReason(WebInspector.UIString("Element has a 3D transform"));
392         if (compositingReasons.video)
393             addReason(WebInspector.UIString("Element is <video>"));
394         if (compositingReasons.canvas)
395             addReason(WebInspector.UIString("Element is <canvas>"));
396         if (compositingReasons.plugin)
397             addReason(WebInspector.UIString("Element is a plug-in"));
398         if (compositingReasons.iFrame)
399             addReason(WebInspector.UIString("Element is <iframe>"));
400         if (compositingReasons.backfaceVisibilityHidden)
401             addReason(WebInspector.UIString("Element has “backface-visibility: hidden” style"));
402         if (compositingReasons.clipsCompositingDescendants)
403             addReason(WebInspector.UIString("Element clips compositing descendants"));
404         if (compositingReasons.animation)
405             addReason(WebInspector.UIString("Element is animated"));
406         if (compositingReasons.filters)
407             addReason(WebInspector.UIString("Element has CSS filters applied"));
408         if (compositingReasons.positionFixed)
409             addReason(WebInspector.UIString("Element has “position: fixed” style"));
410         if (compositingReasons.positionSticky)
411             addReason(WebInspector.UIString("Element has “position: sticky” style"));
412         if (compositingReasons.overflowScrollingTouch)
413             addReason(WebInspector.UIString("Element has “-webkit-overflow-scrolling: touch” style"));
414         if (compositingReasons.stacking)
415             addReason(WebInspector.UIString("Element establishes a stacking context"));
416         if (compositingReasons.overlap)
417             addReason(WebInspector.UIString("Element overlaps other compositing element"));
418         if (compositingReasons.negativeZIndexChildren)
419             addReason(WebInspector.UIString("Element has children with a negative z-index"));
420         if (compositingReasons.transformWithCompositedDescendants)
421             addReason(WebInspector.UIString("Element has a 2D transform and composited descendants"));
422         if (compositingReasons.opacityWithCompositedDescendants)
423             addReason(WebInspector.UIString("Element has opacity applied and composited descendants"));
424         if (compositingReasons.maskWithCompositedDescendants)
425             addReason(WebInspector.UIString("Element is masked and composited descendants"));
426         if (compositingReasons.reflectionWithCompositedDescendants)
427             addReason(WebInspector.UIString("Element has a reflection and composited descendants"));
428         if (compositingReasons.filterWithCompositedDescendants)
429             addReason(WebInspector.UIString("Element has CSS filters applied and composited descendants"));
430         if (compositingReasons.blendingWithCompositedDescendants)
431             addReason(WebInspector.UIString("Element has CSS blending applied and composited descendants"));
432         if (compositingReasons.isolatesCompositedBlendingDescendants)
433             addReason(WebInspector.UIString("Element is a stacking context and has composited descendants with CSS blending applied"));
434         if (compositingReasons.perspective)
435             addReason(WebInspector.UIString("Element has perspective applied"));
436         if (compositingReasons.preserve3D)
437             addReason(WebInspector.UIString("Element has “transform-style: preserve-3d” style"));
438         if (compositingReasons.root)
439             addReason(WebInspector.UIString("Element is the root element"));
440         if (compositingReasons.blending)
441             addReason(WebInspector.UIString("Element has “blend-mode” style"));
442     }
443 };