View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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 static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
18  import static org.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
19  import static org.htmlunit.javascript.configuration.SupportedBrowser.FF;
20  
21  import org.htmlunit.corejs.javascript.Context;
22  import org.htmlunit.corejs.javascript.Function;
23  import org.htmlunit.corejs.javascript.Scriptable;
24  import org.htmlunit.corejs.javascript.VarScope;
25  import org.htmlunit.cssparser.parser.CSSException;
26  import org.htmlunit.html.DomDocumentFragment;
27  import org.htmlunit.html.DomNode;
28  import org.htmlunit.html.HtmlPage;
29  import org.htmlunit.javascript.HtmlUnitScriptable;
30  import org.htmlunit.javascript.JavaScriptEngine;
31  import org.htmlunit.javascript.configuration.JsxClass;
32  import org.htmlunit.javascript.configuration.JsxConstructor;
33  import org.htmlunit.javascript.configuration.JsxFunction;
34  import org.htmlunit.javascript.configuration.JsxGetter;
35  import org.htmlunit.javascript.host.Element;
36  import org.htmlunit.javascript.host.html.HTMLCollection;
37  
38  /**
39   * A JavaScript object for {@code DocumentFragment}.
40   *
41   * @author Ahmed Ashour
42   * @author Frank Danek
43   * @author Ronald Brill
44   *
45   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment">MDN Documentation</a>
46   * @see <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-core.html#ID-B63ED1A3">
47   *     W3C Dom Level 1</a>
48   */
49  @JsxClass(domClass = DomDocumentFragment.class)
50  public class DocumentFragment extends Node {
51  
52      /**
53       * JavaScript constructor.
54       */
55      @Override
56      @JsxConstructor
57      public void jsConstructor() {
58          super.jsConstructor();
59  
60          final HtmlPage page = (HtmlPage) getWindow().getWebWindow().getEnclosedPage();
61          final DomDocumentFragment fragment = new DomDocumentFragment(page);
62          setDomNode(fragment);
63      }
64  
65      /**
66       * Inserts a set of Node objects or string objects after the last child
67       * of the document fragment. String objects are inserted as equivalent Text nodes.
68       * @param context the context
69       * @param scope the scope
70       * @param thisObj this object
71       * @param args the arguments
72       * @param function the function
73       */
74      @JsxFunction
75      public static void append(final Context context, final VarScope scope,
76              final Scriptable thisObj, final Object[] args, final Function function) {
77          Node.append(context, thisObj, args, function);
78      }
79  
80      /**
81       * Inserts a set of Node objects or string objects before the first child
82       * of the document fragment. String objects are inserted as equivalent Text nodes.
83       * @param context the context
84       * @param scope the scope
85       * @param thisObj this object
86       * @param args the arguments
87       * @param function the function
88       */
89      @JsxFunction
90      public static void prepend(final Context context, final VarScope scope,
91              final Scriptable thisObj, final Object[] args, final Function function) {
92          Node.prepend(context, thisObj, args, function);
93      }
94  
95      /**
96       * Replaces the existing children of a DocumentFragment with a specified
97       * new set of children. These can be string or Node objects.
98       * @param context the context
99       * @param scope the scope
100      * @param thisObj this object
101      * @param args the arguments
102      * @param function the function
103      */
104     @JsxFunction
105     public static void replaceChildren(final Context context, final VarScope scope,
106             final Scriptable thisObj, final Object[] args, final Function function) {
107         Node.replaceChildren(context, thisObj, args, function);
108     }
109 
110     /**
111      * Moves a given Node inside the invoking node as a direct child, before a given reference node.
112      *
113      * @param context the JavaScript context
114      * @param scope the scope
115      * @param thisObj the scriptable
116      * @param args the arguments passed into the method
117      * @param function the function
118      */
119     @JsxFunction({CHROME, EDGE, FF})
120     public static void moveBefore(final Context context, final VarScope scope,
121             final Scriptable thisObj, final Object[] args, final Function function) {
122         Node.moveBefore(context, scope, thisObj, args, function);
123     }
124 
125     /**
126      * Retrieves all element nodes from descendants of the starting element node that match any selector
127      * within the supplied selector strings.
128      * The NodeList object returned by the querySelectorAll() method must be static, not live.
129      * @param selectors the selectors
130      * @return the static node list
131      */
132     @JsxFunction
133     public NodeList querySelectorAll(final String selectors) {
134         try {
135             return NodeList.staticNodeList(getParentScope(), getDomNodeOrDie().querySelectorAll(selectors));
136         }
137         catch (final CSSException e) {
138             throw JavaScriptEngine.reportRuntimeError("An invalid or illegal selector was specified (selector: '"
139                     + selectors + "' error: " + e.getMessage() + ").");
140         }
141     }
142 
143     /**
144      * Returns the first element within the document that matches the specified group of selectors.
145      * @param selectors the selectors
146      * @return null if no matches are found; otherwise, it returns the first matching element
147      */
148     @JsxFunction
149     public Node querySelector(final String selectors) {
150         try {
151             final DomNode node = getDomNodeOrDie().querySelector(selectors);
152             if (node != null) {
153                 return node.getScriptableObject();
154             }
155             return null;
156         }
157         catch (final CSSException e) {
158             throw JavaScriptEngine.reportRuntimeError("An invalid or illegal selector was specified (selector: '"
159                     + selectors + "' error: " + e.getMessage() + ").");
160         }
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     public Object getDefaultValue(final Class<?> hint) {
168         if (String.class.equals(hint) || hint == null) {
169             return "[object " + getClassName() + "]";
170         }
171         return super.getDefaultValue(hint);
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     @JsxGetter
179     public int getChildElementCount() {
180         return super.getChildElementCount();
181     }
182 
183     /**
184      * {@inheritDoc}
185      */
186     @Override
187     @JsxGetter
188     public Element getFirstElementChild() {
189         return super.getFirstElementChild();
190     }
191 
192     /**
193      * {@inheritDoc}
194      */
195     @Override
196     @JsxGetter
197     public Element getLastElementChild() {
198         return super.getLastElementChild();
199     }
200 
201     /**
202      * {@inheritDoc}
203      */
204     @Override
205     @JsxGetter
206     public HTMLCollection getChildren() {
207         return super.getChildren();
208     }
209 
210     /**
211      * Returns the element with the specified ID, or {@code null} if that element could not be found.
212      * @param id the ID to search for
213      * @return the element, or {@code null} if it could not be found
214      */
215     @JsxFunction
216     public HtmlUnitScriptable getElementById(final Object id) {
217         if (id == null || JavaScriptEngine.isUndefined(id)) {
218             return null;
219         }
220         final String idString = JavaScriptEngine.toString(id);
221         if (idString == null || idString.length() == 0) {
222             return null;
223         }
224         for (final DomNode child : getDomNodeOrDie().getChildren()) {
225             final Element elem = child.getScriptableObject();
226             if (idString.equals(elem.getId())) {
227                 return elem;
228             }
229         }
230         return null;
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     public Node getRootNode() {
238         return this;
239     }
240 }