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 org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.htmlunit.util.MimeType;
20  import org.junit.jupiter.api.Test;
21  
22  /**
23   * Tests for {@link XMLDocument}.
24   *
25   * @author Ahmed Ashour
26   * @author Ronald Brill
27   * @author Frank Danek
28   */
29  public class XMLDocument2Test extends WebDriverTestCase {
30  
31      /**
32       * @throws Exception if the test fails
33       */
34      @Test
35      @Alerts({"myTarget,myData,7", "myTarget,myData", "<?myTarget myData?>"})
36      public void createProcessingInstruction() throws Exception {
37          final String html = DOCTYPE_HTML
38              + "<html><head>\n"
39              + "<script>\n"
40              + LOG_TITLE_FUNCTION
41              + "  function test() {\n"
42              + "    var doc = document.implementation.createDocument('', '', null);\n"
43              + "    var d = doc.createElement('doc');\n"
44              + "    d.setAttribute('fluffy', 'true');\n"
45              + "    d.setAttribute('numAttributes', '2');\n"
46              + "    doc.appendChild(d);\n"
47              + "    var pi = doc.createProcessingInstruction('myTarget', 'myData');\n"
48              + "    doc.insertBefore(pi, d);\n"
49              + "    log(pi.nodeName + ',' + pi.nodeValue + ',' + pi.nodeType);\n"
50              + "    log(pi.target + ',' + pi.data);\n"
51              + "    log(" + XMLDocumentTest.callSerializeXMLDocumentToString("pi") + ");\n"
52              + "  }\n"
53              + XMLDocumentTest.SERIALIZE_XML_DOCUMENT_TO_STRING_FUNCTION
54              + "</script></head><body onload='test()'>\n"
55              + "</body></html>";
56  
57          loadPageVerifyTitle2(html);
58      }
59  
60      /**
61       * @throws Exception if the test fails
62       */
63      @Test
64      @Alerts({"#cdata-section,abcdefghij,4", "abcdefghij", "<![CDATA[abcdefghij]]>"})
65      public void createCDATASection() throws Exception {
66          final String html = DOCTYPE_HTML
67              + "<html><head>\n"
68              + "<script>\n"
69              + LOG_TITLE_FUNCTION
70              + "  function test() {\n"
71              + "    var doc = document.implementation.createDocument('', '', null);\n"
72              + "    var d = doc.createElement('doc');\n"
73              + "    doc.appendChild(d);\n"
74              + "    var cdata = doc.createCDATASection('abcdefghij');\n"
75              + "    d.appendChild(cdata);\n"
76              + "    log(cdata.nodeName + ',' + cdata.nodeValue + ',' + cdata.nodeType);\n"
77              + "    log(cdata.data);\n"
78              + "    log(" + XMLDocumentTest.callSerializeXMLDocumentToString("cdata") + ");\n"
79              + "  }\n"
80              + XMLDocumentTest.SERIALIZE_XML_DOCUMENT_TO_STRING_FUNCTION
81              + "</script></head><body onload='test()'>\n"
82              + "</body></html>";
83  
84          loadPageVerifyTitle2(html);
85      }
86  
87      /**
88       * @throws Exception if the test fails
89       */
90      @Test
91      @Alerts({"#cdata-section,<>&?,4", "<>&?", "<![CDATA[<>&?]]>"})
92      public void createCDATASection_specialChars() throws Exception {
93          final String html = DOCTYPE_HTML
94              + "<html><head>\n"
95              + "<script>\n"
96              + LOG_TITLE_FUNCTION
97              + "  function test() {\n"
98              + "    var doc = document.implementation.createDocument('', '', null);\n"
99              + "    var d = doc.createElement('doc');\n"
100             + "    doc.appendChild(d);\n"
101             + "    var cdata = doc.createCDATASection('<>&?');\n"
102             + "    d.appendChild(cdata);\n"
103             + "    log(cdata.nodeName + ',' + cdata.nodeValue + ',' + cdata.nodeType);\n"
104             + "    log(cdata.data);\n"
105             + "    log(" + XMLDocumentTest.callSerializeXMLDocumentToString("cdata") + ");\n"
106             + "  }\n"
107             + XMLDocumentTest.SERIALIZE_XML_DOCUMENT_TO_STRING_FUNCTION
108             + "</script></head>\n"
109             + "<body onload='test()'>\n"
110             + "</body></html>";
111 
112         loadPageVerifyTitle2(html);
113     }
114 
115     /**
116      * @throws Exception if the test fails
117      */
118     @Test
119     @Alerts("createNode not available")
120     public void createNode() throws Exception {
121         final String html = DOCTYPE_HTML
122             + "<html><head>\n"
123             + "<script>\n"
124             + LOG_TITLE_FUNCTION
125             + "  function test() {\n"
126             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromString("'<root><child/></root>'") + ";\n"
127             + "    if (document.createNode) {\n"
128             + "      var node = doc.createNode(2, 'Sci-Fi', '');\n"
129             + "      doc.documentElement.childNodes.item(0).attributes.setNamedItem(node);\n"
130             + "      log(" + XMLDocumentTest.callSerializeXMLDocumentToString("doc.documentElement") + ");\n"
131             + "    } else { log('createNode not available'); }\n"
132             + "  }\n"
133             + XMLDocumentTest.LOAD_XML_DOCUMENT_FROM_STRING_FUNCTION
134             + XMLDocumentTest.SERIALIZE_XML_DOCUMENT_TO_STRING_FUNCTION
135             + "</script></head>\n"
136             + "<body onload='test()'>\n"
137             + "</body></html>";
138 
139         loadPageVerifyTitle2(html);
140     }
141 
142     /**
143      * @throws Exception if the test fails
144      */
145     @Test
146     @Alerts("createNode not available")
147     public void createNode_element() throws Exception {
148         final String html = DOCTYPE_HTML
149             + "<html><head>\n"
150             + "<script>\n"
151             + LOG_TITLE_FUNCTION
152             + "  function test() {\n"
153             + "    var doc = document.implementation.createDocument('', '', null);\n"
154             + "    if (document.createNode) {\n"
155             + "      var node = doc.createNode(1, 'test:element', 'uri:test');\n"
156             + "      log(node.localName);\n"
157             + "      log(node.prefix);\n"
158             + "      log(node.namespaceURI);\n"
159             + "      log(node.nodeName);\n"
160             + "    } else { log('createNode not available'); }\n"
161             + "  }\n"
162             + "</script></head>\n"
163             + "<body onload='test()'>\n"
164             + "</body></html>";
165         loadPageVerifyTitle2(html);
166     }
167 
168     /**
169      * @throws Exception if the test fails
170      */
171     @Test
172     @Alerts({"a", "null", "b"})
173     public void documentElementCaching() throws Exception {
174         final String html = DOCTYPE_HTML
175             + "<html><head>\n"
176             + "<script>\n"
177             + LOG_TITLE_FUNCTION
178             + "  function test() {\n"
179             + "    var doc = document.implementation.createDocument('', '', null);\n"
180             + "    var a = doc.createElement('a');\n"
181             + "    var b = doc.createElement('b');\n"
182             + "    doc.appendChild(a);\n"
183             + "    log(doc.documentElement.tagName);\n"
184             + "    doc.removeChild(a);\n"
185             + "    log(doc.documentElement);\n"
186             + "    doc.appendChild(b);\n"
187             + "    log(doc.documentElement.tagName);\n"
188             + "  }\n"
189             + "</script></head>\n"
190             + "<body onload='test()'>\n"
191             + "</body></html>";
192 
193         loadPageVerifyTitle2(html);
194     }
195 
196     /**
197      * @throws Exception if the test fails
198      */
199     @Test
200     @Alerts("a:b")
201     public void createElement_namespace() throws Exception {
202         final String html = DOCTYPE_HTML
203             + "<html><head>\n"
204             + "<script>\n"
205             + LOG_TITLE_FUNCTION
206             + "  function test() {\n"
207             + "    var doc = document.implementation.createDocument('', '', null);\n"
208             + "    var a = doc.createElement('a:b');\n"
209             + "    log(a.tagName);\n"
210             + "  }\n"
211             + "</script></head>\n"
212             + "<body onload='test()'>\n"
213             + "</body></html>";
214 
215         loadPageVerifyTitle2(html);
216     }
217 
218     /**
219      * Test for issue #1523.
220      *
221      * @throws Exception if the test fails
222      */
223     @Test
224     @Alerts("ReferenceError")
225     public void text() throws Exception {
226         final String html = DOCTYPE_HTML
227             + "<html><head>\n"
228             + "<script>\n"
229             + LOG_TITLE_FUNCTION
230             + "  function test() {\n"
231             + "    try {\n"
232             + "      new ActiveXObject('Microsoft.XMLDOM');\n"
233             + "    } catch(e) {\n"
234             + "      logEx(e);\n"
235             + "      return;\n"
236             + "    }\n"
237             + "    var xmldoc = new ActiveXObject('Microsoft.XMLDOM');\n"
238             + "    var xml = '<Envelope><Body>content</Body></Envelope>';\n"
239             + "    xmldoc.loadXML(xml);\n"
240 
241             + "    var expression = '/Envelope/Body';\n"
242             + "    var body = xmldoc.documentElement.selectSingleNode(expression);\n"
243             + "    log(body.text);\n"
244             + "    log(body.firstChild.text);\n"
245             + "  }\n"
246             + "</script></head>\n"
247             + "<body onload='test()'>\n"
248             + "</body></html>";
249         loadPageVerifyTitle2(html);
250     }
251 
252     /**
253      * @throws Exception if the test fails
254      */
255     @Test
256     @Alerts({"foo", "foo"})
257     public void firstChild_element() throws Exception {
258         final String html = DOCTYPE_HTML
259             + "<html><head>\n"
260             + "<script>\n"
261             + LOG_TITLE_FUNCTION
262             + "  function test() {\n"
263             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
264             + "    log(doc.firstChild.nodeName);\n"
265             + "    log(doc.documentElement.nodeName);\n"
266             + "  }\n"
267             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
268             + "</script></head>\n"
269             + "<body onload='test()'>\n"
270             + "</body></html>";
271 
272         final String xml =
273                "<foo>\n"
274              + "    <foofoo name='first'>something</foofoo>\n"
275              + "    <foofoo name='second'>something else</foofoo>\n"
276              + "</foo>";
277 
278         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
279         loadPageVerifyTitle2(html);
280     }
281 
282     /**
283      * @throws Exception if the test fails
284      */
285     @Test
286     @Alerts({"foo", "foo"})
287     // Xerces does not offer any way to access the XML declaration
288     public void firstChild_xmlDeclaration() throws Exception {
289         final String html = DOCTYPE_HTML
290             + "<html><head>\n"
291             + "<script>\n"
292             + LOG_TITLE_FUNCTION
293             + "  function test() {\n"
294             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
295             + "    log(doc.firstChild.nodeName);\n"
296             + "    log(doc.documentElement.nodeName);\n"
297             + "  }\n"
298             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
299             + "</script></head><body onload='test()'>\n"
300             + "</body></html>";
301 
302         final String xml =
303                "<?xml version=\"1.0\"?>\n"
304              + "<foo>\n"
305              + "  <foofoo name='first'>something</foofoo>\n"
306              + "  <foofoo name='second'>something else</foofoo>\n"
307              + "</foo>";
308 
309         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
310         loadPageVerifyTitle2(html);
311     }
312 
313     /**
314      * @throws Exception if the test fails
315      */
316     @Test
317     @Alerts({"apache", "foo"})
318     public void firstChild_processingInstruction() throws Exception {
319         final String html = DOCTYPE_HTML
320             + "<html><head>\n"
321             + "<script>\n"
322             + LOG_TITLE_FUNCTION
323             + "  function test() {\n"
324             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
325             + "    log(doc.firstChild.nodeName);\n"
326             + "    log(doc.documentElement.nodeName);\n"
327             + "  }\n"
328             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
329             + "</script></head>\n"
330             + "<body onload='test()'>\n"
331             + "</body></html>";
332 
333         final String xml =
334                "<?apache include file=\"header.html\" ?>\n"
335              + "<foo>\n"
336              + "  <foofoo name='first'>something</foofoo>\n"
337              + "  <foofoo name='second'>something else</foofoo>\n"
338              + "</foo>";
339 
340         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
341         loadPageVerifyTitle2(html);
342     }
343 
344     /**
345      * @throws Exception if the test fails
346      */
347     @Test
348     @Alerts({"dtd", "a"})
349     public void firstChild_documentType() throws Exception {
350         final String html = DOCTYPE_HTML
351             + "<html><head>\n"
352             + "<script>\n"
353             + LOG_TITLE_FUNCTION
354             + "  function test() {\n"
355             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
356             + "    log(doc.firstChild.nodeName);\n"
357             + "    log(doc.documentElement.nodeName);\n"
358             + "  }\n"
359             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
360             + "</script></head>\n"
361             + "<body onload='test()'>\n"
362             + "</body></html>";
363 
364         final String xml =
365               "<!DOCTYPE dtd [ <!ELEMENT a (b+)> <!ELEMENT b (#PCDATA)> ]>\n"
366             + "<a><b>1</b><b>2</b></a>";
367 
368         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
369         loadPageVerifyTitle2(html);
370     }
371 
372     /**
373      * @throws Exception if the test fails
374      */
375     @Test
376     @Alerts({"#comment", "foo"})
377     public void firstChild_comment() throws Exception {
378         final String html = DOCTYPE_HTML
379             + "<html><head>\n"
380             + "<script>\n"
381             + LOG_TITLE_FUNCTION
382             + "  function test() {\n"
383             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
384             + "    log(doc.firstChild.nodeName);\n"
385             + "    log(doc.documentElement.nodeName);\n"
386             + "  }\n"
387             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
388             + "</script></head>\n"
389             + "<body onload='test()'>\n"
390             + "</body></html>";
391 
392         final String xml =
393               "<!--comment-->\n"
394             + "<foo>\n"
395             + "  <foofoo name='first'>something</foofoo>\n"
396             + "  <foofoo name='second'>something else</foofoo>\n"
397             + "</foo>";
398 
399         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
400         loadPageVerifyTitle2(html);
401     }
402 
403     /**
404      * @throws Exception if the test fails
405      */
406     @Test
407     @Alerts({"foo", "fooc1", "null"})
408     public void firstElementChild() throws Exception {
409         final String html = DOCTYPE_HTML
410             + "<html><head><script>\n"
411             + LOG_TITLE_FUNCTION
412             + "  function test() {\n"
413             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
414 
415             + "    if (doc.firstElementChild == null) { log('not available'); return };\n"
416 
417             + "    log(doc.firstElementChild.nodeName);\n"
418             + "    log(doc.firstElementChild.firstElementChild.nodeName);\n"
419             + "    log(doc.firstElementChild.firstElementChild.firstElementChild);\n"
420             + "  }\n"
421             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
422             + "</script></head>\n"
423             + "<body onload='test()'>\n"
424             + "</body></html>";
425 
426         final String xml =
427                "<foo>\n"
428             + "    <fooc1 name='first'>something</fooc1>\n"
429             + "    <fooc2 name='second'>something else</fooc2>\n"
430             + "</foo>";
431 
432         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
433         loadPageVerifyTitle2(html);
434     }
435 
436     /**
437      * @throws Exception if the test fails
438      */
439     @Test
440     @Alerts({"foo", "fooc2", "null"})
441     public void lastElementChild() throws Exception {
442         final String html = DOCTYPE_HTML
443             + "<html><head>\n"
444             + "<script>\n"
445             + LOG_TITLE_FUNCTION
446             + "  function test() {\n"
447             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromFile("'" + URL_SECOND + "'") + ";\n"
448 
449             + "    if (doc.firstElementChild == null) { log('not available'); return };\n"
450 
451             + "    log(doc.lastElementChild.nodeName);\n"
452             + "    log(doc.firstElementChild.lastElementChild.nodeName);\n"
453             + "    log(doc.firstElementChild.firstElementChild.lastElementChild);\n"
454             + "  }\n"
455             + XMLDocumentTest.LOAD_NATIVE_XML_DOCUMENT_FROM_FILE_FUNCTION
456             + "</script></head>\n"
457             + "<body onload='test()'>\n"
458             + "</body></html>";
459 
460         final String xml =
461                "<foo>\n"
462              + "    <fooc1 name='first'>something</fooc1>\n"
463              + "    <fooc2 name='second'>something else</fooc2>\n"
464              + "</foo>";
465 
466         getMockWebConnection().setResponse(URL_SECOND, xml, MimeType.TEXT_XML);
467         loadPageVerifyTitle2(html);
468     }
469 
470     /**
471      * Test case for issue #1576.
472      * @throws Exception if the test fails
473      */
474     @Test
475     @Alerts({"name: item1", "id: 1", "id: 2", "name: item2", "name: item3", "id: 3"})
476     public void attributeOrder() throws Exception {
477         final String html = DOCTYPE_HTML
478             + "<html><head>\n"
479             + "<script>\n"
480             + LOG_TITLE_FUNCTION
481             + "  function test() {\n"
482             + "    var doc = " + XMLDocumentTest.callLoadXMLDocumentFromString(
483                     "'<items>"
484                         + "<item name=\"item1\" id=\"1\">value1</item>"
485                         + "<item id=\"2\" name=\"item2\">value2</item>"
486                         + "<item name=\"item3\" id=\"3\">value3</item>"
487                     + "</items>'") + ";\n"
488             + "    var items = doc.getElementsByTagName('item');\n"
489             + "    for(var i = 0; i < items.length; i++) {\n"
490             + "      var attribs = items[i].attributes;\n"
491             + "      for(var j = 0; j < attribs.length; j++) {\n"
492             + "        log(attribs[j].name + ': ' + attribs[j].value);\n"
493             + "      }\n"
494             + "    }\n"
495             + "  }\n"
496             + XMLDocumentTest.LOAD_XML_DOCUMENT_FROM_STRING_FUNCTION
497             + "</script></head>\n"
498             + "<body onload='test()'>\n"
499             + "</body></html>";
500 
501         loadPageVerifyTitle2(html);
502     }
503 }