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