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.xml;
16  
17  import java.io.IOException;
18  import java.io.Serializable;
19  import java.util.function.Predicate;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.htmlunit.StringWebResponse;
24  import org.htmlunit.WebResponse;
25  import org.htmlunit.WebWindow;
26  import org.htmlunit.html.DomAttr;
27  import org.htmlunit.html.DomElement;
28  import org.htmlunit.html.DomNode;
29  import org.htmlunit.html.HtmlElement;
30  import org.htmlunit.javascript.HtmlUnitScriptable;
31  import org.htmlunit.javascript.JavaScriptEngine;
32  import org.htmlunit.javascript.configuration.JsxClass;
33  import org.htmlunit.javascript.configuration.JsxConstructor;
34  import org.htmlunit.javascript.configuration.JsxFunction;
35  import org.htmlunit.javascript.host.Element;
36  import org.htmlunit.javascript.host.dom.Attr;
37  import org.htmlunit.javascript.host.dom.Document;
38  import org.htmlunit.javascript.host.html.HTMLCollection;
39  import org.htmlunit.svg.SvgElement;
40  import org.htmlunit.util.StringUtils;
41  import org.htmlunit.xml.XmlPage;
42  
43  /**
44   * JavaScript host object for {@code XMLDocument}.
45   *
46   * @author Ahmed Ashour
47   * @author Marc Guillemot
48   * @author Sudhan Moghe
49   * @author Ronald Brill
50   * @author Chuck Dumont
51   * @author Frank Danek
52   * @author Sven Strickroth
53   *
54   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLDocument">MDN Documentation</a>
55   */
56  @JsxClass
57  public class XMLDocument extends Document {
58  
59      private static final Log LOG = LogFactory.getLog(XMLDocument.class);
60  
61      /**
62       * Creates a new instance.
63       */
64      public XMLDocument() {
65          this(null);
66      }
67  
68      /**
69       * Creates an instance of this object.
70       */
71      @Override
72      @JsxConstructor
73      public void jsConstructor() {
74          super.jsConstructor();
75      }
76  
77      /**
78       * Creates a new instance with an associated {@link XmlPage}.
79       *
80       * @param enclosingWindow the enclosing window
81       */
82      public XMLDocument(final WebWindow enclosingWindow) {
83          super();
84  
85          if (enclosingWindow != null) {
86              try {
87                  final XmlPage page = new XmlPage((WebResponse) null, enclosingWindow);
88                  setDomNode(page);
89              }
90              catch (final IOException e) {
91                  throw JavaScriptEngine.reportRuntimeError("IOException: " + e);
92              }
93          }
94      }
95  
96      /**
97       * Loads an XML document from the supplied string.
98       *
99       * @param strXML a string containing the XML to load into this document;
100      *        this string can contain an entire XML document or a well-formed fragment
101      * @return {@code true} if the load succeeded; {@code false} if the load failed
102      */
103     public boolean loadXML(final String strXML) {
104         final WebWindow webWindow = getWindow().getWebWindow();
105         try {
106             if (StringUtils.isEmptyOrNull(strXML)) {
107                 throw new IOException("Error parsing XML '" + strXML + "'");
108             }
109 
110             final WebResponse webResponse = new StringWebResponse(strXML, webWindow.getEnclosedPage().getUrl());
111 
112             final XmlPage page = new XmlPage(webResponse, webWindow, false);
113             setDomNode(page);
114             return true;
115         }
116         catch (final IOException e) {
117             if (LOG.isDebugEnabled()) {
118                 LOG.debug("Error parsing XML\n" + strXML, e);
119             }
120 
121             try {
122                 final XmlPage page = createParserErrorXmlPage("Syntax Error", webWindow);
123                 setDomNode(page);
124             }
125             catch (final IOException ex) {
126                 LOG.error("Could not handle ParserError", e);
127             }
128 
129             return false;
130         }
131     }
132 
133     private static XmlPage createParserErrorXmlPage(final String message, final WebWindow webWindow)
134             throws IOException {
135         final String xml = "<parsererror xmlns=\"http://www.mozilla.org/newlayout/xml/parsererror.xml\">\n"
136             + message + "\n"
137             + "<sourcetext></sourcetext>\n"
138             + "</parsererror>";
139 
140         final WebResponse webResponse = new StringWebResponse(xml, webWindow.getEnclosedPage().getUrl());
141 
142         return new XmlPage(webResponse, webWindow, false);
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     @Override
149     public HtmlUnitScriptable makeScriptableFor(final DomNode domNode) {
150         final HtmlUnitScriptable scriptable;
151 
152         // TODO: cleanup, getScriptObject() should be used!!!
153         if (domNode instanceof DomElement && !(domNode instanceof HtmlElement)) {
154             if (domNode instanceof SvgElement) {
155                 final Class<? extends HtmlUnitScriptable> javaScriptClass
156                     = ((JavaScriptEngine) getWindow().getWebWindow().getWebClient()
157                         .getJavaScriptEngine()).getJavaScriptClass(domNode.getClass());
158                 try {
159                     scriptable = javaScriptClass.getDeclaredConstructor().newInstance();
160                 }
161                 catch (final Exception e) {
162                     throw JavaScriptEngine.throwAsScriptRuntimeEx(e);
163                 }
164             }
165             else {
166                 scriptable = new Element();
167             }
168         }
169         else if (domNode instanceof DomAttr) {
170             scriptable = new Attr();
171         }
172         else {
173             return super.makeScriptableFor(domNode);
174         }
175 
176         scriptable.setPrototype(getPrototype(scriptable.getClass()));
177         scriptable.setParentScope(getParentScope());
178         scriptable.setDomNode(domNode);
179         return scriptable;
180     }
181 
182     /**
183      * {@inheritDoc}
184      */
185     @Override
186     @JsxFunction
187     public HTMLCollection getElementsByTagName(final String tagName) {
188         final DomNode firstChild = getDomNodeOrDie().getFirstChild();
189         if (firstChild == null) {
190             return HTMLCollection.emptyCollection(getWindow().getDomNodeOrDie());
191         }
192 
193         final HTMLCollection elements = new HTMLCollection(getDomNodeOrDie(), false);
194 
195         elements.setIsMatchingPredicate(
196                 (Predicate<DomNode> & Serializable)
197                 node -> {
198                     final String nodeName = node.getNodeName();
199                     return nodeName.equals(tagName);
200                 });
201         return elements;
202     }
203 }