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.html.parser;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.html.HtmlPageTest;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.htmlunit.junit.annotation.HtmlUnitNYI;
21  import org.junit.jupiter.api.Test;
22  import org.openqa.selenium.WebDriver;
23  
24  /**
25   * Test class for {@link HTMLParser}.
26   *
27   * @author Christian Sell
28   * @author Marc Guillemot
29   * @author Ahmed Ashour
30   * @author Sudhan Moghe
31   * @author Frank Danek
32   * @author Ronald Brill
33   */
34  public class HTMLParser4Test extends WebDriverTestCase {
35  
36      /**
37       * Tests that inserted TBODY and TFOOT don't conflict.
38       * @throws Exception failure
39       */
40      @Test
41      @Alerts("TABLE")
42      public void table_tfoot() throws Exception {
43          final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
44              + "<html><body>"
45              + "<table><tr><td>hello</td></tr>\n"
46              + "<tfoot id='tf'><tr><td>foot</td></tr></tfoot>"
47              + "</table>\n"
48              + "<script>\n"
49              + LOG_TITLE_FUNCTION
50              + "log(document.getElementById('tf').parentNode.nodeName);\n"
51              + "</script>\n"
52              + "</body></html>";
53  
54          loadPageVerifyTitle2(html);
55      }
56  
57      /**
58       * Test for the condition when there is a <tt>&lt;form&gt;</tt> inside of a <tt>&lt;table&gt;</tt> and before
59       * a <tt>&lt;tr&gt;</tt>.
60       * @throws Exception failure
61       */
62      @Test
63      @Alerts("myForm")
64      public void badlyFormedHTML() throws Exception {
65          final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
66              + "<html><head>\n"
67              + "  <script>\n"
68              + LOG_TITLE_FUNCTION
69              + "    function test() {\n"
70              + "      log(document.getElementById('myInput').form.id);\n"
71              + "    }\n"
72              + "  </script>\n"
73              + "</head>\n"
74              + "<body onload='test()'>\n"
75              + "  <table>\n"
76              + "    <form name='myForm' action='foo' id='myForm'>\n"
77              + "      <tr><td>\n"
78              + "      <input type='text' name='myInput' id='myInput'/>\n"
79              + "      </td></tr>\n"
80              + "    </form>\n"
81              + "  </table>\n"
82              + "</body></html>";
83  
84          loadPageVerifyTitle2(html);
85      }
86  
87      /**
88       * @throws Exception failure
89       */
90      @Test
91      @Alerts({"4", "[object HTMLScriptElement]", "[object Text]",
92                  "[object HTMLTitleElement]", "[object Text]"})
93      public void badlyFormedHTML_scriptBeforeHead() throws Exception {
94          final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
95              + "<script>var i = 7;</script>\n"
96              + "<html>\n"
97              + "  <head>\n"
98              + "    <title></title>\n"
99              + "  </head>\n"
100             + "  <body>\n"
101             + "    <script>\n"
102             + LOG_TITLE_FUNCTION
103             + "      var headChildren = document.getElementsByTagName('head')[0].childNodes;\n"
104             + "      log(headChildren.length);\n"
105             + "      log(headChildren[0]);\n"
106             + "      log(headChildren[1]);\n"
107             + "      log(headChildren[2]);\n"
108             + "      log(headChildren[3]);\n"
109             + "    </script>\n"
110             + "  </body>\n"
111             + "</html>";
112 
113         loadPageVerifyTitle2(html);
114     }
115 
116     /**
117      * @throws Exception failure
118      */
119     @Test
120     public void badlyFormedHTML_wrongHeadConfusesStack() throws Exception {
121         final String html =
122             "<table\n"
123             + "<t:head>\n"
124             + "<t:head>\n"
125             + "<t:head>\n"
126             + "<t:head>\n"
127             + "<table>\n"
128             + "<table>";
129 
130         loadPage2(html);
131     }
132 
133     /**
134      * @throws Exception failure
135      */
136     @Test
137     @Alerts({"4", "[object HTMLScriptElement]", "[object Text]",
138                 "[object HTMLTitleElement]", "[object Text]"})
139     public void badlyFormedHTML_scriptBeforeDoctype() throws Exception {
140         final String html = "<script>var i = 7;</script>\n"
141             + HtmlPageTest.STANDARDS_MODE_PREFIX_
142             + "<html>\n"
143             + "  <head>\n"
144             + "    <title></title>\n"
145             + "  </head>\n"
146             + "  <body>\n"
147             + "    <script>\n"
148             + LOG_TITLE_FUNCTION
149             + "      var headChildren = document.getElementsByTagName('head')[0].childNodes;\n"
150             + "      log(headChildren.length);\n"
151             + "      log(headChildren[0]);\n"
152             + "      log(headChildren[1]);\n"
153             + "      log(headChildren[2]);\n"
154             + "      log(headChildren[3]);\n"
155             + "    </script>\n"
156             + "  </body>\n"
157             + "</html>";
158 
159         loadPageVerifyTitle2(html);
160     }
161 
162     /**
163      * @throws Exception failure
164      */
165     @Test
166     @Alerts({"4", "[object HTMLParagraphElement]", "[object Text]",
167                 "[object HTMLScriptElement]", "[object Text]"})
168     @HtmlUnitNYI(CHROME = {"3", "[object HTMLParagraphElement]", "[object HTMLScriptElement]",
169                            "[object Text]", "undefined"},
170             EDGE = {"3", "[object HTMLParagraphElement]", "[object HTMLScriptElement]",
171                     "[object Text]", "undefined"},
172             FF = {"3", "[object HTMLParagraphElement]", "[object HTMLScriptElement]",
173                   "[object Text]", "undefined"},
174             FF_ESR = {"3", "[object HTMLParagraphElement]", "[object HTMLScriptElement]",
175                       "[object Text]", "undefined"})
176     public void badlyFormedHTML_scriptAfterHtml() throws Exception {
177         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
178             + "<html>\n"
179             + "  <head>\n"
180             + "    <script>\n"
181             + LOG_TITLE_FUNCTION
182             + "      function test() {\n"
183             + "        var headChildren = document.getElementsByTagName('body')[0].childNodes;\n"
184             + "        log(headChildren.length);\n"
185             + "        log(headChildren[0]);\n"
186             + "        log(headChildren[1]);\n"
187             + "        log(headChildren[2]);\n"
188             + "        log(headChildren[3]);\n"
189             + "      }\n"
190             + "    </script>\n"
191             + "  </head>\n"
192             + "  <body onload='test()'><p>HtmlUnit</p></body>\n"
193             + "</html>"
194             + "<script>var i = 7;</script>\n";
195 
196         loadPageVerifyTitle2(html);
197     }
198 
199     /**
200      * @throws Exception failure
201      */
202     @Test
203     @Alerts({"from script", "from body"})
204     public void badlyFormedHTML_duplicateHead() throws Exception {
205         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
206             + "<html>\n"
207             + "  <head>\n"
208             + "  <head>\n"
209             + "    <script src='script.js'></script>\n"
210             + "  </head>\n"
211             + "  </head>\n"
212             + "  <body>\n"
213             + "    <p>HtmlUnit</p>\n"
214             + "    <script>window.name += 'from body' + '\\u00a7';</script>\n"
215             + "  </body>\n"
216             + "</html>";
217 
218         getMockWebConnection().setDefaultResponse("window.name += 'from script' + '\\u00a7'", 200, "OK", null);
219 
220         loadPage2(html);
221         verifyWindowName2(getWebDriver(), getExpectedAlerts());
222     }
223 
224     /**
225      * @throws Exception failure
226      */
227     @Test
228     @Alerts("<html><head><title>foo</title></head>"
229             + "<body><script>window.name += document.documentElement.outerHTML + '\\u00a7';</script></body></html>")
230     public void badlyFormedHTML_duplicateHeadStructure() throws Exception {
231         shutDownAll();
232 
233         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
234             + "<html>"
235             + "<head>"
236             + "<head>"
237             + "<title>foo</title>"
238             + "</head>"
239             + "</head>"
240             + "<body>"
241             + "<script>window.name += document.documentElement.outerHTML + '\\u00a7';</script>"
242             + "</body>"
243             + "</html>";
244 
245         loadPage2(html);
246         verifyWindowName2(getWebDriver(), getExpectedAlerts());
247     }
248 
249     /**
250      * @throws Exception failure
251      */
252     @Test
253     @Alerts("<p title=\"Nimbus\ufffd X\">Nimbus\ufffd X</p>")
254     public void badlyFormedHTML_invalidNumericCharacterReference() throws Exception {
255         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
256             + "<html>\n"
257             + "  <head>\n"
258             + "  </head>\n"
259             + "  <body>\n"
260             + "    <div id='tester'><p title='Nimbus&#84823000 X'>Nimbus&#84823000 X</p></div>\n"
261             + "    <script>\n"
262             + LOG_TITLE_FUNCTION
263             + "      log(document.getElementById('tester').innerHTML);\n"
264             + "    </script>\n"
265             + "  </body>\n"
266             + "</html>";
267 
268         loadPageVerifyTitle2(html);
269     }
270 
271     /**
272      * Test when an illegal tag is found in head as some websites do.
273      * @throws Exception failure
274      */
275     @Test
276     @Alerts("first")
277     public void unknownTagInHead() throws Exception {
278         // Note: the <meta> tag in this test is quite important because
279         // I could adapt the TagBalancer to make it work except with this <meta http-equiv...
280         // (it worked with <meta name=...)
281         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
282             + "<html><head><mainA3>\n"
283             + "  <meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1'>\n"
284             + "  <title>first</title>\n"
285             + "  <script>\n"
286             + LOG_WINDOW_NAME_FUNCTION
287             + "    function test() {\n"
288             + "      log(document.title);\n"
289             + "    }\n"
290             + "  </script>\n"
291             + "</head>\n"
292             + "<body onload='test()'>\n"
293             + "</body></html>";
294 
295         loadPage2(html);
296         verifyWindowName2(getWebDriver(), getExpectedAlerts());
297     }
298 
299     /**
300      * @throws Exception failure
301      */
302     @Test
303     @Alerts({"false", "true"})
304     public void duplicatedAttribute() throws Exception {
305         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
306             + "<html><head>\n"
307             + "</head>\n"
308             + "  <script>\n"
309             + LOG_TITLE_FUNCTION
310             + "    function test() {\n"
311             + "      log(document.getElementById('foo') == null);\n"
312             + "      log(document.getElementById('bla') == null);\n"
313             + "    }\n"
314             + "  </script>\n"
315             + "</head>\n"
316             + "<body onload='test()'>\n"
317             + "  <span id='foo' id='bla'></span>"
318             + "</body></html>";
319 
320         loadPageVerifyTitle2(html);
321     }
322 
323     /**
324      * @throws Exception failure
325      */
326     @Test
327     @Alerts({"1", "3", "[object HTMLScriptElement]",
328                 "[object HTMLUnknownElement]", "[object HTMLUnknownElement]", "[object HTMLFormElement]"})
329     public void namespace() throws Exception {
330         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
331             + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
332             + "<html xmlns='http://www.w3.org/1999/xhtml' xmlns:app='http://www.appcelerator.org'>\n"
333             + "<head>\n"
334             + "<script>\n"
335             + LOG_TITLE_FUNCTION
336             + "  function test() {\n"
337             + "    log(document.getElementById('script1'));\n"
338             + "    log(document.getElementById('script2'));\n"
339             + "    log(document.getElementById('message1'));\n"
340             + "    log(document.getElementById('form1'));\n"
341             + "  }\n"
342             + "</script>\n"
343             + "</head>\n"
344             + "<body onload='test()'>\n"
345             + "<script id='script1'>log(1)</script>\n"
346             + "<app:script id='script2'>log(2)</app:script>\n"
347             + "<script>log(3)</script>\n"
348             + "<app:message name='r:tasks.request' id='message1'>hello</app:message>\n"
349             + "<form id='form1' xmlns='http://org.pentaho'/>\n"
350             + "</body></html>";
351 
352         loadPageVerifyTitle2(html);
353     }
354 
355     /**
356      * @throws Exception failure
357      */
358     @Test
359     @Alerts({"1",
360                 "[object Element]", "app:script,app:script,http://www.appcelerator.org,app,script",
361                 "[object HTMLScriptElement]", "SCRIPT,SCRIPT,http://www.w3.org/1999/xhtml,null,script",
362                 "[object HTMLUnknownElement]", "APP:SCRIPT,APP:SCRIPT,http://www.w3.org/1999/xhtml,null,app:script"})
363     public void namespace2() throws Exception {
364         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
365             + "<html xmlns='http://www.w3.org/1999/xhtml' xmlns:app='http://www.appcelerator.org'>\n"
366             + "<head>\n"
367             + "<script>\n"
368             + LOG_TITLE_FUNCTION
369             + "  function test() {\n"
370             + "    try {\n"
371             + "      div = document.createElementNS('http://www.appcelerator.org', 'app:script');\n"
372             + "      debug(div);\n"
373             + "    } catch(e) {log('createElementNS() is not defined')}\n"
374             + "    debug(document.getElementById('script1'));\n"
375             + "    debug(document.getElementById('script2'));\n"
376             + "  }\n"
377             + "  function debug(e) {\n"
378             + "    log(e);\n"
379             + "    log(e.nodeName + ',' + e.tagName + ',' + e.namespaceURI + ',' + e.prefix + ',' + e.localName);\n"
380             + "  }\n"
381             + "</script>\n"
382             + "</head>\n"
383             + "<body onload='test()'>\n"
384             + "<script id='script1'>log(1)</script>\n"
385             + "<app:script id='script2'>log(2)</app:script>\n"
386             + "</body></html>";
387 
388         loadPageVerifyTitle2(html);
389     }
390 
391     /**
392      * See issue #1830.
393      * @throws Exception failure
394      */
395     @Test
396     @Alerts({"[object HTMLHeadElement]",
397                 "HEAD,HEAD,http://www.w3.org/1999/xhtml,null,head",
398                 "[object HTMLBodyElement]", "BODY,BODY,http://www.w3.org/1999/xhtml,null,body"})
399     public void namespace_svg() throws Exception {
400         final String html =
401             "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
402                             + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
403             + "<html xmlns=\"http://www.w3.org/2000/svg\">\n"
404             + "<head>\n"
405             + "<script>\n"
406             + LOG_TITLE_FUNCTION
407             + "  function test() {\n"
408             + "    elem = document.getElementsByTagName('head')[0];\n"
409             + "    debug(elem);\n"
410             + "    elem = document.getElementsByTagName('body')[0];\n"
411             + "    debug(elem);\n"
412             + "  }\n"
413             + "  function debug(e) {\n"
414             + "    log(e);\n"
415             + "    log(e.nodeName + ',' + e.tagName + ',' + e.namespaceURI + ',' + e.prefix + ',' + e.localName);\n"
416             + "  }\n"
417             + "</script>\n"
418             + "</head>\n"
419             + "<body onload='test()'>\n"
420             + "</body></html>";
421 
422         loadPageVerifyTitle2(html);
423     }
424 
425     /**
426      * See issue #1830.
427      * @throws Exception failure
428      */
429     @Test
430     @Alerts({"[object HTMLHeadElement]",
431                 "HEAD,HEAD,http://www.w3.org/1999/xhtml,null,head",
432                 "[object HTMLBodyElement]", "BODY,BODY,http://www.w3.org/1999/xhtml,null,body",
433                 "[object SVGGElement]", "g,g,http://www.w3.org/2000/svg,null,g"})
434     public void svg_withoutNamespace() throws Exception {
435         final String html =
436             "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
437                             + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
438             + "<html>\n"
439             + "<head>\n"
440             + "<script>\n"
441             + LOG_TITLE_FUNCTION
442             + "  function test() {\n"
443             + "    elem = document.getElementsByTagName('head')[0];\n"
444             + "    debug(elem);\n"
445             + "    elem = document.getElementsByTagName('body')[0];\n"
446             + "    debug(elem);\n"
447             + "    elem = document.getElementById('rectangles');\n"
448             + "    debug(elem);\n"
449             + "  }\n"
450             + "  function debug(e) {\n"
451             + "    log(e);\n"
452             + "    log(e.nodeName + ',' + e.tagName + ',' + e.namespaceURI + ',' + e.prefix + ',' + e.localName);\n"
453             + "  }\n"
454             + "</script>\n"
455             + "</head>\n"
456             + "<body onload='test()'>\n"
457             + "  <svg>\n"
458             + "    <g id='rectangles'>\n"
459             + "      <rect x='1' y='11' width='8' height='8'/>\n"
460             + "    </g>\n"
461             + "  </svg>\n"
462             + "</body></html>";
463 
464         loadPageVerifyTitle2(html);
465     }
466 
467     /**
468      * See issue #719.
469      * @throws Exception failure
470      */
471     @Test
472     @Alerts("5")
473     public void svg_selfClosingTags() throws Exception {
474         final String html =
475             "<html>\n"
476             + "<head>\n"
477             + "<script>\n"
478             + LOG_TITLE_FUNCTION
479             + "  function test() {\n"
480             + "    elem = document.getElementById('tester');\n"
481             + "    log(elem.childNodes.length);\n"
482             + "  }\n"
483             + "</script>\n"
484             + "</head>\n"
485             + "<body onload='test()'>\n"
486             + "  <svg id='tester' xmlns=\"http://www.w3.org/2000/svg\">\n"
487             + "    <path fill=\"#4285F4\" />\n"
488             + "    <path fill=\"#34A853\" />\n"
489             + "  </svg>\n"
490             + "</body></html>";
491 
492         loadPageVerifyTitle2(html);
493     }
494 
495     /**
496      * Test for a case where complete HTML page is present inside DIV tag.
497      * Browsers ignore only HTML, HEAD and BODY tags. Contents of HEAD and BODY are added to
498      * the current node (DIV tag in test case).
499      *
500      * @throws Exception failure
501      */
502     @Test
503     @Alerts({"titles", "HEAD", "Outer Html", "DIV", "Inner Html",
504                 "bodyTitles", "DIV", "Inner Html",
505                 "innerDiv", "outerDiv"})
506     public void completeHtmlInsideDiv() throws Exception {
507         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
508             + "<html><head>\n"
509             + "  <title>Outer Html</title>\n"
510             + "  <script>\n"
511             + LOG_TEXTAREA_FUNCTION
512             + "    function test() {\n"
513             + "      log('titles');\n"
514             + "      var titles = document.getElementsByTagName('title');\n"
515             + "      for(var i = 0; i < titles.length; i++) {\n"
516             + "        log(titles[i].parentNode.nodeName);\n"
517             + "        log(titles[i].text);\n"
518             + "      }\n"
519             + "      log('bodyTitles');\n"
520             + "      var bodyTitles = document.body.getElementsByTagName('title');\n"
521             + "      for(var i = 0; i < bodyTitles.length; i++) {\n"
522             + "        log(bodyTitles[i].parentNode.nodeName);\n"
523             + "        log(bodyTitles[i].text);\n"
524             + "      }\n"
525             + "      log('innerDiv');\n"
526             + "      var innerDiv = document.getElementById('innerDiv');\n"
527             + "      log(innerDiv.parentNode.id);\n"
528             + "    }\n"
529             + "  </script>\n"
530             + "</head>\n"
531             + "<body onload='test()'>\n"
532             + "  <DIV id=outerDiv>\n"
533             + "    Outer DIV\n"
534             + "    <html>\n"
535             + "      <head><title>Inner Html</title></head>\n"
536             + "      <body>\n"
537             + "        <DIV id=innerDiv>Inner DIV</DIV>\n"
538             + "      </body>\n"
539             + "    </html>\n"
540             + "  </DIV>\n"
541             + LOG_TEXTAREA
542             + "</body>\n"
543             + "</html>\n";
544 
545         loadPageVerifyTextArea2(html);
546     }
547 
548     /**
549      * Test for a case where complete HTML page is added using document.write() inside DIV tag.
550      * Browsers ignore only HTML, HEAD and BODY tags. Contents of HEAD and BODY are added to
551      * the current node (DIV tag in test case).
552      *
553      * @throws Exception failure
554      */
555     @Test
556     @Alerts({"titles", "HEAD", "Outer Html", "DIV", "Inner Html",
557                 "bodyTitles", "DIV", "Inner Html",
558                 "innerDiv", "outerDiv"})
559     public void writeCompleteHtmlInsideDIV() throws Exception {
560         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
561             + "<html><head>\n"
562             + "  <title>Outer Html</title>\n"
563             + "  <script>\n"
564             + LOG_TEXTAREA_FUNCTION
565             + "    function test() {\n"
566             + "      log('titles');\n"
567             + "      var titles = document.getElementsByTagName('title');\n"
568             + "      for(var i = 0; i < titles.length; i++) {\n"
569             + "        log(titles[i].parentNode.nodeName);\n"
570             + "        log(titles[i].text);\n"
571             + "      }\n"
572             + "      log('bodyTitles');\n"
573             + "      var bodyTitles = document.body.getElementsByTagName('title');\n"
574             + "      for(var i = 0; i < bodyTitles.length; i++) {\n"
575             + "        log(bodyTitles[i].parentNode.nodeName);\n"
576             + "        log(bodyTitles[i].text);\n"
577             + "      }\n"
578             + "      log('innerDiv');\n"
579             + "      var innerDiv = document.getElementById('innerDiv');\n"
580             + "      log(innerDiv.parentNode.id);\n"
581             + "    }\n"
582             + "  </script>\n"
583             + "</head>\n"
584             + "<body onload='test()'>\n"
585             + "  <DIV id=outerDiv>\n"
586             + "     Outer DIV\n"
587             + "     <script>\n"
588             + "       document.write('<html><head><title>Inner Html</title></head>"
589             + "         <body><DIV id=innerDiv>Inner DIV</DIV></body></html>');\n"
590             + "     </script>\n"
591             + "  </DIV>\n"
592             + LOG_TEXTAREA
593             + "</body>\n"
594             + "</html>\n";
595 
596         loadPageVerifyTextArea2(html);
597     }
598 
599     /**
600      * Test for a case where complete HTML page is set in innerHTML of DIV tag.
601      * Behavior is same for any TAG inside body including BODY tag.
602      * Browsers ignore only HTML, HEAD and BODY tags. Contents of BODY are added to
603      * the current node (DIV tag in test case).
604      *
605      * @throws Exception failure
606      */
607     @Test
608     @Alerts({"titles", "HEAD", "Outer Html", "DIV", "Inner Html",
609                 "bodyTitles", "DIV", "Inner Html",
610                 "innerDiv", "outerDiv"})
611     public void setCompleteHtmlToDIV_innerHTML() throws Exception {
612         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
613             + "<html><head>\n"
614             + "  <title>Outer Html</title>\n"
615             + "  <script>\n"
616             + LOG_TEXTAREA_FUNCTION
617             + "    function test() {\n"
618             + "      log('titles');\n"
619             + "      var titles = document.getElementsByTagName('title');\n"
620             + "      for(var i = 0; i < titles.length; i++) {\n"
621             + "        log(titles[i].parentNode.nodeName);\n"
622             + "        log(titles[i].text);\n"
623             + "      }\n"
624             + "      log('bodyTitles');\n"
625             + "      var bodyTitles = document.body.getElementsByTagName('title');\n"
626             + "      for(var i = 0; i < bodyTitles.length; i++) {\n"
627             + "        log(bodyTitles[i].parentNode.nodeName);\n"
628             + "        log(bodyTitles[i].text);\n"
629             + "      }\n"
630             + "      log('innerDiv');\n"
631             + "      var innerDiv = document.getElementById('innerDiv');\n"
632             + "      log(innerDiv.parentNode.id);\n"
633             + "    }\n"
634             + "  </script>\n"
635             + "</head>\n"
636             + "<body onload='test()'>\n"
637             + "  <DIV id=outerDiv>\n"
638             + "     Outer DIV\n"
639             + "  </DIV>\n"
640             + "    <script>\n"
641             + "    document.getElementById('outerDiv').innerHTML ="
642             + "        '<html><head><title>Inner Html</title></head>"
643             + "        <body><DIV id=innerDiv>Inner DIV</DIV></body></html>';\n"
644             + "    </script>\n"
645             + LOG_TEXTAREA
646             + "</body>\n"
647             + "</html>\n";
648 
649         loadPageVerifyTextArea2(html);
650     }
651 
652     /**
653      * Test for a case where complete HTML page is set in innerHTML of DIV tag.
654      * Behavior is same for any TAG inside body including BODY tag.
655      * Browsers ignore only HTML, HEAD and BODY tags. Contents of BODY are added to
656      * the current node (DIV tag in test case).
657      *
658      * @throws Exception failure
659      */
660     @Test
661     @Alerts({"titles", "HEAD", "Outer Html", "DIV", "Inner Html",
662                 "bodyTitles", "DIV", "Inner Html",
663                 "innerDiv", "outerDiv"})
664     public void setCompleteHtmlToDIV2_innerHTML() throws Exception {
665         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
666             + "<html><head>\n"
667             + "  <title>Outer Html</title>\n"
668             + "  <script>\n"
669             + LOG_TEXTAREA_FUNCTION
670             + "    function test() {\n"
671             + "      log('titles');\n"
672             + "      var titles = document.getElementsByTagName('title');\n"
673             + "      for(var i = 0; i < titles.length; i++) {\n"
674             + "        log(titles[i].parentNode.nodeName);\n"
675             + "        log(titles[i].text);\n"
676             + "      }\n"
677             + "      log('bodyTitles');\n"
678             + "      var bodyTitles = document.body.getElementsByTagName('title');\n"
679             + "      for(var i = 0; i < bodyTitles.length; i++) {\n"
680             + "        log(bodyTitles[i].parentNode.nodeName);\n"
681             + "        log(bodyTitles[i].text);\n"
682             + "      }\n"
683             + "      log('innerDiv');\n"
684             + "      var innerDiv = document.getElementById('innerDiv');\n"
685             + "      log(innerDiv.parentNode.id);\n"
686             + "    }\n"
687             + "  </script>\n"
688             + "</head>\n"
689             + "<body onload='test()'>\n"
690             + "  <DIV id=outerDiv>\n"
691             + "     Outer DIV\n"
692             + "  </DIV>\n"
693             + "    <script>\n"
694             + "    document.getElementById('outerDiv').innerHTML ="
695             + "        '<html><head>"
696             + "            <title>Inner Html</title>"
697             + "            <meta name=\"author\" content=\"John Doe\">"
698             + "        </head>"
699             + "        <body><DIV id=innerDiv>Inner DIV</DIV></body></html>';\n"
700             + "    </script>\n"
701             + LOG_TEXTAREA
702             + "</body>\n"
703             + "</html>\n";
704 
705         loadPageVerifyTextArea2(html);
706     }
707 
708     /**
709      * Test for a case where complete HTML page is set in innerHTML of HTML tag.
710      * Others replace the current content of the HTML node by the new one.
711      *
712      * @throws Exception failure
713      */
714     @Test
715     @Alerts({"titles", "HEAD", "Inner Html", "misc", "true", "BODY"})
716     @HtmlUnitNYI(CHROME = {"titles", "HTML", "Inner Html", "misc", "false", "HTML"},
717             EDGE = {"titles", "HTML", "Inner Html", "misc", "false", "HTML"},
718             FF = {"titles", "HTML", "Inner Html", "misc", "false", "HTML"},
719             FF_ESR = {"titles", "HTML", "Inner Html", "misc", "false", "HTML"})
720     // currently the content of HEAD and BODY are added directly to HTML
721     public void setCompleteHtmlToHTML_innerHTML() throws Exception {
722         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
723             + "<html><head>\n"
724             + "  <title>Outer Html</title>\n"
725             + "  <script>\n"
726             + LOG_WINDOW_NAME_FUNCTION
727             + "    function test() {\n"
728             + "      log('titles');\n"
729             + "      var titles = document.getElementsByTagName('title');\n"
730             + "      for(var i = 0; i < titles.length; i++) {\n"
731             + "        log(titles[i].parentNode.nodeName);\n"
732             + "        log(titles[i].text);\n"
733             + "      }\n"
734             + "      log('misc');\n"
735             + "      log(document.body != null);\n"
736             + "      var innerDiv = document.getElementById('innerDiv');\n"
737             + "      if (innerDiv != null) {\n"
738             + "        log(innerDiv.parentNode.nodeName);\n"
739             + "      }\n"
740             + "    }\n"
741             + "  </script>\n"
742             + "</head>\n"
743             + "<body onload='test()'>\n"
744             + "  <DIV id=outerDiv>\n"
745             + "    Outer DIV\n"
746             + "  </DIV>\n"
747             + "  <script>\n"
748             + "    try {\n"
749             + "      document.getElementsByTagName('html')[0].innerHTML ="
750             + "        '<html><head><title>Inner Html</title></head>"
751             + "        <body><DIV id=innerDiv>Inner DIV</DIV></body></html>';\n"
752             + "    } catch(e) { logEx(e) }\n"
753             + "  </script>\n"
754             + "</body>\n"
755             + "</html>\n";
756 
757         loadPage2(html);
758         verifyWindowName2(getWebDriver(), getExpectedAlerts());
759     }
760 
761     /**
762      * @throws Exception failure
763      */
764     @Test
765     @Alerts("before1after1\\nbefore2after2\\nbefore3after3\\nbefore4after4\\nbefore5after5\\nbefore6<>after6")
766     @HtmlUnitNYI(
767             CHROME = "before1after1\\sbefore2after2\\sbefore3after3\\sbefore4after4\\sbefore5after5\\sbefore6<>after6",
768             EDGE = "before1after1\\sbefore2after2\\sbefore3after3\\sbefore4after4\\sbefore5after5\\sbefore6<>after6",
769             FF = "before1after1\\sbefore2after2\\sbefore3after3\\sbefore4after4\\sbefore5after5\\sbefore6<>after6",
770             FF_ESR = "before1after1\\sbefore2after2\\sbefore3after3\\sbefore4after4\\sbefore5after5\\sbefore6<>after6")
771     public void specialComments() throws Exception {
772         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
773                 + "<html><head>\n"
774                 + "  <script>\n"
775                 + LOG_TITLE_FUNCTION_NORMALIZE
776                 + "    function test() {\n"
777                 + "      var body = document.getElementById('tester');\n"
778                 + "      var text = body.innerText;"
779                 + "      log(text);\n"
780                 + "    }\n"
781                 + "  </script>\n"
782                 + "</head>\n"
783                 + "<body id='tester' onload='test()'>\n"
784                 + "  <div>before1<!---->after1</div>\n"
785                 + "  <!--good comment-->\n"
786                 + "  <div>before2<!--->after2</div>\n"
787                 + "  <!--good comment-->\n"
788                 + "  <div>before3<!-->after3</div>\n"
789                 + "  <!--good comment-->\n"
790                 + "  <div>before4<!->after4</div>\n"
791                 + "  <!--good comment-->\n"
792                 + "  <div>before5<!>after5</div>\n"
793                 + "  <!--good comment-->\n"
794                 + "  <div>before6<>after6</div>\n"
795                 + "  <!--good comment-->\n"
796                 + "</body>\n"
797                 + "</html>\n";
798 
799         loadPageVerifyTitle2(html, getExpectedAlerts());
800     }
801 
802     /**
803      * @throws Exception failure
804      */
805     @Test
806     @Alerts("before1after1\\nbefore2\\nbefore3\\nbefore4after4\\nbefore5after5\\nbefore6<\\s>after6")
807     @HtmlUnitNYI(CHROME = "before1after1\\sbefore2\\sbefore3\\sbefore4after4\\sbefore5after5\\sbefore6<\\s>after6",
808             EDGE = "before1after1\\sbefore2\\sbefore3\\sbefore4after4\\sbefore5after5\\sbefore6<\\s>after6",
809             FF = "before1after1\\sbefore2\\sbefore3\\sbefore4after4\\sbefore5after5\\sbefore6<\\s>after6",
810             FF_ESR = "before1after1\\sbefore2\\sbefore3\\sbefore4after4\\sbefore5after5\\sbefore6<\\s>after6")
811     public void specialComments2() throws Exception {
812         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
813                 + "<html><head>\n"
814                 + "  <script>\n"
815                 + LOG_TITLE_FUNCTION_NORMALIZE
816                 + "    function test() {\n"
817                 + "      var body = document.getElementById('tester');\n"
818                 + "      var text = body.innerText;"
819                 + "      log(text);\n"
820                 + "    }\n"
821                 + "  </script>\n"
822                 + "</head>\n"
823                 + "<body id='tester' onload='test()'>\n"
824                 + "  <div>before1<!-- -->after1</div>\n"
825                 + "  <!--good comment-->\n"
826                 + "  <div>before2<!-- ->after2</div>\n"
827                 + "  <!--good comment-->\n"
828                 + "  <div>before3<!-- >after3</div>\n"
829                 + "  <!--good comment-->\n"
830                 + "  <div>before4<!- >after4</div>\n"
831                 + "  <!--good comment-->\n"
832                 + "  <div>before5<! >after5</div>\n"
833                 + "  <!--good comment-->\n"
834                 + "  <div>before6< >after6</div>\n"
835                 + "  <!--good comment-->\n"
836                 + "</body>\n"
837                 + "</html>\n";
838 
839         loadPageVerifyTitle2(html);
840     }
841 
842     /**
843      * @throws Exception failure
844      */
845     @Test
846     @Alerts({"1", "[object HTMLTemplateElement]", "[object DocumentFragment]",
847              "[object HTMLTableElement]", "[object HTMLTableSectionElement]"})
848     public void tableInsideTemplate_addTBody() throws Exception {
849         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
850             + "<html>\n"
851             + "  <head>\n"
852             + "    <script>\n"
853             + LOG_TITLE_FUNCTION
854             + "      function test() {\n"
855             + "        var bodyChildren = document.getElementsByTagName('body')[0].childNodes;\n"
856             + "        log(bodyChildren.length);\n"
857             + "        log(bodyChildren[0]);\n"
858             + "        log(bodyChildren[0].content);\n"
859 
860             + "        if(bodyChildren[0].content === undefined) { return; }"
861             + "        var table = bodyChildren[0].content.firstElementChild;\n"
862             + "        log(table);\n"
863             + "        log(table.firstChild);\n"
864             + "      }\n"
865             + "    </script>\n"
866             + "  </head>\n"
867             + "  <body onload='test()'><template><table><tr><td>42</td></tr></table></template></body>"
868             + "</html>";
869 
870         loadPageVerifyTitle2(html);
871     }
872 
873     /**
874      * @throws Exception failure
875      */
876     @Test
877     @Alerts({"1", "[object HTMLTemplateElement]", "[object DocumentFragment]",
878              "[object HTMLUListElement]"})
879     public void tableInsideTemplate_addTBodyMoveUlOut() throws Exception {
880         final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_
881             + "<html>\n"
882             + "  <head>\n"
883             + "    <script>\n"
884             + LOG_TITLE_FUNCTION
885             + "      function test() {\n"
886             + "        var bodyChildren = document.getElementsByTagName('body')[0].childNodes;\n"
887             + "        log(bodyChildren.length);\n"
888             + "        log(bodyChildren[0]);\n"
889             + "        log(bodyChildren[0].content);\n"
890 
891             + "        if(bodyChildren[0].content === undefined) { return; }"
892             + "        var ul = bodyChildren[0].content.firstElementChild;\n"
893             + "        log(ul);\n"
894             + "      }\n"
895             + "    </script>\n"
896             + "  </head>\n"
897             + "  <body onload='test()'><template><table><ul></ul><tr><td>42</td></tr></table></template></body>"
898             + "</html>";
899 
900         loadPageVerifyTitle2(html);
901     }
902 
903     /**
904      * @throws Exception failure
905      */
906     @Test
907     @Alerts({"1", "[object HTMLElement]", "1", "[object Text]",
908              "<!-- anchor linking to external file -->"
909                      + "<a href='https://www.htmlunit.org/'>External Link</a>"})
910     public void noscript() throws Exception {
911         final String html =
912             "<html>\n"
913             + "  <head>\n"
914             + "    <script>\n"
915             + LOG_TITLE_FUNCTION
916             + "      function test() {\n"
917             + "        var bodyChildren = document.getElementsByTagName('body')[0].childNodes;\n"
918             + "        log(bodyChildren.length);\n"
919             + "        log(bodyChildren[0]);\n"
920 
921             + "        var noscript = bodyChildren[0];\n"
922             + "        log(noscript.childNodes.length);\n"
923             + "        log(noscript.childNodes[0]);\n"
924             + "        log(noscript.childNodes[0].textContent);\n"
925             + "      }\n"
926             + "    </script>\n"
927             + "  </head>\n"
928             + "  <body onload='test()'>"
929                     + "<noscript>"
930                     + "<!-- anchor linking to external file -->"
931                     + "<a href='https://www.htmlunit.org/'>External Link</a>"
932                     + "</noscript>"
933             + "</body>"
934             + "</html>";
935 
936         loadPageVerifyTitle2(html);
937     }
938 
939     /**
940      * @throws Exception failure
941      */
942     @Test
943     @Alerts({"3", "[object HTMLElement]", "[object HTMLDivElement]", "[object Text]",
944              "1", "[object Text]", "<!-- ",
945              "1", "<div>abc</div>",
946              "0", "-->"})
947     public void noscriptBrokenComment() throws Exception {
948         final String html =
949             "<html>\n"
950             + "  <head>\n"
951             + "    <script>\n"
952             + LOG_TITLE_FUNCTION
953             + "      function test() {\n"
954             + "        var bodyChildren = document.getElementsByTagName('body')[0].childNodes;\n"
955             + "        log(bodyChildren.length);\n"
956             + "        log(bodyChildren[0]);\n"
957             + "        log(bodyChildren[1]);\n"
958             + "        log(bodyChildren[2]);\n"
959 
960             + "        var noscript = bodyChildren[0];\n"
961             + "        log(noscript.childNodes.length);\n"
962             + "        log(noscript.childNodes[0]);\n"
963             + "        log(noscript.childNodes[0].textContent);\n"
964 
965             + "        var div = bodyChildren[1];\n"
966             + "        log(div.childNodes.length);\n"
967             + "        log(div.outerHTML);\n"
968 
969             + "        var txt = bodyChildren[2];\n"
970             + "        log(txt.childNodes.length);\n"
971             + "        log(txt.textContent);\n"
972             + "      }\n"
973             + "    </script>\n"
974             + "  </head>\n"
975             + "  <body onload='test()'>"
976                     + "<noscript>"
977                     + "<!-- </noscript><div>abc</div>-->"
978                     + "</noscript>"
979             + "</body>"
980             + "</html>";
981 
982         loadPageVerifyTitle2(html);
983     }
984 
985     /**
986      * @exception Exception If the test fails
987      */
988     @Test
989     @Alerts("ti </head> <body> 1234 </body> </html>")
990     public void badTagInHead() throws Exception {
991         final String html =
992                 "<html>\n"
993                 + "<head><foo/>\n"
994                 + "<title>ti\n"
995                 + "</head>\n"
996                 + "<body>\n"
997                 + "1234\n"
998                 + "</body>\n"
999                 + "</html>";
1000 
1001         final WebDriver driver = loadPage2(html);
1002         assertEquals(getExpectedAlerts()[0], driver.getTitle());
1003     }
1004 }