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;
16  
17  import static java.nio.charset.StandardCharsets.UTF_8;
18  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
19  
20  import java.net.URL;
21  import java.nio.charset.Charset;
22  import java.nio.charset.StandardCharsets;
23  import java.security.SecureRandom;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.zip.Deflater;
27  
28  import org.apache.commons.io.IOUtils;
29  import org.htmlunit.html.HtmlInlineFrame;
30  import org.htmlunit.junit.annotation.Alerts;
31  import org.htmlunit.junit.annotation.HtmlUnitNYI;
32  import org.htmlunit.util.MimeType;
33  import org.htmlunit.util.NameValuePair;
34  import org.junit.jupiter.api.Test;
35  import org.openqa.selenium.By;
36  import org.openqa.selenium.WebDriver;
37  import org.openqa.selenium.WebElement;
38  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
39  
40  /**
41   * Tests for {@link WebClient} using WebDriverTestCase.
42   *
43   * @author Marc Guillemot
44   * @author Frank Danek
45   * @author Ronald Brill
46   */
47  public class WebClient3Test extends WebDriverTestCase {
48  
49      static final SecureRandom RANDOM = new SecureRandom();
50  
51      /**
52       * Regression test for bug 3012067: a null pointer exception was occurring.
53       * @throws Exception if an error occurs
54       */
55      @Test
56      public void bug3012067_npe() throws Exception {
57          final String html = DOCTYPE_HTML
58              + "<html><body>\n"
59              + "<form action='" + URL_FIRST + "#foo' method='post'></form>\n"
60              + "<script>\n"
61              + "function doWork() {\n"
62              + "  var f = document.forms[0];\n"
63              + "  f.submit();\n"
64              + "  f.submit();\n"
65              + "}\n"
66              + "</script>\n"
67              + "<span id='clickMe' onclick='doWork()'>click</span>\n"
68              + "</body></html>";
69  
70          final WebDriver driver = loadPage2(html);
71          driver.findElement(By.id("clickMe")).click();
72      }
73  
74      /**
75       * Ensure that response stream can be read more than one time.
76       * @throws Exception if an error occurs
77       */
78      @Test
79      public void readStreamTwice() throws Exception {
80          final String html = DOCTYPE_HTML
81              + "<html>\n"
82              + "<body>\n"
83              + "<iframe src='binaryFile.bin'></iframe>\n"
84              + "<iframe src='foo.html'></iframe>\n"
85              + "</body></html>";
86  
87          final MockWebConnection mockConnection = getMockWebConnection();
88          final byte[] binaryContent = new byte[4818];
89          for (int i = 0; i < binaryContent.length; i++) {
90              binaryContent[i] = (byte) (RANDOM.nextInt(Byte.MAX_VALUE));
91          }
92          mockConnection.setDefaultResponse(binaryContent, 200, "OK", MimeType.APPLICATION_OCTET_STREAM);
93          final URL urlFoo = new URL(URL_FIRST, "foo.html");
94          mockConnection.setResponse(urlFoo, DOCTYPE_HTML + "<html></html>");
95  
96          final WebDriver driver = loadPage2(html);
97          final WebElement iframe1 = driver.findElement(By.tagName("iframe"));
98          if (driver instanceof HtmlUnitDriver) {
99              final HtmlInlineFrame htmlUnitIFrame1 = (HtmlInlineFrame) toHtmlElement(iframe1);
100             final WebResponse iframeWebResponse = htmlUnitIFrame1.getEnclosedPage().getWebResponse();
101             byte[] receivedBytes = IOUtils.toByteArray(iframeWebResponse.getContentAsStream());
102             receivedBytes = IOUtils.toByteArray(iframeWebResponse.getContentAsStream());
103             assertArrayEquals(binaryContent, receivedBytes);
104         }
105     }
106 
107     /**
108      * Was causing an Exception in IE simulation
109      * as of HtmlUnit-2.8-SNAPSHOT on Aug. 04, 2010.
110      * @throws Exception if the test fails
111      */
112     @Test
113     public void escapeRequestQuery() throws Exception {
114         getMockWebConnection().setDefaultResponse("");
115 
116         loadPage2("", new URL(URL_FIRST, "foo?a=<b>i</b>"));
117     }
118 
119     /**
120      * Was causing a "java.net.URISyntaxException: Malformed escape pair".
121      * HtmlUnit now escapes the "%%" to "%25%25" to build a valid URL but FF doesn't care
122      * and sends the invalid "%%" sequence as it.
123      * This will be quite difficult to simulate FF here as HttpClient's HttpRequestBase
124      * uses URI and "%%" can't be part of the query string for a URI.
125      * @throws Exception if the test fails
126      */
127     @Test
128     @Alerts("0")
129     @HtmlUnitNYI(CHROME = "1",
130             EDGE = "1",
131             FF = "1",
132             FF_ESR = "1")
133     public void escapeRequestQuery2a() throws Exception {
134         getMockWebConnection().setDefaultResponse("");
135 
136         final URL url = new URL(URL_FIRST, "foo.png?cb=%%RANDOM_NUMBER%%");
137         loadPage2("", url);
138 
139         // real browsers do not send this request
140         // 'Unable to parse URI query'
141         assertEquals(Integer.parseInt(getExpectedAlerts()[0]), getMockWebConnection().getRequestCount());
142     }
143 
144     /**
145      * Was causing a "java.net.URISyntaxException: Malformed escape pair".
146      * This is a simplified version of {@link #escapeRequestQuery2a()} only testing
147      * that no exception is thrown. The request performed is not fully correct.
148      * This test can be removed once {@link #escapeRequestQuery2a()} runs correctly.
149      * @throws Exception if the test fails
150      */
151     @Test
152     public void escapeRequestQuery2b() throws Exception {
153         getMockWebConnection().setDefaultResponse("");
154 
155         final URL url = new URL(URL_FIRST, "foo.png?cb=%%RANDOM_NUMBER%%");
156         loadPage2("", url);
157     }
158 
159     /**
160      * Regression test for issue 3193004.
161      * Ensure that the click returns once the target page has been loaded into the target window.
162      * @throws Exception if an error occurs
163      */
164     @Test
165     public void clickReturnsWhenThePageHasBeenCompleteLoaded() throws Exception {
166         final String firstContent = DOCTYPE_HTML
167             + "<html><head>\n"
168             + "<script>window.setInterval(\'',1);</script></head>\n"
169             + "<body><a href='" + URL_SECOND + "'>to second</a></body></html>";
170         final String secondContent = DOCTYPE_HTML + "<html><body></body></html>";
171 
172         final MockWebConnection webConnection = getMockWebConnection();
173         webConnection.setResponse(URL_SECOND, secondContent);
174 
175         for (int i = 1; i < 100; i++) {
176             final WebDriver webDriver = loadPage2(firstContent);
177             webDriver.findElement(By.tagName("a")).click();
178             assertEquals("Run " + i, URL_SECOND.toExternalForm(), webDriver.getCurrentUrl());
179         }
180     }
181 
182     /**
183      * Ensures, that a window opened by an anchor with target attribute is attached
184      * to the javascript event loop.
185      *
186      * @throws Exception if an error occurs
187      */
188     @Test
189     @Alerts({"open", "first", "second"})
190     public void windowOpenedByAnchorTargetIsAttachedToJavascriptEventLoop() throws Exception {
191         final String firstContent = DOCTYPE_HTML
192             + "<html>\n"
193             + "<head>\n"
194             + "<script type='text/javascript'>\n"
195             + "  function info(msg) {\n"
196             + "    alert(msg);\n"
197             + "  }\n"
198             + "</script>\n"
199             + "</head>\n"
200             + "<body>\n"
201             + "  <a id='testAnchor' href='" + URL_SECOND + "' target='_blank' onclick='info(\"open\")'>to second</a>\n"
202             + "</body></html>";
203         final String secondContent = DOCTYPE_HTML
204             + "<html><head>\n"
205             + "<script type='text/javascript'>\n"
206             + "  function first() {\n"
207             + "    window.opener.info('first');\n"
208             + "    window.setTimeout(second, 10);\n"
209             + "  }\n"
210             + "  function second() {\n"
211             + "    window.opener.info('second');\n"
212             + "    window.close();\n"
213             + "  }\n"
214             + "</script>\n"
215             + "</head>\n"
216 
217             + "<body onLoad='window.setTimeout(first, 5);'></body></html>";
218 
219         getMockWebConnection().setResponse(URL_SECOND, secondContent);
220 
221         final WebDriver driver = loadPage2(firstContent);
222         driver.findElement(By.id("testAnchor")).click();
223         if (useRealBrowser()) {
224             Thread.sleep(400);
225         }
226 
227         verifyAlerts(driver, getExpectedAlerts());
228     }
229 
230     /**
231      * Ensures, that a window opened by a form with target attribute is attached
232      * to the javascript event loop.
233      *
234      * @throws Exception if an error occurs
235      */
236     @Test
237     @Alerts({"open", "first", "second"})
238     public void windowOpenedByFormTargetIsAttachedToJavascriptEventLoop() throws Exception {
239         final String firstContent = DOCTYPE_HTML
240             + "<html>\n"
241             + "<head>\n"
242             + "<script type='text/javascript'>\n"
243             + "  function info(msg) {\n"
244             + "    alert(msg);\n"
245             + "  }\n"
246             + "</script>\n"
247             + "</head>\n"
248             + "<body>\n"
249             + "<form action='" + URL_SECOND + "' target='_blank'>\n"
250             + "  <input id='testSubmit' type='submit' value='Submit' onclick='info(\"open\")'>\n"
251             + "</form>\n"
252             + "</body></html>";
253         final String secondContent = DOCTYPE_HTML
254             + "<html><head>\n"
255             + "<script type='text/javascript'>\n"
256             + "  function first() {\n"
257             + "    window.opener.info('first');\n"
258             + "    window.setTimeout(second, 10);\n"
259             + "  }\n"
260             + "  function second() {\n"
261             + "    window.opener.info('second');\n"
262             + "    window.close();\n"
263             + "  }\n"
264             + "</script>\n"
265             + "</head>\n"
266 
267             + "<body onLoad='window.setTimeout(first, 5);'></body></html>";
268 
269         getMockWebConnection().setResponse(URL_SECOND, secondContent);
270 
271         final WebDriver driver = loadPage2(firstContent);
272         driver.findElement(By.id("testSubmit")).click();
273         if (useRealBrowser()) {
274             Thread.sleep(400);
275         }
276 
277         verifyAlerts(driver, getExpectedAlerts());
278     }
279 
280     /**
281      * Ensures, that a window opened by javascript window.open is attached
282      * to the javascript event loop.
283      *
284      * @throws Exception if an error occurs
285      */
286     @Test
287     @Alerts({"open", "first", "second"})
288     public void windowOpenedByJavascriptIsAttachedToJavascriptEventLoop() throws Exception {
289         final String firstContent = DOCTYPE_HTML
290             + "<html>\n"
291             + "<head>\n"
292             + "<script type='text/javascript'>\n"
293             + "  function info(msg) {\n"
294             + "    alert(msg);\n"
295             + "  }\n"
296             + "</script>\n"
297             + "</head>\n"
298             + "<body>\n"
299             + "  <a id='testAnchor' href='#'"
300             + "    onclick='info(\"open\");window.open(\"" + URL_SECOND + "\", \"Popup\", \"\");'>open window</a>\n"
301             + "</body></html>";
302         final String secondContent = DOCTYPE_HTML
303             + "<html><head>\n"
304             + "<script type='text/javascript'>\n"
305             + "  function first() {\n"
306             + "    window.opener.info('first');\n"
307             + "    window.setTimeout(second, 10);\n"
308             + "  }\n"
309             + "  function second() {\n"
310             + "    window.opener.info('second');\n"
311             + "    window.close();\n"
312             + "  }\n"
313             + "</script>\n"
314             + "</head>\n"
315 
316             + "<body onLoad='window.setTimeout(first, 5);'></body></html>";
317 
318         getMockWebConnection().setResponse(URL_SECOND, secondContent);
319 
320         final WebDriver driver = loadPage2(firstContent);
321         driver.findElement(By.id("testAnchor")).click();
322 
323         verifyAlerts(driver, getExpectedAlerts());
324     }
325 
326     /**
327      * Ensures, that a window opened by javascript and than filled by a form with target attribute
328      * is attached to the javascript event loop.
329      *
330      * @throws Exception if an error occurs
331      */
332     @Test
333     @Alerts({"open", "first", "second"})
334     public void windowOpenedByJavascriptFilledByFormTargetIsAttachedToJavascriptEventLoop() throws Exception {
335         final String firstContent = DOCTYPE_HTML
336             + "<html>\n"
337             + "<head>\n"
338             + "<script type='text/javascript'>\n"
339             + "  function info(msg) {\n"
340             + "    alert(msg);\n"
341             + "  }\n"
342             + "</script>\n"
343             + "</head>\n"
344             + "<body>\n"
345             + "<form action='" + URL_SECOND + "' name='myForm'>\n"
346             + "  <input id='testSubmit' type='button' value='Submit' "
347             + "    onclick='info(\"open\");"
348             + "    window.open(\"" + URL_SECOND + "\", \"Popup\");"
349             + "    document.myForm.target = \"Popup\";'"
350             + "  >\n"
351             + "</form>\n"
352             + "</body></html>";
353         final String secondContent = DOCTYPE_HTML
354             + "<html><head>\n"
355             + "<script type='text/javascript'>\n"
356             + "  function first() {\n"
357             + "    window.opener.info('first');\n"
358             + "    window.setTimeout(second, 10);\n"
359             + "  }\n"
360             + "  function second() {\n"
361             + "    window.opener.info('second');\n"
362             + "    window.close();\n"
363             + "  }\n"
364             + "</script>\n"
365             + "</head>\n"
366 
367             + "<body onLoad='window.setTimeout(first, 5);'></body></html>";
368 
369         getMockWebConnection().setResponse(URL_SECOND, secondContent);
370 
371         final WebDriver driver = loadPage2(firstContent);
372         driver.findElement(By.id("testSubmit")).click();
373 
374         verifyAlerts(driver, getExpectedAlerts());
375     }
376 
377     /**
378      * @throws Exception if an error occurs
379      */
380     @Test
381     @Alerts({"Executed", "later"})
382     public void execJavascriptOnErrorPages() throws Exception {
383         final String errorHtml = DOCTYPE_HTML
384                 + "<html>\n"
385                 + "<head>\n"
386                 + "</head>\n"
387                 + "<body>\n"
388                 + "<script type='text/javascript'>\n"
389                 + LOG_TITLE_FUNCTION
390                 + "  log('Executed');\n"
391                 + "  setTimeout(\"log('later')\", 10);\n"
392                 + "</script>\n"
393                 + "</body></html>\n";
394 
395         final MockWebConnection conn = getMockWebConnection();
396         conn.setResponse(URL_FIRST, errorHtml, 404, "Not Found", MimeType.TEXT_HTML, new ArrayList<>());
397 
398         loadPage2(URL_FIRST, StandardCharsets.UTF_8);
399         verifyTitle2(DEFAULT_WAIT_TIME, getWebDriver(), getExpectedAlerts());
400     }
401 
402     /**
403      * This test was failing due to a change made in revision 7104 (not in any release)
404      * that was transforming %20 into %2520.
405      * @throws Exception if an error occurs
406      */
407     @Test
408     @Alerts("hello")
409     public void urlEncodingPercent20() throws Exception {
410         final String html = DOCTYPE_HTML
411                 + "<html><body>\n"
412                 + "<script src='a%20b.js'></script>\n"
413                 + "</body></html>";
414 
415         final MockWebConnection conn = getMockWebConnection();
416         conn.setResponse(new URL(URL_FIRST, "a%20b.js"), "alert('hello');", "text/javascript");
417 
418         loadPageWithAlerts2(html);
419     }
420 
421     /**
422      * Test "deflate" encoding without ZLIB header and checksum fields.
423      * @throws Exception if the test fails
424      */
425     @Test
426     @Alerts("modified")
427     public void deflateCompressionGZipCompatible() throws Exception {
428         doTestDeflateCompression(true);
429     }
430 
431     /**
432      * Test "deflate" encoding with ZLIB header and checksum fields.
433      * @throws Exception if the test fails
434      */
435     @Test
436     @Alerts("modified")
437     // IE does not support deflate compression anymore but I couldn't find a way to disable it in HttpClient
438     public void deflateCompressionNonGZipCompatible() throws Exception {
439         doTestDeflateCompression(false);
440     }
441 
442     private void doTestDeflateCompression(final boolean gzipCompatibleCompression) throws Exception {
443         final byte[] input = "document.title = 'modified';".getBytes(UTF_8);
444 
445         final byte[] buffer = new byte[100];
446         final Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, gzipCompatibleCompression);
447         deflater.setInput(input);
448         deflater.finish();
449 
450         final int compressedDataLength = deflater.deflate(buffer);
451         final byte[] content = new byte[compressedDataLength];
452         System.arraycopy(buffer, 0, content, 0, compressedDataLength);
453 
454         final List<NameValuePair> headers = new ArrayList<>();
455         headers.add(new NameValuePair("Content-Encoding", "deflate"));
456         headers.add(new NameValuePair(HttpHeader.CONTENT_LENGTH, String.valueOf(compressedDataLength)));
457 
458         final MockWebConnection conn = getMockWebConnection();
459         conn.setResponse(URL_SECOND, content, 200, "OK", "text/javascript", headers);
460 
461         final String html = DOCTYPE_HTML
462             + "<html><head>\n"
463             + "<title>Hello world</title>\n"
464             + "<script src='" + URL_SECOND + "'></script>\n"
465             + "</head><body><script>alert(document.title)</script></body></html>";
466         loadPageWithAlerts2(html);
467     }
468 
469     /**
470      * @throws Exception if something goes wrong
471      */
472     @Test
473     @Alerts("executed")
474     public void javascriptContentDetectorWithoutContentType() throws Exception {
475         final MockWebConnection conn = getMockWebConnection();
476         conn.setDefaultResponse("<script>alert('executed')</script>", 200, "OK", null);
477         loadPageWithAlerts2(URL_FIRST);
478     }
479 
480     /**
481      * @throws Exception if something goes wrong
482      */
483     @Test
484     @Alerts("executed")
485     public void javascriptContentDetectorWithoutContentType500() throws Exception {
486         final MockWebConnection conn = getMockWebConnection();
487         conn.setDefaultResponse("<script>alert('executed')</script>", 500, "OK", null);
488         loadPageWithAlerts2(URL_FIRST);
489     }
490 
491     /**
492      * @throws Exception if something goes wrong
493      */
494     @Test
495     @Alerts("executed")
496     public void javascriptContentDetectorWithoutContentTypeWhitespace() throws Exception {
497         final MockWebConnection conn = getMockWebConnection();
498         conn.setDefaultResponse(" \t \r\n \n   <script>alert('executed')</script>", 200, "OK", null);
499         loadPageWithAlerts2(URL_FIRST);
500     }
501 
502     /**
503      * @throws Exception if something goes wrong
504      */
505     @Test
506     public void javascriptContentDetectorWithoutContentTypeTextBefore() throws Exception {
507         final MockWebConnection conn = getMockWebConnection();
508         conn.setDefaultResponse("Attention<script>alert('executed')</script>", 200, "OK", null);
509         loadPageWithAlerts2(URL_FIRST);
510     }
511 
512     /**
513      * @throws Exception if something goes wrong
514      */
515     @Test
516     @Alerts("executed")
517     public void javascriptContentDetectorWithoutContentUppercase() throws Exception {
518         final MockWebConnection conn = getMockWebConnection();
519         conn.setDefaultResponse("<SCRIPT>alert('executed')</SCRIPT>", 200, "OK", null);
520         loadPageWithAlerts2(URL_FIRST);
521     }
522 
523     /**
524      * @throws Exception if something goes wrong
525      */
526     @Test
527     @Alerts("executed")
528     public void javascriptContentDetectorWithoutContentMixedCase() throws Exception {
529         final MockWebConnection conn = getMockWebConnection();
530         conn.setDefaultResponse("<scRIPt>alert('executed')</scRIPt>", 200, "OK", null);
531         loadPageWithAlerts2(URL_FIRST);
532     }
533 
534     /**
535      * @throws Exception if something goes wrong
536      */
537     @Test
538     public void javascriptContentDetectorContentTypeTextPlain() throws Exception {
539         final MockWebConnection conn = getMockWebConnection();
540         conn.setDefaultResponse("<script>alert('executed')</script>", 200, "OK", MimeType.TEXT_PLAIN);
541         loadPageWithAlerts2(URL_FIRST);
542     }
543 
544     /**
545      * @throws Exception if something goes wrong
546      */
547     @Test
548     @Alerts({})
549     @HtmlUnitNYI(CHROME = "executed",
550             EDGE = "executed",
551             FF = "executed",
552             FF_ESR = "executed")
553     public void javascriptContentDetectorContentTypeApplicationJavascript() throws Exception {
554         final MockWebConnection conn = getMockWebConnection();
555         conn.setDefaultResponse("<script>alert('executed')</script>", 200, "OK", MimeType.TEXT_JAVASCRIPT);
556         loadPageWithAlerts2(URL_FIRST);
557     }
558 
559     /**
560      * @throws Exception if the test fails
561      */
562     @Test
563     public void encodingCharsetGB2312() throws Exception {
564         encodingCharset("\u6211\u662F\u6211\u7684 Abc", "GB2312", "GB2312");
565     }
566 
567     /**
568      * @throws Exception if the test fails
569      */
570     @Test
571     public void encodingCharsetGB2312GBKChar() throws Exception {
572         encodingCharset("\u4eb8 Abc", "GB2312", "GBK");
573     }
574 
575     /**
576      * @throws Exception if the test fails
577      */
578     @Test
579     public void encodingCharsetGBK() throws Exception {
580         encodingCharset("\u6211\u662F\u6211\u7684 \u4eb8 Abc", "GBK", "GBK");
581     }
582 
583     private void encodingCharset(final String title,
584             final String metaCharset, final String responseCharset) throws Exception {
585         final String html = DOCTYPE_HTML
586             + "<html><head>\n"
587             + "<meta http-equiv='Content-Type' content='text/html; charset=" + metaCharset + "'>\n"
588             + "<title>" + title + "</title>\n"
589             + "</head>\n"
590             + "<body>\n"
591             + "</body></html>";
592 
593         final String firstResponse = "HTTP/1.1 200 OK\r\n"
594                 + "Content-Length: " + html.length() + "\r\n"
595                 + "Content-Type: text/html\r\n"
596                 + "Connection: close\r\n"
597                 + "\r\n" + html;
598 
599         shutDownAll();
600         try (PrimitiveWebServer primitiveWebServer =
601                 new PrimitiveWebServer(Charset.forName(responseCharset), firstResponse, firstResponse)) {
602             final String url = "http://localhost:" + primitiveWebServer.getPort() + "/";
603             final WebDriver driver = getWebDriver();
604 
605             driver.get(url);
606             assertEquals(title, driver.getTitle());
607         }
608     }
609 
610     /**
611      * @throws Exception if the test fails
612      */
613     @Test
614     @Alerts(DEFAULT = {"en-US", "en-US"},
615             FF = {"en-US", "en-US,en"},
616             FF_ESR = {"en-US", "en-US,en"})
617     @HtmlUnitNYI(CHROME = {"en-US", "en-US,en"},
618             EDGE = {"en-US", "en-US,en"})
619     public void language() throws Exception {
620         final String html = DOCTYPE_HTML
621             + "<html><head><script>\n"
622             + LOG_TITLE_FUNCTION
623             + "function test() {\n"
624             + "  log(navigator.language);\n"
625             + "  log(navigator.languages);\n"
626             + "}\n"
627             + "</script></head><body onload='test()'>\n"
628             + "</body></html>";
629 
630         shutDownAll();
631         try {
632             loadPageVerifyTitle2(html);
633         }
634         finally {
635             shutDownAll();
636         }
637     }
638 
639     /**
640      * @throws Exception if the test fails
641      */
642     @Test
643     @Alerts(DEFAULT = {"de-DE", "de-DE"},
644             FF = {"de-DE", "de-DE,de"},
645             FF_ESR = {"de-DE", "de-DE,de"})
646     @HtmlUnitNYI(CHROME = {"de-DE", "de-DE,de"},
647             EDGE = {"de-DE", "de-DE,de"})
648     public void languageDE() throws Exception {
649         final String html = DOCTYPE_HTML
650             + "<html><head><script>\n"
651             + LOG_TITLE_FUNCTION
652             + "function test() {\n"
653             + "  log(navigator.language);\n"
654             + "  log(navigator.languages);\n"
655             + "}\n"
656             + "</script></head><body onload='test()'>\n"
657             + "</body></html>";
658 
659         shutDownAll();
660         try {
661             final BrowserVersion.BrowserVersionBuilder builder
662                 = new BrowserVersion.BrowserVersionBuilder(getBrowserVersion());
663             builder.setBrowserLanguage("de-DE");
664             builder.setAcceptLanguageHeader("de-DE,de");
665             setBrowserVersion(builder.build());
666 
667             loadPageVerifyTitle2(html);
668         }
669         finally {
670             shutDownAll();
671         }
672     }
673 }