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 java.util.Locale;
18  import java.util.Map;
19  import java.util.Map.Entry;
20  
21  import org.htmlunit.junit.annotation.Alerts;
22  import org.htmlunit.util.MimeType;
23  import org.junit.jupiter.api.Test;
24  import org.openqa.selenium.By;
25  import org.openqa.selenium.WebDriver;
26  
27  /**
28   * Unit tests for {@link BrowserVersion}.
29   * For some details on IE view you can have a look at
30   * http://blogs.msdn.com/b/ieinternals/archive/2009/07/01/ie-and-the-accept-header.aspx
31   *
32   * @author Ronald Brill
33   * @author Frank Danek
34   */
35  public class BrowserVersion2Test extends WebDriverTestCase {
36  
37      /**
38       * @throws Exception if an error occurs
39       */
40      @Test
41      @Alerts(DEFAULT = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
42                      + "image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
43              FF = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
44              FF_ESR = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
45      public void acceptHeaderGetUrl() throws Exception {
46          final String html = DOCTYPE_HTML
47                  + "<html><body>Response</body></html>";
48          loadPage2(html);
49  
50          assertEquals(getExpectedAlerts()[0], acceptHeaderString());
51      }
52  
53      /**
54       * @throws Exception if an error occurs
55       */
56      @Test
57      @Alerts(DEFAULT = {"2", "Accept: text/html,application/xhtml+xml,"
58                      + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
59                      + "q=0.8,application/signed-exchange;v=b3;q=0.7"},
60              FF = {"2", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
61              FF_ESR = {"2", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"})
62      public void acceptHeaderWindowOpen() throws Exception {
63          String html = DOCTYPE_HTML
64                  + "<html><body>Response</body></html>";
65          getMockWebConnection().setDefaultResponse(html);
66  
67          html = DOCTYPE_HTML
68                  + "<html><head><title>First</title></head>\n"
69                  + "<body>\n"
70                  + "  <a id='clickme' href='javascript: window.open(\"" + URL_SECOND + "\")'>Click me</a>\n"
71                  + "</body></html>";
72          final WebDriver driver = loadPage2(html);
73          driver.findElement(By.id("clickme")).click();
74          // because real browsers are doing the open async, we have to be a bit patient
75          Thread.sleep(DEFAULT_WAIT_TIME.toMillis());
76  
77          assertEquals(getExpectedAlerts()[0], Integer.toString(getMockWebConnection().getRequestCount()));
78          assertEquals(getExpectedAlerts()[1], acceptHeaderString());
79      }
80  
81      /**
82       * @throws Exception if an error occurs
83       */
84      @Test
85      @Alerts(DEFAULT = {"2", "Accept: text/html,application/xhtml+xml,"
86                      + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
87                      + "q=0.8,application/signed-exchange;v=b3;q=0.7"},
88              FF = {"2", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
89              FF_ESR = {"2", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"})
90      public void acceptHeaderAnchorClick() throws Exception {
91          String html = DOCTYPE_HTML
92                  + "<html><body>Response</body></html>";
93          getMockWebConnection().setDefaultResponse(html);
94  
95          html = DOCTYPE_HTML
96                  + "<html><head><title>First</title></head>\n"
97                  + "<body>\n"
98                  + "  <a id='clickme' href='test.html'>Click me</a>\n"
99                  + "</body></html>";
100         final WebDriver driver = loadPage2(html);
101         driver.findElement(By.id("clickme")).click();
102 
103         assertEquals(getExpectedAlerts()[0], Integer.toString(getMockWebConnection().getRequestCount()));
104         assertEquals(getExpectedAlerts()[1], acceptHeaderString());
105     }
106 
107     /**
108      * @throws Exception if an error occurs
109      */
110     @Test
111     @Alerts(DEFAULT = "Accept: text/html,application/xhtml+xml,application/xml;"
112                     + "q=0.9,image/avif,image/webp,image/apng,*/*;"
113                     + "q=0.8,application/signed-exchange;v=b3;q=0.7",
114             FF = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
115             FF_ESR = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
116     public void acceptHeaderAnchorClickWithType() throws Exception {
117         String html = DOCTYPE_HTML
118                 + "<html><body>Response</body></html>";
119         getMockWebConnection().setDefaultResponse(html);
120 
121         html = DOCTYPE_HTML
122                 + "<html><head><title>First</title></head>\n"
123                 + "<body>\n"
124                 + "  <a id='clickme' href='test.html' type='text/plain'>Click me</a>\n"
125                 + "</body></html>";
126         final WebDriver driver = loadPage2(html);
127         driver.findElement(By.id("clickme")).click();
128 
129         // we have to be a bit patient
130         Thread.sleep(100);
131 
132         assertEquals(2, getMockWebConnection().getRequestCount());
133         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
134     }
135 
136     /**
137      * @throws Exception if an error occurs
138      */
139     @Test
140     @Alerts(DEFAULT = "Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
141             FF = "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5",
142             FF_ESR = "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
143     public void acceptHeaderImage() throws Exception {
144         final String html = DOCTYPE_HTML
145             + "<html><head>\n"
146             + "<script>\n"
147             + "  function doTest() {\n"
148             // force access
149             + "    document.getElementById('anImage').height;\n"
150             + "}\n"
151             + "</script></head>\n"
152             + "<body onload='doTest()'>\n"
153             + "  <img src='foo.gif' id='anImage'/>\n"
154             + "</body></html>";
155         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
156         loadPage2(html);
157 
158         assertEquals(2, getMockWebConnection().getRequestCount());
159         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
160     }
161 
162     /**
163      * @throws Exception if an error occurs
164      */
165     @Test
166     @Alerts("Accept: text/css,*/*;q=0.1")
167     public void acceptHeaderCss() throws Exception {
168         final String html = DOCTYPE_HTML
169             + "<html><head>\n"
170             + "  <link href='test.css' rel='stylesheet' type='text/css'>\n"
171             + "<script>\n"
172             + "  function doTest() {\n"
173             // force access
174             + "    var b = document.body;\n"
175             + "    window.getComputedStyle(b, null);\n"
176             + "}\n"
177             + "</script></head>\n"
178             + "<body onload='doTest()'>\n"
179             + "</body></html>";
180         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
181         loadPage2(html);
182 
183         assertEquals(2, getMockWebConnection().getRequestCount());
184         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
185     }
186 
187     /**
188      * @throws Exception if an error occurs
189      */
190     @Test
191     @Alerts("Accept: */*")
192     public void acceptHeaderJavascript() throws Exception {
193         final String html = DOCTYPE_HTML
194             + "<html><head>\n"
195             + "  <script src='test.js' type='text/javascript'></script>\n"
196             + "</head>\n"
197             + "<body>\n"
198             + "</body></html>";
199         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
200         loadPage2(html);
201 
202         assertEquals(2, getMockWebConnection().getRequestCount());
203         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
204     }
205 
206     /**
207      * @throws Exception if an error occurs
208      */
209     @Test
210     @Alerts("Accept: */*")
211     public void acceptHeaderJavascriptWithoutType() throws Exception {
212         final String html = DOCTYPE_HTML
213             + "<html><head>\n"
214             + "  <script src='test.js'></script>\n"
215             + "</head>\n"
216             + "<body>\n"
217             + "</body></html>";
218         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
219         loadPage2(html);
220 
221         assertEquals(2, getMockWebConnection().getRequestCount());
222         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
223     }
224 
225     /**
226      * @throws Exception if an error occurs
227      */
228     @Test
229     @Alerts("Accept: text/css,*/*;q=0.1")
230     public void acceptHeaderCssWithoutType() throws Exception {
231         final String html = DOCTYPE_HTML
232             + "<html><head>\n"
233             + "  <link href='test.css' rel='stylesheet'>\n"
234             + "<script>\n"
235             + "  function doTest() {\n"
236             // force access
237             + "    var b = document.body;\n"
238             + "    window.getComputedStyle(b, null);\n"
239             + "}\n"
240             + "</script></head>\n"
241             + "<body onload='doTest()'>\n"
242             + "</body></html>";
243         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
244         loadPage2(html);
245 
246         assertEquals(2, getMockWebConnection().getRequestCount());
247         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
248     }
249 
250     /**
251      * @throws Exception if an error occurs
252      */
253     @Test
254     @Alerts({"2", "Accept: text/css,*/*;q=0.1"})
255     public void acceptHeaderCssEmptyType() throws Exception {
256         final String html = DOCTYPE_HTML
257             + "<html><head>\n"
258             + "  <link href='test.css' rel='stylesheet' type=''>\n"
259             + "<script>\n"
260             + "  function doTest() {\n"
261             // force access
262             + "    var b = document.body;\n"
263             + "    window.getComputedStyle(b, null);\n"
264             + "}\n"
265             + "</script></head>\n"
266             + "<body onload='doTest()'>\n"
267             + "</body></html>";
268         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
269 
270         final int requests = getMockWebConnection().getRequestCount();
271         loadPage2(html);
272 
273         final int count = Integer.parseInt(getExpectedAlerts()[0]);
274         assertEquals(count, getMockWebConnection().getRequestCount() - requests);
275         assertEquals(getExpectedAlerts()[1], acceptHeaderString());
276     }
277 
278     /**
279      * @throws Exception if an error occurs
280      */
281     @Test
282     @Alerts({"2", "Accept: text/css,*/*;q=0.1"})
283     public void acceptHeaderCssBlankType() throws Exception {
284         final String html = DOCTYPE_HTML
285             + "<html><head>\n"
286             + "  <link href='test.css' rel='stylesheet' type=' '>\n"
287             + "<script>\n"
288             + "  function doTest() {\n"
289             // force access
290             + "    var b = document.body;\n"
291             + "    window.getComputedStyle(b, null);\n"
292             + "}\n"
293             + "</script></head>\n"
294             + "<body onload='doTest()'>\n"
295             + "</body></html>";
296         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
297 
298         final int requests = getMockWebConnection().getRequestCount();
299         loadPage2(html);
300 
301         final int count = Integer.parseInt(getExpectedAlerts()[0]);
302         assertEquals(count, getMockWebConnection().getRequestCount() - requests);
303         assertEquals(getExpectedAlerts()[1], acceptHeaderString());
304     }
305 
306     /**
307      * @throws Exception if an error occurs
308      */
309     @Test
310     @Alerts(DEFAULT = {"2", "Accept: text/css,*/*;q=0.1"},
311             CHROME = {"1", "Accept: text/html,application/xhtml+xml,"
312                     + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
313                     + "q=0.8,application/signed-exchange;v=b3;q=0.7"},
314             EDGE = {"1", "Accept: text/html,application/xhtml+xml,"
315                     + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
316                     + "q=0.8,application/signed-exchange;v=b3;q=0.7"})
317     public void acceptHeaderCssDifferentType() throws Exception {
318         final String html = DOCTYPE_HTML
319             + "<html><head>\n"
320             + "  <link href='test.css' rel='stylesheet' type='text/html'>\n"
321             + "<script>\n"
322             + "  function doTest() {\n"
323             // force access
324             + "    var b = document.body;\n"
325             + "    window.getComputedStyle(b, null);\n"
326             + "}\n"
327             + "</script></head>\n"
328             + "<body onload='doTest()'>\n"
329             + "</body></html>";
330         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
331 
332         final int requests = getMockWebConnection().getRequestCount();
333         loadPage2(html);
334 
335         final int count = Integer.parseInt(getExpectedAlerts()[0]);
336         assertEquals(count, getMockWebConnection().getRequestCount() - requests);
337         assertEquals(getExpectedAlerts()[1], acceptHeaderString());
338     }
339 
340     /**
341      * @throws Exception if an error occurs
342      */
343     @Test
344     @Alerts(DEFAULT = {"2", "Accept: text/css,*/*;q=0.1"},
345             CHROME = {"1", "Accept: text/html,application/xhtml+xml,"
346                     + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
347                     + "q=0.8,application/signed-exchange;v=b3;q=0.7"},
348             EDGE = {"1", "Accept: text/html,application/xhtml+xml,"
349                     + "application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;"
350                     + "q=0.8,application/signed-exchange;v=b3;q=0.7"})
351     public void acceptHeaderCssWrongType() throws Exception {
352         final String html = DOCTYPE_HTML
353             + "<html><head>\n"
354             + "  <link href='test.css' rel='stylesheet' type='css'>\n"
355             + "<script>\n"
356             + "  function doTest() {\n"
357             // force access
358             + "    var b = document.body;\n"
359             + "    window.getComputedStyle(b, null);\n"
360             + "}\n"
361             + "</script></head>\n"
362             + "<body onload='doTest()'>\n"
363             + "</body></html>";
364         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
365 
366         final int requests = getMockWebConnection().getRequestCount();
367         loadPage2(html);
368 
369         final int count = Integer.parseInt(getExpectedAlerts()[0]);
370         assertEquals(count, getMockWebConnection().getRequestCount() - requests);
371         assertEquals(getExpectedAlerts()[1], acceptHeaderString());
372     }
373 
374     /**
375      * @throws Exception if the test fails
376      */
377     @Test
378     @Alerts("Accept: */*")
379     public void acceptHeaderXMLHttpRequest() throws Exception {
380         final String html = DOCTYPE_HTML
381             + "<html>\n"
382             + "  <head>\n"
383             + "    <title>XMLHttpRequest Test</title>\n"
384             + "    <script>\n"
385             + "      function test() {\n"
386             + "        var request = new XMLHttpRequest();\n"
387             + "        request.open('GET', '" + URL_SECOND + "', false);\n"
388             + "        request.send('');\n"
389             + "      }\n"
390             + "    </script>\n"
391             + "  </head>\n"
392             + "  <body onload='test()'>\n"
393             + "  </body>\n"
394             + "</html>";
395 
396         final String xml =
397               "<xml>\n"
398             + "<content>blah</content>\n"
399             + "<content>blah2</content>\n"
400             + "</xml>";
401 
402         getMockWebConnection().setDefaultResponse(xml);
403         loadPage2(html);
404 
405         // we have to be a bit patient
406         Thread.sleep(100);
407 
408         assertEquals(2, getMockWebConnection().getRequestCount());
409         assertEquals(getExpectedAlerts()[0], acceptHeaderString());
410     }
411 
412     private String acceptHeaderString() {
413         final WebRequest lastRequest = getMockWebConnection().getLastWebRequest();
414         final Map<String, String> headers = lastRequest.getAdditionalHeaders();
415 
416         final StringBuilder sb = new StringBuilder();
417         for (final Entry<String, String> headerEntry : headers.entrySet()) {
418             final String headerName = headerEntry.getKey();
419             final String headerNameLower = headerName.toLowerCase(Locale.ROOT);
420             if (HttpHeader.ACCEPT_LC.equals(headerNameLower)) {
421                 sb.append(headerName);
422                 sb.append(": ");
423                 sb.append(headerEntry.getValue());
424             }
425         }
426         return sb.toString();
427     }
428 }