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.Object} 34 * @param {!TreeOutline} treeOutline 35 */ 36 WebInspector.LayerTreeOutline = function(treeOutline) 37 { 38 WebInspector.Object.call(this); 39 this._treeOutline = treeOutline; 40 this._treeOutline.childrenListElement.addEventListener("mousemove", this._onMouseMove.bind(this), false); 41 this._treeOutline.childrenListElement.addEventListener("mouseout", this._onMouseMove.bind(this), false); 42 this._treeOutline.childrenListElement.addEventListener("contextmenu", this._onContextMenu.bind(this), true); 43 this._lastHoveredNode = null; 44 } 45 46 /** 47 * @enum {string} 48 */ 49 WebInspector.LayerTreeOutline.Events = { 50 LayerHovered: "LayerHovered", 51 LayerSelected: "LayerSelected" 52 } 53 54 WebInspector.LayerTreeOutline.prototype = { 55 /** 56 * @param {?WebInspector.Layer} layer 57 */ 58 selectLayer: function(layer) 59 { 60 this.hoverLayer(null); 61 var node = layer && this._treeOutline.getCachedTreeElement(layer); 62 if (node) 63 node.revealAndSelect(true); 64 else if (this._treeOutline.selectedTreeElement) 65 this._treeOutline.selectedTreeElement.deselect(); 66 }, 67 68 /** 69 * @param {?WebInspector.Layer} layer 70 */ 71 hoverLayer: function(layer) 72 { 73 var node = layer && this._treeOutline.getCachedTreeElement(layer); 74 if (node === this._lastHoveredNode) 75 return; 76 if (this._lastHoveredNode) 77 this._lastHoveredNode.setHovered(false); 78 if (node) 79 node.setHovered(true); 80 this._lastHoveredNode = node; 81 }, 82 83 /** 84 * @param {?WebInspector.LayerTreeBase} layerTree 85 */ 86 update: function(layerTree) 87 { 88 var seenLayers = new Map(); 89 90 /** 91 * @param {!WebInspector.Layer} layer 92 * @this {WebInspector.LayerTreeOutline} 93 */ 94 function updateLayer(layer) 95 { 96 if (seenLayers.get(layer)) 97 console.assert(false, "Duplicate layer: " + layer.id()); 98 seenLayers.put(layer, true); 99 var node = this._treeOutline.getCachedTreeElement(layer); 100 var parent = layer === layerTree.contentRoot() ? this._treeOutline : this._treeOutline.getCachedTreeElement(layer.parent()); 101 if (!parent) 102 console.assert(false, "Parent is not in the tree"); 103 if (!node) { 104 node = new WebInspector.LayerTreeElement(this, layer); 105 parent.appendChild(node); 106 } else { 107 if (node.parent !== parent) { 108 node.parent.removeChild(node); 109 parent.appendChild(node); 110 } 111 node._update(); 112 } 113 } 114 if (layerTree && layerTree.contentRoot()) 115 layerTree.forEachLayer(updateLayer.bind(this), layerTree.contentRoot()); 116 // Cleanup layers that don't exist anymore from tree. 117 for (var node = /** @type {!TreeElement|!TreeOutline|null} */ (this._treeOutline.children[0]); node && !node.root;) { 118 if (seenLayers.get(node.representedObject)) { 119 node = node.traverseNextTreeElement(false); 120 } else { 121 var nextNode = node.nextSibling || node.parent; 122 node.parent.removeChild(node); 123 if (node === this._lastHoveredNode) 124 this._lastHoveredNode = null; 125 node = nextNode; 126 } 127 } 128 }, 129 130 /** 131 * @param {?Event} event 132 */ 133 _onMouseMove: function(event) 134 { 135 var node = this._treeOutline.treeElementFromPoint(event.pageX, event.pageY); 136 if (node === this._lastHoveredNode) 137 return; 138 this.dispatchEventToListeners(WebInspector.LayerTreeOutline.Events.LayerHovered, node && node.representedObject ? {layer: node.representedObject} : null); 139 }, 140 141 /** 142 * @param {!WebInspector.LayerTreeElement} node 143 */ 144 _selectedNodeChanged: function(node) 145 { 146 var layer = /** @type {!WebInspector.Layer} */ (node.representedObject); 147 this.dispatchEventToListeners(WebInspector.LayerTreeOutline.Events.LayerSelected, {layer: layer}); 148 }, 149 150 /** 151 * @param {?Event} event 152 */ 153 _onContextMenu: function(event) 154 { 155 var node = this._treeOutline.treeElementFromPoint(event.pageX, event.pageY); 156 if (!node || !node.representedObject) 157 return; 158 var layer = /** @type {!WebInspector.Layer} */ (node.representedObject); 159 if (!layer) 160 return; 161 var domNode = layer.nodeForSelfOrAncestor(); 162 if (!domNode) 163 return; 164 var contextMenu = new WebInspector.ContextMenu(event); 165 contextMenu.appendApplicableItems(domNode); 166 contextMenu.show(); 167 }, 168 169 __proto__: WebInspector.Object.prototype 170 } 171 172 /** 173 * @constructor 174 * @param {!WebInspector.LayerTreeOutline} tree 175 * @param {!WebInspector.Layer} layer 176 * @extends {TreeElement} 177 */ 178 WebInspector.LayerTreeElement = function(tree, layer) 179 { 180 TreeElement.call(this, "", layer); 181 this._treeOutline = tree; 182 this._update(); 183 } 184 185 WebInspector.LayerTreeElement.prototype = { 186 onattach: function() 187 { 188 var selection = document.createElement("div"); 189 selection.className = "selection"; 190 this.listItemElement.insertBefore(selection, this.listItemElement.firstChild); 191 }, 192 193 _update: function() 194 { 195 var layer = /** @type {!WebInspector.Layer} */ (this.representedObject); 196 var node = layer.nodeForSelfOrAncestor(); 197 var title = document.createDocumentFragment(); 198 title.createChild("div", "selection"); 199 title.appendChild(document.createTextNode(node ? WebInspector.DOMPresentationUtils.simpleSelector(node) : "#" + layer.id())); 200 var details = title.createChild("span", "dimmed"); 201 details.textContent = WebInspector.UIString(" (%d %d)", layer.width(), layer.height()); 202 this.title = title; 203 }, 204 205 /** 206 * @override 207 * @return {boolean} 208 */ 209 onselect: function() 210 { 211 this._treeOutline._selectedNodeChanged(this); 212 return false; 213 }, 214 215 /** 216 * @param {boolean} hovered 217 */ 218 setHovered: function(hovered) 219 { 220 this.listItemElement.classList.toggle("hovered", hovered); 221 }, 222 223 __proto__: TreeElement.prototype 224 } 225