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