From 227009ca9aecfda884de2e3aef0547b224c84c87 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 5 Aug 2026 20:33:11 +0300 Subject: [PATCH] refactor: use the elemental DOM API directly in the client engine DomApi.wrap only returned the node itself after the Polymer DOM API was removed, and DomNode and DomElement describe the same native DOM operations as elemental Node and Element. Call sites now use the elemental types directly, which also lets StateTree.getStateNodeForDomNode accept a Node. DomApiAbstractionUsageTest verified that every DOM call went through DomApi.wrap so that Polymer could rewrite the operation. There is nothing left to rewrite, so the test is removed together with the abstraction. --- .../vaadin/client/ApplicationConnection.java | 3 +- .../client/ExecuteJavaScriptElementUtils.java | 8 +- .../java/com/vaadin/client/PolymerUtils.java | 5 +- .../java/com/vaadin/client/WidgetUtil.java | 6 +- .../com/vaadin/client/flow/StateTree.java | 3 +- .../binding/SimpleElementBindingStrategy.java | 48 ++--- .../com/vaadin/client/flow/dom/DomApi.java | 84 -------- .../vaadin/client/flow/dom/DomElement.java | 200 ------------------ .../com/vaadin/client/flow/dom/DomNode.java | 176 --------------- .../client/DomApiAbstractionUsageTest.java | 196 ----------------- 10 files changed, 29 insertions(+), 700 deletions(-) delete mode 100644 flow-client/src/main/java/com/vaadin/client/flow/dom/DomApi.java delete mode 100644 flow-client/src/main/java/com/vaadin/client/flow/dom/DomElement.java delete mode 100644 flow-client/src/main/java/com/vaadin/client/flow/dom/DomNode.java delete mode 100644 flow-client/src/test/java/com/vaadin/client/DomApiAbstractionUsageTest.java diff --git a/flow-client/src/main/java/com/vaadin/client/ApplicationConnection.java b/flow-client/src/main/java/com/vaadin/client/ApplicationConnection.java index 98eb76f680b..1d44f2ba262 100644 --- a/flow-client/src/main/java/com/vaadin/client/ApplicationConnection.java +++ b/flow-client/src/main/java/com/vaadin/client/ApplicationConnection.java @@ -25,7 +25,6 @@ import com.vaadin.client.flow.StateNode; import com.vaadin.client.flow.binding.Binder; import com.vaadin.client.flow.collection.JsArray; -import com.vaadin.client.flow.dom.DomApi; import com.vaadin.client.flow.util.NativeFunction; import com.vaadin.flow.internal.nodefeature.NodeFeatures; import com.vaadin.flow.internal.nodefeature.NodeProperties; @@ -270,7 +269,7 @@ private JavaScriptObject getElementStyleProperties(int id) { private int getNodeId(Element element) { StateNode node = registry.getStateTree() - .getStateNodeForDomNode(DomApi.wrap(element)); + .getStateNodeForDomNode(element); return node == null ? -1 : node.getId(); } diff --git a/flow-client/src/main/java/com/vaadin/client/ExecuteJavaScriptElementUtils.java b/flow-client/src/main/java/com/vaadin/client/ExecuteJavaScriptElementUtils.java index c660780f1bc..7cc83f5b857 100644 --- a/flow-client/src/main/java/com/vaadin/client/ExecuteJavaScriptElementUtils.java +++ b/flow-client/src/main/java/com/vaadin/client/ExecuteJavaScriptElementUtils.java @@ -22,7 +22,6 @@ import com.vaadin.client.flow.collection.JsArray; import com.vaadin.client.flow.collection.JsCollections; import com.vaadin.client.flow.collection.JsMap; -import com.vaadin.client.flow.dom.DomApi; import com.vaadin.client.flow.model.UpdatableModelProperties; import com.vaadin.client.flow.nodefeature.NodeList; import com.vaadin.client.flow.nodefeature.NodeMap; @@ -79,13 +78,12 @@ private ExecuteJavaScriptElementUtils() { public static void attachExistingElement(StateNode parent, Element previousSibling, String tagName, int id) { Element existingElement = null; - JsArray childNodes = DomApi.wrap(parent.getDomNode()) - .getChildNodes(); + elemental.dom.NodeList childNodes = parent.getDomNode().getChildNodes(); JsMap indices = new JsMap<>(); boolean afterSibling = previousSibling == null; int elementIndex = -1; - for (int i = 0; i < childNodes.length(); i++) { - Node node = childNodes.get(i); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); indices.set(node, i); if (node.equals(previousSibling)) { afterSibling = true; diff --git a/flow-client/src/main/java/com/vaadin/client/PolymerUtils.java b/flow-client/src/main/java/com/vaadin/client/PolymerUtils.java index 528d3a066ba..ae5b36c6b5a 100644 --- a/flow-client/src/main/java/com/vaadin/client/PolymerUtils.java +++ b/flow-client/src/main/java/com/vaadin/client/PolymerUtils.java @@ -20,7 +20,6 @@ import com.vaadin.client.flow.collection.JsCollections; import com.vaadin.client.flow.collection.JsSet; import com.vaadin.client.flow.collection.JsWeakMap; -import com.vaadin.client.flow.dom.DomApi; import com.vaadin.client.flow.nodefeature.ListSpliceEvent; import com.vaadin.client.flow.nodefeature.MapProperty; import com.vaadin.client.flow.nodefeature.NodeFeature; @@ -613,13 +612,13 @@ public static void fireReadyEvent(Element polymerElement) { } private static Node getChildIgnoringStyles(Node parent, int index) { - HTMLCollection children = DomApi.wrap(parent).getChildren(); + HTMLCollection children = ((Element) parent).getChildren(); int filteredIndex = -1; for (int i = 0; i < children.getLength(); i++) { Node next = children.item(i); assert next instanceof Element : "Unexpected element type in the collection of children. " - + "DomElement::getChildren is supposed to return Element chidren only, but got " + + "Element::getChildren is supposed to return Element chidren only, but got " + next.getClass(); Element element = (Element) next; if (!"style".equalsIgnoreCase(element.getTagName())) { diff --git a/flow-client/src/main/java/com/vaadin/client/WidgetUtil.java b/flow-client/src/main/java/com/vaadin/client/WidgetUtil.java index 5eecbb76765..4949d4cbb34 100644 --- a/flow-client/src/main/java/com/vaadin/client/WidgetUtil.java +++ b/flow-client/src/main/java/com/vaadin/client/WidgetUtil.java @@ -20,8 +20,6 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; -import com.vaadin.client.flow.dom.DomApi; - import elemental.client.Browser; import elemental.dom.Element; import elemental.html.AnchorElement; @@ -155,9 +153,9 @@ public static String toPrettyJson(JsonValue json) { public static void updateAttribute(Element element, String attribute, String value) { if (value == null) { - DomApi.wrap(element).removeAttribute(attribute); + element.removeAttribute(attribute); } else { - DomApi.wrap(element).setAttribute(attribute, value); + element.setAttribute(attribute, value); } } diff --git a/flow-client/src/main/java/com/vaadin/client/flow/StateTree.java b/flow-client/src/main/java/com/vaadin/client/flow/StateTree.java index 000a8628ea6..d71916ca097 100644 --- a/flow-client/src/main/java/com/vaadin/client/flow/StateTree.java +++ b/flow-client/src/main/java/com/vaadin/client/flow/StateTree.java @@ -22,7 +22,6 @@ import com.vaadin.client.flow.collection.JsArray; import com.vaadin.client.flow.collection.JsCollections; import com.vaadin.client.flow.collection.JsMap; -import com.vaadin.client.flow.dom.DomNode; import com.vaadin.client.flow.nodefeature.MapProperty; import com.vaadin.client.flow.nodefeature.NodeList; import com.vaadin.client.flow.nodefeature.NodeMap; @@ -186,7 +185,7 @@ public void setResync(boolean resync) { * the dom node to find state node for * @return the state node or null */ - public StateNode getStateNodeForDomNode(DomNode domNode) { + public StateNode getStateNodeForDomNode(Node domNode) { final JsArray stateNodes = idToNode.mapValues(); for (int i = 0; i < stateNodes.length(); i++) { StateNode stateNode = stateNodes.get(i); diff --git a/flow-client/src/main/java/com/vaadin/client/flow/binding/SimpleElementBindingStrategy.java b/flow-client/src/main/java/com/vaadin/client/flow/binding/SimpleElementBindingStrategy.java index f8c34e93084..4fd0d49fd7c 100644 --- a/flow-client/src/main/java/com/vaadin/client/flow/binding/SimpleElementBindingStrategy.java +++ b/flow-client/src/main/java/com/vaadin/client/flow/binding/SimpleElementBindingStrategy.java @@ -43,10 +43,6 @@ import com.vaadin.client.flow.collection.JsMap.ForEachCallback; import com.vaadin.client.flow.collection.JsSet; import com.vaadin.client.flow.collection.JsWeakMap; -import com.vaadin.client.flow.dom.DomApi; -import com.vaadin.client.flow.dom.DomElement; -import com.vaadin.client.flow.dom.DomElement.DomTokenList; -import com.vaadin.client.flow.dom.DomNode; import com.vaadin.client.flow.model.UpdatableModelProperties; import com.vaadin.client.flow.nodefeature.ListSpliceEvent; import com.vaadin.client.flow.nodefeature.MapProperty; @@ -61,6 +57,7 @@ import elemental.client.Browser; import elemental.css.CSSStyleDeclaration; +import elemental.dom.DOMTokenList; import elemental.dom.Element; import elemental.dom.Node; import elemental.events.Event; @@ -855,7 +852,7 @@ private EventRemover bindChildren(BindingContext context) { context.binderContext.createAndBind(childNode); } else { child = context.binderContext.createAndBind(childNode); - DomApi.wrap(context.htmlNode).appendChild(child); + context.htmlNode.appendChild(child); } } @@ -1115,8 +1112,8 @@ private void handleChildrenSplice(ListSpliceEvent event, assert child != null : "Can't find element to remove"; - if (DomApi.wrap(child).getParentNode() == htmlNode) { - DomApi.wrap(htmlNode).removeChild(child); + if (child.getParentNode() == htmlNode) { + htmlNode.removeChild(child); } /* * If the client-side element is not inside the parent the @@ -1135,9 +1132,8 @@ private void handleChildrenSplice(ListSpliceEvent event, } private void removeAllChildren(Node htmlNode) { - DomElement wrap = DomApi.wrap(htmlNode); - while (wrap.getFirstChild() != null) { - wrap.removeChild(wrap.getFirstChild()); + while (htmlNode.getFirstChild() != null) { + htmlNode.removeChild(htmlNode.getFirstChild()); } } @@ -1155,8 +1151,7 @@ private void addChildren(int index, BindingContext context, StateNode previousSibling = getPreviousSibling(index, context); // Insert before the next sibling of the current node beforeRef = previousSibling == null ? null - : DomApi.wrap(previousSibling.getDomNode()) - .getNextSibling(); + : previousSibling.getDomNode().getNextSibling(); } else { // Insert at the end beforeRef = null; @@ -1176,20 +1171,19 @@ private void addChildren(int index, BindingContext context, } else { childNode = context.binderContext.createAndBind(newChild); - DomApi.wrap(context.htmlNode).insertBefore(childNode, - beforeRef); + context.htmlNode.insertBefore(childNode, beforeRef); } - beforeRef = DomApi.wrap(childNode).getNextSibling(); + beforeRef = childNode.getNextSibling(); } } private static Node getFirstNodeMappedAsStateNode( NodeList mappedNodeChildren, Node htmlNode) { - JsArray clientList = DomApi.wrap(htmlNode).getChildNodes(); - for (int i = 0; i < clientList.length(); i++) { - Node clientNode = clientList.get(i); + elemental.dom.NodeList clientList = htmlNode.getChildNodes(); + for (int i = 0; i < clientList.getLength(); i++) { + Node clientNode = clientList.item(i); for (int j = 0; j < mappedNodeChildren.length(); j++) { StateNode stateNode = (StateNode) mappedNodeChildren.get(j); if (clientNode.equals(stateNode.getDomNode())) { @@ -1492,12 +1486,11 @@ private EventRemover bindClassList(Element element, StateNode node) { NodeList classNodeList = node.getList(NodeFeatures.CLASS_LIST); for (int i = 0; i < classNodeList.length(); i++) { - DomApi.wrap(element).getClassList() - .add((String) classNodeList.get(i)); + element.getClassList().add((String) classNodeList.get(i)); } return classNodeList.addSpliceListener(e -> { - DomTokenList classList = DomApi.wrap(element).getClassList(); + DOMTokenList classList = element.getClassList(); JsArray remove = e.getRemove(); for (int i = 0; i < remove.length(); i++) { @@ -1577,7 +1570,7 @@ private int getClosestStateNodeIdToEventTarget(StateNode topNode, return -1; } try { - DomNode targetNode = DomApi.wrap(WidgetUtil.crazyJsCast(target)); + Node targetNode = WidgetUtil.crazyJsCast(target); JsArray stack = JsCollections.array(); stack.push(topNode); @@ -1594,7 +1587,7 @@ private int getClosestStateNodeIdToEventTarget(StateNode topNode, } // no direct match, all child element state nodes collected. // bottom-up search elements until matching state node found - targetNode = DomApi.wrap(targetNode.getParentNode()); + targetNode = targetNode.getParentNode(); return getStateNodeForElement(stack, targetNode); } catch (Exception e) { // not going to let event handling fail; just report nothing found @@ -1607,7 +1600,7 @@ private int getClosestStateNodeIdToEventTarget(StateNode topNode, } private static int getStateNodeForElement(JsArray searchStack, - DomNode targetNode) { + Node targetNode) { while (targetNode != null) { for (int i = searchStack.length() - 1; i > -1; i--) { final StateNode stateNode = searchStack.get(i); @@ -1615,7 +1608,7 @@ private static int getStateNodeForElement(JsArray searchStack, return stateNode.getId(); } } - targetNode = DomApi.wrap(targetNode.getParentNode()); + targetNode = targetNode.getParentNode(); } return -1; } @@ -1626,15 +1619,14 @@ private int getClosestStateNodeIdToDomNode(StateTree stateTree, return -1; } try { - DomNode targetNode = DomApi - .wrap(WidgetUtil.crazyJsCast(domNodeReference)); + Node targetNode = WidgetUtil.crazyJsCast(domNodeReference); while (targetNode != null) { StateNode stateNodeForDomNode = stateTree .getStateNodeForDomNode(targetNode); if (stateNodeForDomNode != null) { return stateNodeForDomNode.getId(); } - targetNode = DomApi.wrap(targetNode.getParentNode()); + targetNode = targetNode.getParentNode(); } } catch (Exception e) { // not going to let event handling fail; just report nothing found diff --git a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomApi.java b/flow-client/src/main/java/com/vaadin/client/flow/dom/DomApi.java deleted file mode 100644 index 3e2e013771c..00000000000 --- a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomApi.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.client.flow.dom; - -import com.vaadin.client.Console; - -import elemental.dom.Node; - -/** - * Access point for DOM API. All operations and interactions with DOM nodes and - * elements should go through this class. - *

- * This class delegates the operations to the actual DOM API implementations, - * which might be changed on the run, meaning after dependencies have been - * loaded. - * - * @author Vaadin Ltd - * @since 1.0 - */ -public class DomApi { - - /** - * Flag for tracking if Polymer-micro.html is loaded (contains dom). - * - * Package protected for testing reasons. - */ - static boolean polymerMicroLoaded = false; - - /** - * The currently used DOM API implementation. By default just returns the - * same object. - * - * Package protected for testing reasons. - */ - static DomApiImpl impl; - - private DomApi() { - // NOOP - } - - /** - * Wraps the given DOM node to make it safe to invoke any of the methods - * from {@link DomNode} or {@link DomElement}. - * - * @param node - * the node to wrap - * @return a wrapped element - */ - public static DomElement wrap(Node node) { - if (impl == null) { - return (DomElement) node; - } - return impl.wrap(node); - } - - /** - * Updates the DOM API implementation used. - */ - public static void updateApiImplementation() { - if (!polymerMicroLoaded && PolymerDomApiImpl.isPolymerMicroLoaded()) { - polymerMicroLoaded(); - } - } - - private static void polymerMicroLoaded() { - polymerMicroLoaded = true; - Console.debug("Polymer micro is now loaded, using Polymer DOM API"); - impl = new PolymerDomApiImpl(); - } - -} diff --git a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomElement.java b/flow-client/src/main/java/com/vaadin/client/flow/dom/DomElement.java deleted file mode 100644 index f79548709d2..00000000000 --- a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomElement.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.client.flow.dom; - -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; - -import com.vaadin.client.flow.collection.JsArray; - -import elemental.dom.Element; -import elemental.dom.Node; -import elemental.html.HTMLCollection; - -/** - * Element that has all methods from - * Element - * API that have been overridden in - * Polymer - * DOM module. - *

- * No instances of this class should be created directly, but instead - * {@link DomApi#wrap(elemental.dom.Node)} should be used - * - * @author Vaadin Ltd - * @since 1.0 - */ -@JsType(isNative = true) -public interface DomElement extends DomNode { - - /** - * A - * - * DOMTokenList java representation. - */ - @JsType(isNative = true) - interface DomTokenList { - /** - * Returns the length property. - * - * @return the token list length - */ - @JsProperty - int getLength(); - - /** - * Returns an item in the list by its index. - * - * @param index - * the index to look for the item - * @return the token at the given index - */ - String item(int index); - - /** - * Returns whether the underlying string contains token. - * - * @param token - * the token to check for - * @return true if token was found, false if - * not - */ - boolean contains(String token); - - /** - * Adds token to the underlying string. - * - * @param token - * the token to add - */ - void add(String token); - - /** - * Removes token from the underlying string. - * - * @param token - * the token to remove - */ - void remove(String token); - - /** - * Removes token from string and returns false - * . If token doesn't exist it's added and the function - * returns true. - * - * @param token - * the token to toggle - * @return true if token did not exist and was added, - * false if token existed and was removed - */ - boolean toggle(String token); - } - - /** - * Returns the classList property. - * - * @return the class list - */ - @JsProperty - DomTokenList getClassList(); - - /** - * Returns the firstElementChild property. - * - * @return the first element child - */ - @JsProperty - Element getFirstElementChild(); - - /** - * Returns the lastElementChild property. - * - * @return the last last element child - */ - @JsProperty - Element getLastElementChild(); - - /** - * Returns the innerHTML property. - * - * @return the inner html - */ - @JsProperty - String getInnerHTML(); - - /** - * Sets the innerHTML property to the given string. - * - * @param innerHTML - * the inner html to set - */ - @JsProperty - void setInnerHTML(String innerHTML); - - /** - * Returns the children property containing all child elements - * of the element, as a live collection. - * - * @return a collection of all child elements - */ - @JsProperty - HTMLCollection getChildren(); - - /** - * Returns the first Node which matches the specified selector - * string relative to the element. - * - * @param selectors - * a group of selectors to match on - * @return the first node that matched the given selectors - */ - Element querySelector(String selectors); - - /** - * Returns a non-live NodeList of all elements descended from - * this element and match the given group of CSS selectors. - *

- * NOTE: returns an array since that is what the Polymer.dom API does, and - * luckily native NodeList items can be accessed array like with - * list[index]. - *

- * This means that only {@link JsArray#get(int)} and - * {@link JsArray#length()} methods can be used from the returned "array". - * - * @param selectors - * a group of selectors to match on - * @return a non-live node list of elements that matched the given selectors - */ - JsArray querySelectorAll(String selectors); - - /** - * Sets an attribute value for this node. - * - * @param name - * the attribute name - * @param value - * the attribute value - */ - void setAttribute(String name, String value); - - /** - * Removes an attribute from this node. - * - * @param name - * the attribute name - */ - void removeAttribute(String name); -} diff --git a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomNode.java b/flow-client/src/main/java/com/vaadin/client/flow/dom/DomNode.java deleted file mode 100644 index cba9f0de999..00000000000 --- a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomNode.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.client.flow.dom; - -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; - -import com.vaadin.client.flow.collection.JsArray; - -import elemental.dom.Node; - -/** - * Node that has all methods from - * Node API - * that have been overridden in - * Polymer - * DOM module. - *

- * No instances of this class should be created directly, but instead - * {@link DomApi#wrap(elemental.dom.Node)} should be used - * - * @author Vaadin Ltd - * @since 1.0 - */ -@JsType(isNative = true) -public interface DomNode { - - /** - * Returns the childNodes property. - *

- * NOTE: returns an array since that is what the Polymer.dom API does, and - * luckily native NodeList items can be accessed array like with - * list[index]. - *

- * This means that only {@link JsArray#get(int)} and - * {@link JsArray#length()} methods can be used from the returned "array". - * - * @return the child nodes - */ - @JsProperty - JsArray getChildNodes(); - - /** - * Returns the firstChild property. - * - * @return the first child - */ - @JsProperty - Node getFirstChild(); - - /** - * Returns the lastChild property. - * - * @return the last child - */ - @JsProperty - Node getLastChild(); - - /** - * Returns the nextSibling property. - * - * @return the next sibling - */ - @JsProperty - Node getNextSibling(); - - /** - * Returns the previousSibling property. - * - * @return the previous sibling - */ - @JsProperty - Node getPreviousSibling(); - - /** - * Returns the textContent property. - * - * @return the text content - */ - @JsProperty - String getTextContent(); - - /** - * A setter for the childNodes property. - * - * @param textContent - * the text content to set - */ - @JsProperty - void setTextContent(String textContent); - - /** - * Insert a node as the last child node of this element. - * - * @param node - * the node to append - */ - void appendChild(Node node); - - /** - * Inserts the first Node given in a parameter immediately before the - * second, child of this element, Node. - * - * @param newChild - * the node to be inserted - * @param refChild - * the node before which newChild is inserted - */ - void insertBefore(Node newChild, Node refChild); - - /** - * Removes a child node from the current node, which much be a child of the - * current node. - * - * @param childNode - * the child node to remove - */ - void removeChild(Node childNode); - - /** - * Replaces one child Node of the current one with the second one given in - * parameter. - * - * @param newChild - * the new node to replace the oldChild. If it already exists in - * the DOM, it is first removed. - * @param oldChild - * is the existing child to be replaced. - */ - void replaceChild(Node newChild, Node oldChild); - - /** - * Clone a Node, and optionally, all of its contents. By default, it clones - * the content of the node. - * - * @param deep - * true if the children of the node should also be - * cloned, or false to clone only the specified - * node. - * @return a clone of this node - */ - Node cloneNode(boolean deep); - - /** - * Gets the parent node of this node. - * - * @return the parent node, not null if this node has no - * parent. - */ - @JsProperty - Node getParentNode(); - - /** - * The isSameNode() method for Node objects is a legacy alias the for the - * === strict equality operator. That is, it tests whether two nodes are the - * same (in other words, whether they reference the same object). - * - * @param node - * the node to test - * @return whether the nodes are the same - */ - boolean isSameNode(Node node); -} diff --git a/flow-client/src/test/java/com/vaadin/client/DomApiAbstractionUsageTest.java b/flow-client/src/test/java/com/vaadin/client/DomApiAbstractionUsageTest.java deleted file mode 100644 index adf18d89462..00000000000 --- a/flow-client/src/test/java/com/vaadin/client/DomApiAbstractionUsageTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.client; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.junit.Assert; -import org.junit.Test; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import com.vaadin.client.bootstrap.Bootstrapper; -import com.vaadin.client.flow.dom.DomApi; -import com.vaadin.client.flow.dom.DomElement; -import com.vaadin.client.flow.dom.DomNode; - -import elemental.dom.Document; -import elemental.dom.Element; -import elemental.dom.Node; -import elemental.dom.Text; -import elemental.html.AnchorElement; - -public class DomApiAbstractionUsageTest { - private static final Set ignoredClasses = Stream - .of(DomElement.class, DomNode.class, ResourceLoader.class, - BrowserInfo.class, SystemErrorHandler.class, Profiler.class) - .map(Class::getName).collect(Collectors.toSet()); - - private static final Set> ignoredElementalClasses = Stream - .of(Document.class, AnchorElement.class, Text.class) - .collect(Collectors.toSet()); - - private static final Set ignoredElementMethods = Stream - .of("getTagName", "addEventListener", "getOwnerDocument", - "hasAttribute", "getStyle", "getLocalName", "getAttribute", - "equals", "getClass", "getNamespaceURI") - .collect(Collectors.toSet()); - - private final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5) { - private boolean whitelistedClass = false; - private String className; - - @Override - public void visit(int version, int access, String name, - String signature, String superName, String[] interfaces) { - className = name.replace('/', '.'); - - String outerClassName = className.replaceAll("\\$.*", ""); - whitelistedClass = ignoredClasses.contains(outerClassName); - } - - @Override - public FieldVisitor visitField(int access, String name, String desc, - String signature, Object value) { - // Trim array markers (not efficient, but straightforward) - while (desc.startsWith("[")) { - desc = desc.substring(1); - } - - if (desc.startsWith("L")) { - // Lcom/foo/Foo; - String typeName = desc.substring(1, desc.length() - 1); - Class type = DomApiAbstractionUsageTest.getClass(typeName); - if (DomNode.class.isAssignableFrom(type)) { - Assert.fail(className + "." + name - + " references a wrapped node"); - } - } - return null; - } - - @Override - public MethodVisitor visitMethod(int access, String methodName, - String desc, String signature, String[] exceptions) { - if (whitelistedClass) { - return null; - } - - return new MethodVisitor(api) { - @Override - public void visitMethodInsn(int opcode, String targetClass, - String targetMethod, String targetDesc, - boolean inInterface) { - verifyMethod(className + "." + methodName, targetClass, - targetMethod); - } - }; - } - }; - - /** - * This tests that no API from {@link DomElement} or {@link DomNode} is used - * without wrapping it with a {@link DomApi#wrap(elemental.dom.Node)} call. - */ - @Test - public void testDomApiCodeNotUsed() throws IOException { - String classesPath = getClassesLocation(Bootstrapper.class); - - Files.walk(Paths.get(classesPath)) - .filter(path -> path.toString().endsWith(".class")) - .forEach(this::testClassFile); - } - - private void testClassFile(Path classFile) { - try (InputStream stream = new FileInputStream(classFile.toString())) { - ClassReader classReader = new ClassReader(stream); - - int flags = 0; - classReader.accept(classVisitor, flags); - } catch (IOException e) { - throw new RuntimeException(classFile.toString(), e); - } - } - - private static void verifyMethod(String callingMethod, - String targetClassName, String targetMethod) { - // Won't care about overhead of loading all - // classes since this is just a test - Class targetClass = getClass(targetClassName); - - if (!Node.class.isAssignableFrom(targetClass)) { - return; - } - - if (ignoredElementalClasses.contains(targetClass)) { - return; - } - - if ((Element.class == targetClass || Node.class == targetClass) - && ignoredElementMethods.contains(targetMethod)) { - return; - } - - Assert.fail(callingMethod + " calls " + targetClass.getName() + "." - + targetMethod); - } - - private static Class getClass(String targetClassName) { - try { - return Class.forName(targetClassName.replace('/', '.'), false, - DomApiAbstractionUsageTest.class.getClassLoader()); - } catch (ClassNotFoundException e) { - throw new RuntimeException(targetClassName, e); - } - } - - private String getClassesLocation(Class sampleClass) { - String sampleClassName = '/' + sampleClass.getName().replace('.', '/') - + ".class"; - - URL sampleClassLocation = sampleClass.getResource(sampleClassName); - - assert "file".equals(sampleClassLocation.getProtocol()); - - String sampleClassAbsolutePath; - try { - sampleClassAbsolutePath = Paths.get(sampleClassLocation.toURI()) - .toFile().getPath(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - assert sampleClassAbsolutePath - .endsWith(sampleClassName.replace('/', File.separatorChar)); - - return sampleClassAbsolutePath.substring(0, - sampleClassAbsolutePath.length() - sampleClassName.length()); - } -}