ElementsTreeOutline.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. export default class ElementsTreeOutline extends UI.TreeOutline{constructor(omitRootDOMNode,selectEnabled,hideGutter){super();this._treeElementSymbol=Symbol('treeElement');const shadowContainer=createElement('div');this._shadowRoot=UI.createShadowRootWithCoreStyles(shadowContainer,'elements/elementsTreeOutline.css');const outlineDisclosureElement=this._shadowRoot.createChild('div','elements-disclosure');this._element=this.element;this._element.classList.add('elements-tree-outline','source-code');if(hideGutter){this._element.classList.add('elements-hide-gutter');}
  2. UI.ARIAUtils.setAccessibleName(this._element,Common.UIString('Page DOM'));this._element.addEventListener('focusout',this._onfocusout.bind(this),false);this._element.addEventListener('mousedown',this._onmousedown.bind(this),false);this._element.addEventListener('mousemove',this._onmousemove.bind(this),false);this._element.addEventListener('mouseleave',this._onmouseleave.bind(this),false);this._element.addEventListener('dragstart',this._ondragstart.bind(this),false);this._element.addEventListener('dragover',this._ondragover.bind(this),false);this._element.addEventListener('dragleave',this._ondragleave.bind(this),false);this._element.addEventListener('drop',this._ondrop.bind(this),false);this._element.addEventListener('dragend',this._ondragend.bind(this),false);this._element.addEventListener('contextmenu',this._contextMenuEventFired.bind(this),false);this._element.addEventListener('clipboard-beforecopy',this._onBeforeCopy.bind(this),false);this._element.addEventListener('clipboard-copy',this._onCopyOrCut.bind(this,false),false);this._element.addEventListener('clipboard-cut',this._onCopyOrCut.bind(this,true),false);this._element.addEventListener('clipboard-paste',this._onPaste.bind(this),false);this._element.addEventListener('keydown',this._onKeyDown.bind(this),false);outlineDisclosureElement.appendChild(this._element);this.element=shadowContainer;this._includeRootDOMNode=!omitRootDOMNode;this._selectEnabled=selectEnabled;this._rootDOMNode=null;this._selectedDOMNode=null;this._visible=false;this._popoverHelper=new UI.PopoverHelper(this._element,this._getPopoverRequest.bind(this));this._popoverHelper.setHasPadding(true);this._popoverHelper.setTimeout(0,100);this._updateRecords=new Map();this._treeElementsBeingUpdated=new Set();this._showHTMLCommentsSetting=Common.moduleSetting('showHTMLComments');this._showHTMLCommentsSetting.addChangeListener(this._onShowHTMLCommentsChange.bind(this));this.useLightSelectionColor();}
  3. static forDOMModel(domModel){return domModel[ElementsTreeOutline._treeOutlineSymbol]||null;}
  4. _onShowHTMLCommentsChange(){const selectedNode=this.selectedDOMNode();if(selectedNode&&selectedNode.nodeType()===Node.COMMENT_NODE&&!this._showHTMLCommentsSetting.get()){this.selectDOMNode(selectedNode.parentNode);}
  5. this.update();}
  6. treeElementSymbol(){return this._treeElementSymbol;}
  7. setWordWrap(wrap){this._element.classList.toggle('elements-tree-nowrap',!wrap);}
  8. setMultilineEditing(multilineEditing){this._multilineEditing=multilineEditing;}
  9. visibleWidth(){return this._visibleWidth;}
  10. setVisibleWidth(width){this._visibleWidth=width;if(this._multilineEditing){this._multilineEditing.resize();}}
  11. _setClipboardData(data){if(this._clipboardNodeData){const treeElement=this.findTreeElement(this._clipboardNodeData.node);if(treeElement){treeElement.setInClipboard(false);}
  12. delete this._clipboardNodeData;}
  13. if(data){const treeElement=this.findTreeElement(data.node);if(treeElement){treeElement.setInClipboard(true);}
  14. this._clipboardNodeData=data;}}
  15. resetClipboardIfNeeded(removedNode){if(this._clipboardNodeData&&this._clipboardNodeData.node===removedNode){this._setClipboardData(null);}}
  16. _onBeforeCopy(event){event.handled=true;}
  17. _onCopyOrCut(isCut,event){this._setClipboardData(null);const originalEvent=event['original'];if(originalEvent.target.hasSelection()){return;}
  18. if(UI.isEditing()){return;}
  19. const targetNode=this.selectedDOMNode();if(!targetNode){return;}
  20. originalEvent.clipboardData.clearData();event.handled=true;this.performCopyOrCut(isCut,targetNode);}
  21. performCopyOrCut(isCut,node){if(isCut&&(node.isShadowRoot()||node.ancestorUserAgentShadowRoot())){return;}
  22. node.copyNode();this._setClipboardData({node:node,isCut:isCut});}
  23. canPaste(targetNode){if(targetNode.isShadowRoot()||targetNode.ancestorUserAgentShadowRoot()){return false;}
  24. if(!this._clipboardNodeData){return false;}
  25. const node=this._clipboardNodeData.node;if(this._clipboardNodeData.isCut&&(node===targetNode||node.isAncestor(targetNode))){return false;}
  26. if(targetNode.domModel()!==node.domModel()){return false;}
  27. return true;}
  28. pasteNode(targetNode){if(this.canPaste(targetNode)){this._performPaste(targetNode);}}
  29. _onPaste(event){if(UI.isEditing()){return;}
  30. const targetNode=this.selectedDOMNode();if(!targetNode||!this.canPaste(targetNode)){return;}
  31. event.handled=true;this._performPaste(targetNode);}
  32. _performPaste(targetNode){if(this._clipboardNodeData.isCut){this._clipboardNodeData.node.moveTo(targetNode,null,expandCallback.bind(this));this._setClipboardData(null);}else{this._clipboardNodeData.node.copyTo(targetNode,null,expandCallback.bind(this));}
  33. function expandCallback(error,nodeId){if(error){return;}
  34. const pastedNode=targetNode.domModel().nodeForId(nodeId);if(!pastedNode){return;}
  35. this.selectDOMNode(pastedNode);}}
  36. setVisible(visible){if(visible===this._visible){return;}
  37. this._visible=visible;if(!this._visible){this._popoverHelper.hidePopover();if(this._multilineEditing){this._multilineEditing.cancel();}
  38. return;}
  39. this.runPendingUpdates();if(this._selectedDOMNode){this._revealAndSelectNode(this._selectedDOMNode,false);}}
  40. get rootDOMNode(){return this._rootDOMNode;}
  41. set rootDOMNode(x){if(this._rootDOMNode===x){return;}
  42. this._rootDOMNode=x;this._isXMLMimeType=x&&x.isXMLNode();this.update();}
  43. get isXMLMimeType(){return this._isXMLMimeType;}
  44. selectedDOMNode(){return this._selectedDOMNode;}
  45. selectDOMNode(node,focus){if(this._selectedDOMNode===node){this._revealAndSelectNode(node,!focus);return;}
  46. this._selectedDOMNode=node;this._revealAndSelectNode(node,!focus);if(this._selectedDOMNode===node){this._selectedNodeChanged(!!focus);}}
  47. editing(){const node=this.selectedDOMNode();if(!node){return false;}
  48. const treeElement=this.findTreeElement(node);if(!treeElement){return false;}
  49. return treeElement.isEditing()||false;}
  50. update(){const selectedNode=this.selectedDOMNode();this.removeChildren();if(!this.rootDOMNode){return;}
  51. if(this._includeRootDOMNode){const treeElement=this._createElementTreeElement(this.rootDOMNode);this.appendChild(treeElement);}else{const children=this._visibleChildren(this.rootDOMNode);for(const child of children){const treeElement=this._createElementTreeElement(child);this.appendChild(treeElement);}}
  52. if(selectedNode){this._revealAndSelectNode(selectedNode,true);}}
  53. _selectedNodeChanged(focus){this.dispatchEventToListeners(ElementsTreeOutline.Events.SelectedNodeChanged,{node:this._selectedDOMNode,focus:focus});}
  54. _fireElementsTreeUpdated(nodes){this.dispatchEventToListeners(ElementsTreeOutline.Events.ElementsTreeUpdated,nodes);}
  55. findTreeElement(node){let treeElement=this._lookUpTreeElement(node);if(!treeElement&&node.nodeType()===Node.TEXT_NODE){treeElement=this._lookUpTreeElement(node.parentNode);}
  56. return(treeElement);}
  57. _lookUpTreeElement(node){if(!node){return null;}
  58. const cachedElement=node[this._treeElementSymbol];if(cachedElement){return cachedElement;}
  59. const ancestors=[];let currentNode;for(currentNode=node.parentNode;currentNode;currentNode=currentNode.parentNode){ancestors.push(currentNode);if(currentNode[this._treeElementSymbol])
  60. {break;}}
  61. if(!currentNode){return null;}
  62. for(let i=ancestors.length-1;i>=0;--i){const child=ancestors[i-1]||node;const treeElement=ancestors[i][this._treeElementSymbol];if(treeElement){treeElement.onpopulate();if(child.index>=treeElement.expandedChildrenLimit()){this.setExpandedChildrenLimit(treeElement,child.index+1);}}}
  63. return node[this._treeElementSymbol];}
  64. createTreeElementFor(node){let treeElement=this.findTreeElement(node);if(treeElement){return treeElement;}
  65. if(!node.parentNode){return null;}
  66. treeElement=this.createTreeElementFor(node.parentNode);return treeElement?this._showChild(treeElement,node):null;}
  67. set suppressRevealAndSelect(x){if(this._suppressRevealAndSelect===x){return;}
  68. this._suppressRevealAndSelect=x;}
  69. _revealAndSelectNode(node,omitFocus){if(this._suppressRevealAndSelect){return;}
  70. if(!this._includeRootDOMNode&&node===this.rootDOMNode&&this.rootDOMNode){node=this.rootDOMNode.firstChild;}
  71. if(!node){return;}
  72. const treeElement=this.createTreeElementFor(node);if(!treeElement){return;}
  73. treeElement.revealAndSelect(omitFocus);}
  74. _treeElementFromEvent(event){const scrollContainer=this.element.parentElement;const x=scrollContainer.totalOffsetLeft()+scrollContainer.offsetWidth-36;const y=event.pageY;const elementUnderMouse=this.treeElementFromPoint(x,y);const elementAboveMouse=this.treeElementFromPoint(x,y-2);let element;if(elementUnderMouse===elementAboveMouse){element=elementUnderMouse;}else{element=this.treeElementFromPoint(x,y+2);}
  75. return element;}
  76. _getPopoverRequest(event){let link=event.target;while(link&&!link[Elements.ElementsTreeElement.HrefSymbol]){link=link.parentElementOrShadowHost();}
  77. if(!link){return null;}
  78. return{box:link.boxInWindow(),show:async popover=>{const listItem=link.enclosingNodeOrSelfWithNodeName('li');if(!listItem){return false;}
  79. const node=(listItem.treeElement).node();const precomputedFeatures=await Components.ImagePreview.loadDimensionsForNode(node);const preview=await Components.ImagePreview.build(node.domModel().target(),link[Elements.ElementsTreeElement.HrefSymbol],true,{precomputedFeatures});if(preview){popover.contentElement.appendChild(preview);}
  80. return!!preview;}};}
  81. _onfocusout(event){SDK.OverlayModel.hideDOMNodeHighlight();}
  82. _onmousedown(event){const element=this._treeElementFromEvent(event);if(!element||element.isEventWithinDisclosureTriangle(event)){return;}
  83. element.select();}
  84. setHoverEffect(treeElement){if(this._previousHoveredElement===treeElement){return;}
  85. if(this._previousHoveredElement){this._previousHoveredElement.hovered=false;delete this._previousHoveredElement;}
  86. if(treeElement){treeElement.hovered=true;this._previousHoveredElement=treeElement;}}
  87. _onmousemove(event){const element=this._treeElementFromEvent(event);if(element&&this._previousHoveredElement===element){return;}
  88. this.setHoverEffect(element);this._highlightTreeElement((element),!UI.KeyboardShortcut.eventHasCtrlOrMeta(event));}
  89. _highlightTreeElement(element,showInfo){if(element instanceof Elements.ElementsTreeElement){element.node().domModel().overlayModel().highlightInOverlay({node:element.node()},'all',showInfo);return;}
  90. if(element instanceof ShortcutTreeElement){element.domModel().overlayModel().highlightInOverlay({deferredNode:element.deferredNode()},'all',showInfo);}}
  91. _onmouseleave(event){this.setHoverEffect(null);SDK.OverlayModel.hideDOMNodeHighlight();}
  92. _ondragstart(event){if(event.target.hasSelection()){return false;}
  93. if(event.target.nodeName==='A'){return false;}
  94. const treeElement=this._validDragSourceOrTarget(this._treeElementFromEvent(event));if(!treeElement){return false;}
  95. if(treeElement.node().nodeName()==='BODY'||treeElement.node().nodeName()==='HEAD'){return false;}
  96. event.dataTransfer.setData('text/plain',treeElement.listItemElement.textContent.replace(/\u200b/g,''));event.dataTransfer.effectAllowed='copyMove';this._treeElementBeingDragged=treeElement;SDK.OverlayModel.hideDOMNodeHighlight();return true;}
  97. _ondragover(event){if(!this._treeElementBeingDragged){return false;}
  98. const treeElement=this._validDragSourceOrTarget(this._treeElementFromEvent(event));if(!treeElement){return false;}
  99. let node=treeElement.node();while(node){if(node===this._treeElementBeingDragged._node){return false;}
  100. node=node.parentNode;}
  101. treeElement.listItemElement.classList.add('elements-drag-over');this._dragOverTreeElement=treeElement;event.preventDefault();event.dataTransfer.dropEffect='move';return false;}
  102. _ondragleave(event){this._clearDragOverTreeElementMarker();event.preventDefault();return false;}
  103. _validDragSourceOrTarget(treeElement){if(!treeElement){return null;}
  104. if(!(treeElement instanceof Elements.ElementsTreeElement)){return null;}
  105. const elementsTreeElement=(treeElement);const node=elementsTreeElement.node();if(!node.parentNode||node.parentNode.nodeType()!==Node.ELEMENT_NODE){return null;}
  106. return elementsTreeElement;}
  107. _ondrop(event){event.preventDefault();const treeElement=this._treeElementFromEvent(event);if(treeElement instanceof Elements.ElementsTreeElement){this._doMove(treeElement);}}
  108. _doMove(treeElement){if(!this._treeElementBeingDragged){return;}
  109. let parentNode;let anchorNode;if(treeElement.isClosingTag()){parentNode=treeElement.node();}else{const dragTargetNode=treeElement.node();parentNode=dragTargetNode.parentNode;anchorNode=dragTargetNode;}
  110. const wasExpanded=this._treeElementBeingDragged.expanded;this._treeElementBeingDragged._node.moveTo(parentNode,anchorNode,this.selectNodeAfterEdit.bind(this,wasExpanded));delete this._treeElementBeingDragged;}
  111. _ondragend(event){event.preventDefault();this._clearDragOverTreeElementMarker();delete this._treeElementBeingDragged;}
  112. _clearDragOverTreeElementMarker(){if(this._dragOverTreeElement){this._dragOverTreeElement.listItemElement.classList.remove('elements-drag-over');delete this._dragOverTreeElement;}}
  113. _contextMenuEventFired(event){const treeElement=this._treeElementFromEvent(event);if(treeElement instanceof Elements.ElementsTreeElement){this.showContextMenu(treeElement,event);}}
  114. showContextMenu(treeElement,event){if(UI.isEditing()){return;}
  115. const contextMenu=new UI.ContextMenu(event);const isPseudoElement=!!treeElement.node().pseudoType();const isTag=treeElement.node().nodeType()===Node.ELEMENT_NODE&&!isPseudoElement;let textNode=event.target.enclosingNodeOrSelfWithClass('webkit-html-text-node');if(textNode&&textNode.classList.contains('bogus')){textNode=null;}
  116. const commentNode=event.target.enclosingNodeOrSelfWithClass('webkit-html-comment');contextMenu.saveSection().appendItem(ls`Store as global variable`,this._saveNodeToTempVariable.bind(this,treeElement.node()));if(textNode){treeElement.populateTextContextMenu(contextMenu,textNode);}else if(isTag){treeElement.populateTagContextMenu(contextMenu,event);}else if(commentNode){treeElement.populateNodeContextMenu(contextMenu);}else if(isPseudoElement){treeElement.populateScrollIntoView(contextMenu);}
  117. contextMenu.appendApplicableItems(treeElement.node());contextMenu.show();}
  118. async _saveNodeToTempVariable(node){const remoteObjectForConsole=await node.resolveToObject();await SDK.consoleModel.saveToTempVariable(UI.context.flavor(SDK.ExecutionContext),remoteObjectForConsole);}
  119. runPendingUpdates(){this._updateModifiedNodes();}
  120. _onKeyDown(event){const keyboardEvent=(event);if(UI.isEditing()){return;}
  121. const node=this.selectedDOMNode();if(!node){return;}
  122. const treeElement=node[this._treeElementSymbol];if(!treeElement){return;}
  123. if(UI.KeyboardShortcut.eventHasCtrlOrMeta(keyboardEvent)&&node.parentNode){if(keyboardEvent.key==='ArrowUp'&&node.previousSibling){node.moveTo(node.parentNode,node.previousSibling,this.selectNodeAfterEdit.bind(this,treeElement.expanded));keyboardEvent.consume(true);return;}
  124. if(keyboardEvent.key==='ArrowDown'&&node.nextSibling){node.moveTo(node.parentNode,node.nextSibling.nextSibling,this.selectNodeAfterEdit.bind(this,treeElement.expanded));keyboardEvent.consume(true);return;}}}
  125. toggleEditAsHTML(node,startEditing,callback){const treeElement=node[this._treeElementSymbol];if(!treeElement||!treeElement.hasEditableNode()){return;}
  126. if(node.pseudoType()){return;}
  127. const parentNode=node.parentNode;const index=node.index;const wasExpanded=treeElement.expanded;treeElement.toggleEditAsHTML(editingFinished.bind(this),startEditing);function editingFinished(success){if(callback){callback();}
  128. if(!success){return;}
  129. this.runPendingUpdates();const newNode=parentNode?parentNode.children()[index]||parentNode:null;if(!newNode){return;}
  130. this.selectDOMNode(newNode,true);if(wasExpanded){const newTreeItem=this.findTreeElement(newNode);if(newTreeItem){newTreeItem.expand();}}}}
  131. selectNodeAfterEdit(wasExpanded,error,newNode){if(error){return null;}
  132. this.runPendingUpdates();if(!newNode){return null;}
  133. this.selectDOMNode(newNode,true);const newTreeItem=this.findTreeElement(newNode);if(wasExpanded){if(newTreeItem){newTreeItem.expand();}}
  134. return newTreeItem;}
  135. async toggleHideElement(node){const pseudoType=node.pseudoType();const effectiveNode=pseudoType?node.parentNode:node;if(!effectiveNode){return;}
  136. const hidden=node.marker('hidden-marker');const object=await effectiveNode.resolveToObject('');if(!object){return;}
  137. await object.callFunction(toggleClassAndInjectStyleRule,[{value:pseudoType},{value:!hidden}]);object.release();node.setMarker('hidden-marker',hidden?null:true);function toggleClassAndInjectStyleRule(pseudoType,hidden){const classNamePrefix='__web-inspector-hide';const classNameSuffix='-shortcut__';const styleTagId='__web-inspector-hide-shortcut-style__';const selectors=[];selectors.push('.__web-inspector-hide-shortcut__');selectors.push('.__web-inspector-hide-shortcut__ *');selectors.push('.__web-inspector-hidebefore-shortcut__::before');selectors.push('.__web-inspector-hideafter-shortcut__::after');const selector=selectors.join(', ');const ruleBody=' visibility: hidden !important;';const rule='\n'+selector+'\n{\n'+ruleBody+'\n}\n';const className=classNamePrefix+(pseudoType||'')+classNameSuffix;this.classList.toggle(className,hidden);let localRoot=this;while(localRoot.parentNode){localRoot=localRoot.parentNode;}
  138. if(localRoot.nodeType===Node.DOCUMENT_NODE){localRoot=document.head;}
  139. let style=localRoot.querySelector('style#'+styleTagId);if(style){return;}
  140. style=document.createElement('style');style.id=styleTagId;style.textContent=rule;localRoot.appendChild(style);}}
  141. isToggledToHidden(node){return!!node.marker('hidden-marker');}
  142. _reset(){this.rootDOMNode=null;this.selectDOMNode(null,false);this._popoverHelper.hidePopover();delete this._clipboardNodeData;SDK.OverlayModel.hideDOMNodeHighlight();this._updateRecords.clear();}
  143. wireToDOMModel(domModel){domModel[ElementsTreeOutline._treeOutlineSymbol]=this;domModel.addEventListener(SDK.DOMModel.Events.MarkersChanged,this._markersChanged,this);domModel.addEventListener(SDK.DOMModel.Events.NodeInserted,this._nodeInserted,this);domModel.addEventListener(SDK.DOMModel.Events.NodeRemoved,this._nodeRemoved,this);domModel.addEventListener(SDK.DOMModel.Events.AttrModified,this._attributeModified,this);domModel.addEventListener(SDK.DOMModel.Events.AttrRemoved,this._attributeRemoved,this);domModel.addEventListener(SDK.DOMModel.Events.CharacterDataModified,this._characterDataModified,this);domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated,this._documentUpdated,this);domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated,this._childNodeCountUpdated,this);domModel.addEventListener(SDK.DOMModel.Events.DistributedNodesChanged,this._distributedNodesChanged,this);}
  144. unwireFromDOMModel(domModel){domModel.removeEventListener(SDK.DOMModel.Events.MarkersChanged,this._markersChanged,this);domModel.removeEventListener(SDK.DOMModel.Events.NodeInserted,this._nodeInserted,this);domModel.removeEventListener(SDK.DOMModel.Events.NodeRemoved,this._nodeRemoved,this);domModel.removeEventListener(SDK.DOMModel.Events.AttrModified,this._attributeModified,this);domModel.removeEventListener(SDK.DOMModel.Events.AttrRemoved,this._attributeRemoved,this);domModel.removeEventListener(SDK.DOMModel.Events.CharacterDataModified,this._characterDataModified,this);domModel.removeEventListener(SDK.DOMModel.Events.DocumentUpdated,this._documentUpdated,this);domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated,this._childNodeCountUpdated,this);domModel.removeEventListener(SDK.DOMModel.Events.DistributedNodesChanged,this._distributedNodesChanged,this);delete domModel[ElementsTreeOutline._treeOutlineSymbol];}
  145. _addUpdateRecord(node){let record=this._updateRecords.get(node);if(!record){record=new UpdateRecord();this._updateRecords.set(node,record);}
  146. return record;}
  147. _updateRecordForHighlight(node){if(!this._visible){return null;}
  148. return this._updateRecords.get(node)||null;}
  149. _documentUpdated(event){const domModel=(event.data);this._reset();if(domModel.existingDocument()){this.rootDOMNode=domModel.existingDocument();}}
  150. _attributeModified(event){const node=(event.data.node);this._addUpdateRecord(node).attributeModified(event.data.name);this._updateModifiedNodesSoon();}
  151. _attributeRemoved(event){const node=(event.data.node);this._addUpdateRecord(node).attributeRemoved(event.data.name);this._updateModifiedNodesSoon();}
  152. _characterDataModified(event){const node=(event.data);this._addUpdateRecord(node).charDataModified();if(node.parentNode&&node.parentNode.firstChild===node.parentNode.lastChild){this._addUpdateRecord(node.parentNode).childrenModified();}
  153. this._updateModifiedNodesSoon();}
  154. _nodeInserted(event){const node=(event.data);this._addUpdateRecord((node.parentNode)).nodeInserted(node);this._updateModifiedNodesSoon();}
  155. _nodeRemoved(event){const node=(event.data.node);const parentNode=(event.data.parent);this.resetClipboardIfNeeded(node);this._addUpdateRecord(parentNode).nodeRemoved(node);this._updateModifiedNodesSoon();}
  156. _childNodeCountUpdated(event){const node=(event.data);this._addUpdateRecord(node).childrenModified();this._updateModifiedNodesSoon();}
  157. _distributedNodesChanged(event){const node=(event.data);this._addUpdateRecord(node).childrenModified();this._updateModifiedNodesSoon();}
  158. _updateModifiedNodesSoon(){if(!this._updateRecords.size){return;}
  159. if(this._updateModifiedNodesTimeout){return;}
  160. this._updateModifiedNodesTimeout=setTimeout(this._updateModifiedNodes.bind(this),50);}
  161. _updateModifiedNodes(){if(this._updateModifiedNodesTimeout){clearTimeout(this._updateModifiedNodesTimeout);delete this._updateModifiedNodesTimeout;}
  162. const updatedNodes=this._updateRecords.keysArray();const hidePanelWhileUpdating=updatedNodes.length>10;let treeOutlineContainerElement;let originalScrollTop;if(hidePanelWhileUpdating){treeOutlineContainerElement=this.element.parentNode;originalScrollTop=treeOutlineContainerElement?treeOutlineContainerElement.scrollTop:0;this._element.classList.add('hidden');}
  163. if(this._rootDOMNode&&this._updateRecords.get(this._rootDOMNode)&&this._updateRecords.get(this._rootDOMNode).hasChangedChildren()){this.update();}else{for(const node of this._updateRecords.keys()){if(this._updateRecords.get(node).hasChangedChildren()){this._updateModifiedParentNode(node);}else{this._updateModifiedNode(node);}}}
  164. if(hidePanelWhileUpdating){this._element.classList.remove('hidden');if(originalScrollTop){treeOutlineContainerElement.scrollTop=originalScrollTop;}}
  165. this._updateRecords.clear();this._fireElementsTreeUpdated(updatedNodes);}
  166. _updateModifiedNode(node){const treeElement=this.findTreeElement(node);if(treeElement){treeElement.updateTitle(this._updateRecordForHighlight(node));}}
  167. _updateModifiedParentNode(node){const parentTreeElement=this.findTreeElement(node);if(parentTreeElement){parentTreeElement.setExpandable(this._hasVisibleChildren(node));parentTreeElement.updateTitle(this._updateRecordForHighlight(node));if(parentTreeElement.populated){this._updateChildren(parentTreeElement);}}}
  168. populateTreeElement(treeElement){if(treeElement.childCount()||!treeElement.isExpandable()){return Promise.resolve();}
  169. return new Promise(resolve=>{treeElement.node().getChildNodes(()=>{treeElement.populated=true;this._updateModifiedParentNode(treeElement.node());resolve();});});}
  170. _createElementTreeElement(node,closingTag){const treeElement=new Elements.ElementsTreeElement(node,closingTag);treeElement.setExpandable(!closingTag&&this._hasVisibleChildren(node));if(node.nodeType()===Node.ELEMENT_NODE&&node.parentNode&&node.parentNode.nodeType()===Node.DOCUMENT_NODE&&!node.parentNode.parentNode){treeElement.setCollapsible(false);}
  171. treeElement.selectable=this._selectEnabled;return treeElement;}
  172. _showChild(treeElement,child){if(treeElement.isClosingTag()){return null;}
  173. const index=this._visibleChildren(treeElement.node()).indexOf(child);if(index===-1){return null;}
  174. if(index>=treeElement.expandedChildrenLimit()){this.setExpandedChildrenLimit(treeElement,index+1);}
  175. return(treeElement.childAt(index));}
  176. _visibleChildren(node){let visibleChildren=Elements.ElementsTreeElement.visibleShadowRoots(node);const contentDocument=node.contentDocument();if(contentDocument){visibleChildren.push(contentDocument);}
  177. const importedDocument=node.importedDocument();if(importedDocument){visibleChildren.push(importedDocument);}
  178. const templateContent=node.templateContent();if(templateContent){visibleChildren.push(templateContent);}
  179. const markerPseudoElement=node.markerPseudoElement();if(markerPseudoElement){visibleChildren.push(markerPseudoElement);}
  180. const beforePseudoElement=node.beforePseudoElement();if(beforePseudoElement){visibleChildren.push(beforePseudoElement);}
  181. if(node.childNodeCount()){let children=node.children()||[];if(!this._showHTMLCommentsSetting.get()){children=children.filter(n=>n.nodeType()!==Node.COMMENT_NODE);}
  182. visibleChildren=visibleChildren.concat(children);}
  183. const afterPseudoElement=node.afterPseudoElement();if(afterPseudoElement){visibleChildren.push(afterPseudoElement);}
  184. return visibleChildren;}
  185. _hasVisibleChildren(node){if(node.isIframe()){return true;}
  186. if(node.isPortal()){return true;}
  187. if(node.contentDocument()){return true;}
  188. if(node.importedDocument()){return true;}
  189. if(node.templateContent()){return true;}
  190. if(Elements.ElementsTreeElement.visibleShadowRoots(node).length){return true;}
  191. if(node.hasPseudoElements()){return true;}
  192. if(node.isInsertionPoint()){return true;}
  193. return!!node.childNodeCount()&&!Elements.ElementsTreeElement.canShowInlineText(node);}
  194. _createExpandAllButtonTreeElement(treeElement){const button=UI.createTextButton('',handleLoadAllChildren.bind(this));button.value='';const expandAllButtonElement=new UI.TreeElement(button);expandAllButtonElement.selectable=false;expandAllButtonElement.expandAllButton=true;expandAllButtonElement.button=button;return expandAllButtonElement;function handleLoadAllChildren(event){const visibleChildCount=this._visibleChildren(treeElement.node()).length;this.setExpandedChildrenLimit(treeElement,Math.max(visibleChildCount,treeElement.expandedChildrenLimit()+Elements.ElementsTreeElement.InitialChildrenLimit));event.consume();}}
  195. setExpandedChildrenLimit(treeElement,expandedChildrenLimit){if(treeElement.expandedChildrenLimit()===expandedChildrenLimit){return;}
  196. treeElement.setExpandedChildrenLimit(expandedChildrenLimit);if(treeElement.treeOutline&&!this._treeElementsBeingUpdated.has(treeElement)){this._updateModifiedParentNode(treeElement.node());}}
  197. _updateChildren(treeElement){if(!treeElement.isExpandable()){const selectedTreeElement=treeElement.treeOutline.selectedTreeElement;if(selectedTreeElement&&selectedTreeElement.hasAncestor(treeElement)){treeElement.select(true);}
  198. treeElement.removeChildren();return;}
  199. console.assert(!treeElement.isClosingTag());this._innerUpdateChildren(treeElement);}
  200. insertChildElement(treeElement,child,index,closingTag){const newElement=this._createElementTreeElement(child,closingTag);treeElement.insertChild(newElement,index);return newElement;}
  201. _moveChild(treeElement,child,targetIndex){if(treeElement.indexOfChild(child)===targetIndex){return;}
  202. const wasSelected=child.selected;if(child.parent){child.parent.removeChild(child);}
  203. treeElement.insertChild(child,targetIndex);if(wasSelected){child.select();}}
  204. _innerUpdateChildren(treeElement){if(this._treeElementsBeingUpdated.has(treeElement)){return;}
  205. this._treeElementsBeingUpdated.add(treeElement);const node=treeElement.node();const visibleChildren=this._visibleChildren(node);const visibleChildrenSet=new Set(visibleChildren);const existingTreeElements=new Map();for(let i=treeElement.childCount()-1;i>=0;--i){const existingTreeElement=treeElement.childAt(i);if(!(existingTreeElement instanceof Elements.ElementsTreeElement)){treeElement.removeChildAtIndex(i);continue;}
  206. const elementsTreeElement=(existingTreeElement);const existingNode=elementsTreeElement.node();if(visibleChildrenSet.has(existingNode)){existingTreeElements.set(existingNode,existingTreeElement);continue;}
  207. treeElement.removeChildAtIndex(i);}
  208. for(let i=0;i<visibleChildren.length&&i<treeElement.expandedChildrenLimit();++i){const child=visibleChildren[i];const existingTreeElement=existingTreeElements.get(child)||this.findTreeElement(child);if(existingTreeElement&&existingTreeElement!==treeElement){this._moveChild(treeElement,existingTreeElement,i);}else{const newElement=this.insertChildElement(treeElement,child,i);if(this._updateRecordForHighlight(node)&&treeElement.expanded){Elements.ElementsTreeElement.animateOnDOMUpdate(newElement);}
  209. if(treeElement.childCount()>treeElement.expandedChildrenLimit()){this.setExpandedChildrenLimit(treeElement,treeElement.expandedChildrenLimit()+1);}}}
  210. const expandedChildCount=treeElement.childCount();if(visibleChildren.length>expandedChildCount){const targetButtonIndex=expandedChildCount;if(!treeElement.expandAllButtonElement){treeElement.expandAllButtonElement=this._createExpandAllButtonTreeElement(treeElement);}
  211. treeElement.insertChild(treeElement.expandAllButtonElement,targetButtonIndex);treeElement.expandAllButtonElement.button.textContent=Common.UIString('Show All Nodes (%d More)',visibleChildren.length-expandedChildCount);}else if(treeElement.expandAllButtonElement){delete treeElement.expandAllButtonElement;}
  212. if(node.isInsertionPoint()){for(const distributedNode of node.distributedNodes()){treeElement.appendChild(new ShortcutTreeElement(distributedNode));}}
  213. if(node.nodeType()===Node.ELEMENT_NODE&&treeElement.isExpandable()){this.insertChildElement(treeElement,node,treeElement.childCount(),true);}
  214. this._treeElementsBeingUpdated.delete(treeElement);}
  215. _markersChanged(event){const node=(event.data);const treeElement=node[this._treeElementSymbol];if(treeElement){treeElement.updateDecorations();}}}
  216. ElementsTreeOutline._treeOutlineSymbol=Symbol('treeOutline');ElementsTreeOutline.Events={SelectedNodeChanged:Symbol('SelectedNodeChanged'),ElementsTreeUpdated:Symbol('ElementsTreeUpdated')};export const MappedCharToEntity={'\xA0':'nbsp','\x93':'#147','\xAD':'shy','\u2002':'ensp','\u2003':'emsp','\u2009':'thinsp','\u200a':'#8202','\u200b':'#8203','\u200c':'zwnj','\u200d':'zwj','\u200e':'lrm','\u200f':'rlm','\u202a':'#8234','\u202b':'#8235','\u202c':'#8236','\u202d':'#8237','\u202e':'#8238','\ufeff':'#65279'};export class UpdateRecord{attributeModified(attrName){if(this._removedAttributes&&this._removedAttributes.has(attrName)){this._removedAttributes.delete(attrName);}
  217. if(!this._modifiedAttributes){this._modifiedAttributes=(new Set());}
  218. this._modifiedAttributes.add(attrName);}
  219. attributeRemoved(attrName){if(this._modifiedAttributes&&this._modifiedAttributes.has(attrName)){this._modifiedAttributes.delete(attrName);}
  220. if(!this._removedAttributes){this._removedAttributes=(new Set());}
  221. this._removedAttributes.add(attrName);}
  222. nodeInserted(node){this._hasChangedChildren=true;}
  223. nodeRemoved(node){this._hasChangedChildren=true;this._hasRemovedChildren=true;}
  224. charDataModified(){this._charDataModified=true;}
  225. childrenModified(){this._hasChangedChildren=true;}
  226. isAttributeModified(attributeName){return this._modifiedAttributes&&this._modifiedAttributes.has(attributeName);}
  227. hasRemovedAttributes(){return!!this._removedAttributes&&!!this._removedAttributes.size;}
  228. isCharDataModified(){return!!this._charDataModified;}
  229. hasChangedChildren(){return!!this._hasChangedChildren;}
  230. hasRemovedChildren(){return!!this._hasRemovedChildren;}}
  231. export class Renderer{async render(object){let node;if(object instanceof SDK.DOMNode){node=(object);}else if(object instanceof SDK.DeferredDOMNode){node=await((object)).resolvePromise();}
  232. if(!node){return null;}
  233. const treeOutline=new ElementsTreeOutline(false,true,true);treeOutline.rootDOMNode=node;if(!treeOutline.firstChild().isExpandable()){treeOutline._element.classList.add('single-node');}
  234. treeOutline.setVisible(true);treeOutline.element.treeElementForTest=treeOutline.firstChild();treeOutline.setShowSelectionOnKeyboardFocus(true,true);return{node:treeOutline.element,tree:treeOutline};}}
  235. export class ShortcutTreeElement extends UI.TreeElement{constructor(nodeShortcut){super('');this.listItemElement.createChild('div','selection fill');const title=this.listItemElement.createChild('span','elements-tree-shortcut-title');let text=nodeShortcut.nodeName.toLowerCase();if(nodeShortcut.nodeType===Node.ELEMENT_NODE){text='<'+text+'>';}
  236. title.textContent='\u21AA '+text;const link=Elements.DOMLinkifier.linkifyDeferredNodeReference(nodeShortcut.deferredNode);this.listItemElement.createTextChild(' ');link.classList.add('elements-tree-shortcut-link');link.textContent=Common.UIString('reveal');this.listItemElement.appendChild(link);this._nodeShortcut=nodeShortcut;}
  237. get hovered(){return this._hovered;}
  238. set hovered(x){if(this._hovered===x){return;}
  239. this._hovered=x;this.listItemElement.classList.toggle('hovered',x);}
  240. deferredNode(){return this._nodeShortcut.deferredNode;}
  241. domModel(){return this._nodeShortcut.deferredNode.domModel();}
  242. onselect(selectedByUser){if(!selectedByUser){return true;}
  243. this._nodeShortcut.deferredNode.highlight();this._nodeShortcut.deferredNode.resolve(resolved.bind(this));function resolved(node){if(node){this.treeOutline._selectedDOMNode=node;this.treeOutline._selectedNodeChanged();}}
  244. return true;}}
  245. self.Elements=self.Elements||{};Elements=Elements||{};Elements.ElementsTreeOutline=ElementsTreeOutline;Elements.ElementsTreeOutline.MappedCharToEntity=MappedCharToEntity;Elements.ElementsTreeOutline.UpdateRecord=UpdateRecord;Elements.ElementsTreeOutline.Renderer=Renderer;Elements.ElementsTreeOutline.ShortcutTreeElement=ShortcutTreeElement;Elements.ElementsTreeOutline.ClipboardData;