1 /* 2 * Copyright (c) 2002-2025 Gargoyle Software Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * https://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package org.htmlunit.javascript.host.dom; 16 17 import org.htmlunit.html.DomNode; 18 import org.htmlunit.html.HtmlDomTreeWalker; 19 import org.htmlunit.javascript.HtmlUnitScriptable; 20 import org.htmlunit.javascript.JavaScriptEngine; 21 import org.htmlunit.javascript.configuration.JsxClass; 22 import org.htmlunit.javascript.configuration.JsxConstructor; 23 import org.htmlunit.javascript.configuration.JsxFunction; 24 import org.htmlunit.javascript.configuration.JsxGetter; 25 import org.htmlunit.javascript.configuration.JsxSetter; 26 import org.w3c.dom.DOMException; 27 28 /** 29 * The JavaScript object that represents a {@code TreeWalker}. 30 * 31 * @see <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html"> 32 * DOM-Level-2-Traversal-Range</a> 33 * @author <a href="mailto:mike@10gen.com">Mike Dirolf</a> 34 * @author Frank Danek 35 * @author Ahmed Ashour 36 * @author Ronald Brill 37 */ 38 @JsxClass 39 public class TreeWalker extends HtmlUnitScriptable { 40 41 private HtmlDomTreeWalker walker_; 42 43 /** 44 * Creates an instance. 45 */ 46 public TreeWalker() { 47 super(); 48 } 49 50 /** 51 * Creates an instance. 52 */ 53 @JsxConstructor 54 public void jsConstructor() { 55 throw JavaScriptEngine.typeErrorIllegalConstructor(); 56 } 57 58 /** 59 * Creates an instance. 60 * 61 * @param root The root node of the TreeWalker. Must not be 62 * {@code null}. 63 * @param whatToShow Flag specifying which types of nodes appear in the 64 * logical view of the TreeWalker. See {@link NodeFilter} for the 65 * set of possible Show_ values. 66 * @param filter The {@link NodeFilter} to be used with this TreeWalker, 67 * or {@code null} to indicate no filter. 68 * @param expandEntityReferences If false, the contents of 69 * EntityReference nodes are not present in the logical view. 70 */ 71 public TreeWalker(final Node root, 72 final int whatToShow, 73 final org.w3c.dom.traversal.NodeFilter filter, 74 final boolean expandEntityReferences) { 75 super(); 76 if (root == null) { 77 throw JavaScriptEngine.typeError("root must not be null"); 78 } 79 walker_ = new HtmlDomTreeWalker(root.getDomNodeOrDie(), whatToShow, filter, expandEntityReferences); 80 } 81 82 /** 83 * Gets the root node of the TreeWalker, as specified when it was created. 84 * 85 * @return the root node of the TreeWalker 86 */ 87 @JsxGetter 88 public Node getRoot() { 89 return getNodeOrNull(walker_.getRoot()); 90 } 91 92 /** 93 * Gets the whatToShow attribute of the TreeWalker. This attribute 94 * determines which node types are presented via the TreeWalker. The set 95 * of available constants is defined in {@link NodeFilter}. 96 * 97 * @return the value of the whatToShow attribute of the TreeWalker 98 */ 99 @JsxGetter 100 public long getWhatToShow() { 101 long whatToShow = walker_.getWhatToShow(); 102 if (whatToShow == org.w3c.dom.traversal.NodeFilter.SHOW_ALL) { 103 whatToShow = 0xFFFFFFFFL; 104 } 105 return whatToShow; 106 } 107 108 /** 109 * Gets the filter used to screen nodes. 110 * 111 * @return the filter used to screen nodes 112 */ 113 @JsxGetter 114 public Object getFilter() { 115 //TODO: we should return the original filter 116 return walker_.getFilter(); 117 } 118 119 /** 120 * Gets the node at which the TreeWalker is currently positioned. 121 * 122 * @return the currentNode 123 */ 124 @JsxGetter 125 public Node getCurrentNode() { 126 return getNodeOrNull(walker_.getCurrentNode()); 127 } 128 129 /** 130 * Sets the node at which the TreeWalker is currently positioned. 131 * 132 * @param currentNode The node to be used as the current position of the 133 * TreeWalker. 134 * @throws DOMException on attempt to set currentNode to 135 * {@code null}. 136 */ 137 @JsxSetter 138 public void setCurrentNode(final Node currentNode) throws DOMException { 139 if (currentNode == null) { 140 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, 141 "currentNode cannot be set to null"); 142 } 143 walker_.setCurrentNode(currentNode.getDomNodeOrDie()); 144 } 145 146 /** 147 * Given a {@link Node}, return the appropriate constant for whatToShow. 148 * 149 * @param node the node 150 * @return the whatToShow constant for the type of specified node 151 */ 152 static int getFlagForNode(final Node node) { 153 switch (node.getNodeType()) { 154 case Node.ELEMENT_NODE: 155 return NodeFilter.SHOW_ELEMENT; 156 case Node.ATTRIBUTE_NODE: 157 return NodeFilter.SHOW_ATTRIBUTE; 158 case Node.TEXT_NODE: 159 return NodeFilter.SHOW_TEXT; 160 case Node.CDATA_SECTION_NODE: 161 return NodeFilter.SHOW_CDATA_SECTION; 162 case Node.ENTITY_REFERENCE_NODE: 163 return NodeFilter.SHOW_ENTITY_REFERENCE; 164 case Node.ENTITY_NODE: 165 return NodeFilter.SHOW_ENTITY; 166 case Node.PROCESSING_INSTRUCTION_NODE: 167 return NodeFilter.SHOW_PROCESSING_INSTRUCTION; 168 case Node.COMMENT_NODE: 169 return NodeFilter.SHOW_COMMENT; 170 case Node.DOCUMENT_NODE: 171 return NodeFilter.SHOW_DOCUMENT; 172 case Node.DOCUMENT_TYPE_NODE: 173 return NodeFilter.SHOW_DOCUMENT_TYPE; 174 case Node.DOCUMENT_FRAGMENT_NODE: 175 return NodeFilter.SHOW_DOCUMENT_FRAGMENT; 176 case Node.NOTATION_NODE: 177 return NodeFilter.SHOW_NOTATION; 178 default: 179 return 0; 180 } 181 } 182 183 /** 184 * Moves to and returns the closest visible ancestor node of the current 185 * node. If the search for parentNode attempts to step upward from the 186 * TreeWalker's root node, or if it fails to find a visible ancestor node, 187 * this method retains the current position and returns null. 188 * 189 * @return The new parent node, or {@code null} if the current node 190 * has no parent in the TreeWalker's logical view. 191 */ 192 @JsxFunction 193 public Node parentNode() { 194 return getNodeOrNull(walker_.parentNode()); 195 } 196 197 private static Node getNodeOrNull(final DomNode domNode) { 198 if (domNode == null) { 199 return null; 200 } 201 return domNode.getScriptableObject(); 202 } 203 204 /** 205 * Moves the TreeWalker to the first visible child of the current node, 206 * and returns the new node. If the current node has no visible children, 207 * returns {@code null}, and retains the current node. 208 * 209 * @return The new node, or {@code null} if the current node has no 210 * visible children in the TreeWalker's logical view. 211 */ 212 @JsxFunction 213 public Node firstChild() { 214 return getNodeOrNull(walker_.firstChild()); 215 } 216 217 /** 218 * Moves the TreeWalker to the last visible child of the current node, 219 * and returns the new node. If the current node has no visible children, 220 * returns {@code null}, and retains the current node. 221 * 222 * @return The new node, or {@code null} if the current node has no 223 * visible children in the TreeWalker's logical view. 224 */ 225 @JsxFunction 226 public Node lastChild() { 227 return getNodeOrNull(walker_.lastChild()); 228 } 229 230 /** 231 * Moves the TreeWalker to the previous sibling of the current node, and 232 * returns the new node. If the current node has no visible previous 233 * sibling, returns {@code null}, and retains the current node. 234 * 235 * @return The new node, or {@code null} if the current node has no 236 * previous sibling in the TreeWalker's logical view. 237 */ 238 @JsxFunction 239 public Node previousSibling() { 240 return getNodeOrNull(walker_.previousSibling()); 241 } 242 243 /** 244 * Moves the TreeWalker to the next sibling of the current node, and 245 * returns the new node. If the current node has no visible next sibling, 246 * returns {@code null}, and retains the current node. 247 * 248 * @return The new node, or {@code null} if the current node has no 249 * next sibling in the TreeWalker's logical view. 250 */ 251 @JsxFunction 252 public Node nextSibling() { 253 return getNodeOrNull(walker_.nextSibling()); 254 } 255 256 /** 257 * Moves the TreeWalker to the previous visible node in document order 258 * relative to the current node, and returns the new node. If the current 259 * node has no previous node, or if the search for previousNode attempts 260 * to step upward from the TreeWalker's root node, returns 261 * {@code null}, and retains the current node. 262 * 263 * @return The new node, or {@code null} if the current node has no 264 * previous node in the TreeWalker's logical view. 265 */ 266 @JsxFunction 267 public Node previousNode() { 268 return getNodeOrNull(walker_.previousNode()); 269 } 270 271 /** 272 * Moves the TreeWalker to the next visible node in document order 273 * relative to the current node, and returns the new node. If the current 274 * node has no next node, or if the search for nextNode attempts to step 275 * upward from the TreeWalker's root node, returns {@code null}, and 276 * retains the current node. 277 * 278 * @return The new node, or {@code null} if the current node has no 279 * next node in the TreeWalker's logical view. 280 */ 281 @JsxFunction 282 public Node nextNode() { 283 return getNodeOrNull(walker_.nextNode()); 284 } 285 286 }