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