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