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 d49c634bf38..cff38ba3e1d 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); } } @@ -1134,8 +1131,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 @@ -1154,9 +1151,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()); } } @@ -1171,11 +1167,10 @@ private void removeAllChildren(Node htmlNode) { private void removeAllChildrenAfterReplacement(BindingContext context) { // getChildNodes returns the live DOM child list, so the nodes to remove // are collected before anything is inserted - JsArray liveChildren = DomApi.wrap(context.htmlNode) - .getChildNodes(); + elemental.dom.NodeList liveChildren = context.htmlNode.getChildNodes(); JsArray replacedChildren = JsCollections.array(); - for (int i = 0; i < liveChildren.length(); i++) { - replacedChildren.push(liveChildren.get(i)); + for (int i = 0; i < liveChildren.getLength(); i++) { + replacedChildren.push(liveChildren.item(i)); } Reactive.addPostFlushListener( @@ -1196,9 +1191,8 @@ private void removeReplacedChildren(BindingContext context, * the new contents, and a node that the server moved to another * parent now belongs to that parent. Neither may be removed here. */ - if (!keptChildren.has(child) - && DomApi.wrap(child).getParentNode() == htmlNode) { - DomApi.wrap(htmlNode).removeChild(child); + if (!keptChildren.has(child) && child.getParentNode() == htmlNode) { + htmlNode.removeChild(child); } } } @@ -1217,8 +1211,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; @@ -1238,11 +1231,10 @@ 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(); } } @@ -1250,9 +1242,9 @@ private static Node getFirstNodeMappedAsStateNode( NodeList mappedNodeChildren, Node htmlNode) { JsSet mappedDomNodes = getMappedDomNodes(mappedNodeChildren); - 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); if (mappedDomNodes.has(clientNode)) { return clientNode; } @@ -1572,12 +1564,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++) { @@ -1657,7 +1648,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); @@ -1674,7 +1665,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 @@ -1687,7 +1678,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); @@ -1695,7 +1686,7 @@ private static int getStateNodeForElement(JsArray searchStack, return stateNode.getId(); } } - targetNode = DomApi.wrap(targetNode.getParentNode()); + targetNode = targetNode.getParentNode(); } return -1; } @@ -1706,15 +1697,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 1ea26afdf85..00000000000 --- a/flow-client/src/main/java/com/vaadin/client/flow/dom/DomApi.java +++ /dev/null @@ -1,45 +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 elemental.dom.Node; - -/** - * Access point for DOM API. All operations and interactions with DOM nodes and - * elements should go through this class. - * - * @author Vaadin Ltd - * @since 1.0 - */ -public class DomApi { - - 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) { - return (DomElement) node; - } - -} 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()); - } -}