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.css;
16  
17  import java.net.URL;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.lang3.StringUtils;
22  import org.htmlunit.MockWebConnection;
23  import org.htmlunit.WebClient;
24  import org.htmlunit.WebDriverTestCase;
25  import org.htmlunit.junit.annotation.Alerts;
26  import org.htmlunit.junit.annotation.HtmlUnitNYI;
27  import org.htmlunit.util.MimeType;
28  import org.htmlunit.util.NameValuePair;
29  import org.junit.jupiter.api.Test;
30  import org.openqa.selenium.By;
31  import org.openqa.selenium.WebDriver;
32  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
33  
34  /**
35   * Unit tests for {@link CSSStyleSheet}.
36   *
37   * @author Marc Guillemot
38   * @author Ahmed Ashour
39   * @author Frank Danek
40   * @author Ronald Brill
41   * @author Carsten Steul
42   */
43  public class CSSStyleSheetTest extends WebDriverTestCase {
44  
45      /**
46       * @throws Exception on test failure
47       */
48      @Test
49      @Alerts({"[object CSSStyleSheet]", "[object HTMLStyleElement]", "true", "undefined", "false"})
50      public void owningNodeOwningElement() throws Exception {
51          final String html = DOCTYPE_HTML
52                  + "<html><head>\n"
53                  + "<script>\n"
54                  + LOG_TITLE_FUNCTION
55                  + "function test() {\n"
56                  + "  var myStyle = document.getElementById('myStyle');\n"
57                  + "  var stylesheet = document.styleSheets[0];\n"
58                  + "  log(stylesheet);\n"
59                  + "  log(stylesheet.ownerNode);\n"
60                  + "  log(stylesheet.ownerNode == myStyle);\n"
61                  + "  log(stylesheet.owningElement);\n"
62                  + "  log(stylesheet.owningElement == myStyle);\n"
63                  + "}\n"
64                  + "</script>\n"
65                  + "<style id='myStyle' type='text/css'></style>\n"
66                  + "</head><body onload='test()'>\n"
67                  + "</body></html>";
68          loadPageVerifyTitle2(html);
69      }
70  
71      /**
72       * @throws Exception on test failure
73       */
74      @Test
75      @Alerts(DEFAULT = {"4", "0", "1", "2", "3", "length", "item"},
76              FF = {"4", "0", "1", "2", "3", "item", "length"},
77              FF_ESR = {"4", "0", "1", "2", "3", "item", "length"})
78      public void rules() throws Exception {
79          final String html = DOCTYPE_HTML
80                  + "<html><head>\n"
81                  + "<style>\n"
82                  + "  BODY { background-color: white; color: black; }\n"
83                  + "  H1 { font: 8pt Arial bold; }\n"
84                  + "  P  { font: 10pt Arial; text-indent: 0.5in; }\n"
85                  + "  A  { text-decoration: none; color: blue; }\n"
86                  + "</style>\n"
87                  + "<script>\n"
88                  + LOG_TITLE_FUNCTION
89                  + "  function test() {\n"
90                  + "    var rules;\n"
91                  + "    if (document.styleSheets[0].cssRules)\n"
92                  + "      rules = document.styleSheets[0].cssRules;\n"
93                  + "    else\n"
94                  + "      rules = document.styleSheets[0].rules;\n"
95                  + "    log(rules.length);\n"
96                  + "    for (var i in rules)\n"
97                  + "      log(i);\n"
98                  + "  }\n"
99                  + "</script>\n"
100                 + "</head><body onload='test()'>\n"
101                 + "</body></html>";
102         loadPageVerifyTitle2(html);
103     }
104 
105     /**
106      * Test for bug #680 (missing href attribute).
107      * @throws Exception if an error occurs
108      */
109     @Test
110     @Alerts({"4", "§§URL§§style2.css", "§§URL§§style4.css", "null", "null"})
111     public void href() throws Exception {
112         final String html = DOCTYPE_HTML
113             + "<html>\n"
114             + "  <head>\n"
115             + "    <link href='" + URL_FIRST + "style1.css' type='text/css'></link>\n"
116             + "    <link href='" + URL_FIRST + "style2.css' rel='stylesheet'></link>\n"
117             + "    <link href='" + URL_FIRST + "style3.css'></link>\n"
118             + "    <link href='style4.css' rel='stylesheet'></link>\n"
119             + "    <style>div.x { color: red; }</style>\n"
120             + "  </head>\n"
121             + "  <body>\n"
122             + "    <style>div.y { color: green; }</style>\n"
123             + "    <script>\n"
124             + LOG_TITLE_FUNCTION
125             + "      log(document.styleSheets.length);\n"
126             + "      for (i = 0; i < document.styleSheets.length; i++) {\n"
127             + "        log(document.styleSheets[i].href);\n"
128             + "      }\n"
129             + "    </script>\n" + "  </body>\n"
130             + "</html>";
131 
132         final MockWebConnection conn = getMockWebConnection();
133         conn.setResponse(new URL(URL_FIRST, "style1.css"), "", MimeType.TEXT_CSS);
134         conn.setResponse(new URL(URL_FIRST, "style2.css"), "", MimeType.TEXT_CSS);
135         conn.setResponse(new URL(URL_FIRST, "style3.css"), "", MimeType.TEXT_CSS);
136         conn.setResponse(new URL(URL_FIRST, "style4.css"), "", MimeType.TEXT_CSS);
137 
138         expandExpectedAlertsVariables(URL_FIRST);
139         loadPageVerifyTitle2(html);
140     }
141 
142     /**
143      * @throws Exception if an error occurs
144      */
145     @Test
146     @Alerts({"8", "§§URL§§style1.css 1", "§§URL§§style2.css 0",
147              "§§URL§§style3.css 0", "§§URL§§style4.css 1",
148              "§§URL§§style5.css 1", "§§URL§§style6.css 0",
149              "§§URL§§style7.css 0", "§§URL§§style8.css 1"})
150     public void hrefWrongContentType() throws Exception {
151         final String html = DOCTYPE_HTML
152             + "<html>\n"
153             + "  <head>\n"
154             + "    <link href='" + URL_FIRST + "style1.css' rel='stylesheet' type='text/css'></link>\n"
155             + "    <link href='" + URL_FIRST + "style2.css' rel='stylesheet' type='text/css'></link>\n"
156             + "    <link href='" + URL_FIRST + "style3.css' rel='stylesheet' type='text/css'></link>\n"
157             + "    <link href='" + URL_FIRST + "style4.css' rel='stylesheet' type='text/css'></link>\n"
158             + "    <link href='" + URL_FIRST + "style5.css' rel='stylesheet' ></link>\n"
159             + "    <link href='" + URL_FIRST + "style6.css' rel='stylesheet' ></link>\n"
160             + "    <link href='" + URL_FIRST + "style7.css' rel='stylesheet' ></link>\n"
161             + "    <link href='" + URL_FIRST + "style8.css' rel='stylesheet' ></link>\n"
162             + "  </head>\n" + "  <body>\n"
163             + "    <script>\n"
164             + LOG_TITLE_FUNCTION
165             + "      log(document.styleSheets.length);\n"
166             + "      for (i = 0; i < document.styleSheets.length; i++) {\n"
167             + "        var sheet = document.styleSheets[i];\n"
168             + "        log(sheet.href + ' ' + sheet.cssRules.length);\n"
169             + "      }\n"
170             + "    </script>\n" + "  </body>\n"
171             + "</html>";
172 
173         final MockWebConnection conn = getMockWebConnection();
174         conn.setResponse(new URL(URL_FIRST, "style1.css"), "div { color: red; }", MimeType.TEXT_CSS);
175         conn.setResponse(new URL(URL_FIRST, "style2.css"), "div { color: red; }", MimeType.TEXT_HTML);
176         conn.setResponse(new URL(URL_FIRST, "style3.css"), "div { color: red; }", MimeType.TEXT_PLAIN);
177         conn.setResponse(new URL(URL_FIRST, "style4.css"), "div { color: red; }", "");
178         conn.setResponse(new URL(URL_FIRST, "style5.css"), "div { color: red; }", MimeType.TEXT_CSS);
179         conn.setResponse(new URL(URL_FIRST, "style6.css"), "div { color: red; }", MimeType.TEXT_HTML);
180         conn.setResponse(new URL(URL_FIRST, "style7.css"), "div { color: red; }", MimeType.TEXT_PLAIN);
181         conn.setResponse(new URL(URL_FIRST, "style8.css"), "div { color: red; }", "");
182 
183         expandExpectedAlertsVariables(URL_FIRST);
184         loadPageVerifyTitle2(html);
185     }
186 
187     /**
188      * Minimal test for addRule / insertRule.
189      * @throws Exception if an error occurs
190      */
191     @Test
192     @Alerts({"1", "false", "-1", "div", "color: red;", "2"})
193     public void addRule() throws Exception {
194         final String html = DOCTYPE_HTML
195             + "<html>\n"
196             + "<head>\n"
197             + "<script>\n"
198             + LOG_TITLE_FUNCTION
199             + "  function doTest() {\n"
200             + "    var f = document.getElementById('myStyle');\n"
201             + "    var s = f.sheet ? f.sheet : f.styleSheet;\n"
202             + "    var rules = s.cssRules || s.rules;\n"
203             + "    log(rules.length);\n"
204             + "    log(s.addRule == undefined);\n"
205             + "    if (s.addRule) {\n"
206             + "      log(s.addRule('div', 'color: red;'));\n"
207             + "      log(rules[rules.length - 1].selectorText);\n"
208             + "      log(rules[rules.length - 1].style.cssText);\n"
209             + "    }\n"
210             + "    log(rules.length);\n"
211             + "  }\n"
212             + "</script>\n"
213             + "<style id='myStyle'>p { vertical-align:top }</style>\n"
214             + "</head>\n"
215             + "<body onload='doTest()'>\n"
216             + "</body></html>";
217 
218         loadPageVerifyTitle2(html);
219     }
220 
221     /**
222      * @throws Exception if an error occurs
223      */
224     @Test
225     @Alerts({"2", "-1", "div", "", "3"})
226     public void addRuleInvalidRule() throws Exception {
227         final String html = DOCTYPE_HTML
228                 + "<html>\n"
229                 + "<head>\n"
230                 + "<script>\n"
231                 + LOG_TITLE_FUNCTION
232                 + "  function doTest() {\n"
233                 + "    var f = document.getElementById('myStyle');\n"
234                 + "    var s = f.sheet ? f.sheet : f.styleSheet;\n"
235                 + "    var rules = s.cssRules || s.rules;\n"
236                 + "    log(rules.length);\n"
237                 + "    if (s.addRule) {\n"
238                 + "      log(s.addRule('div', 'invalid'));\n"
239                 + "      log(rules[rules.length - 1].selectorText);\n"
240                 + "      log(rules[rules.length - 1].style.cssText);\n"
241                 + "    }\n"
242                 + "    log(rules.length);\n"
243                 + "  }\n"
244                 + "  </script>\n"
245                 + "  <style id='myStyle'>p { vertical-align: top } h1 { color: blue; }</style>\n"
246                 + "</head>\n"
247                 + "<body onload='doTest()'>\n"
248                 + "</body></html>";
249 
250         loadPageVerifyTitle2(html);
251     }
252 
253     /**
254      * Test that exception handling in addRule.
255      * @throws Exception if an error occurs
256      */
257     @Test
258     @Alerts("SyntaxError/DOMException")
259     public void addInvalidRule() throws Exception {
260         final String html = DOCTYPE_HTML
261             + "<html><head>\n"
262             + "<script>\n"
263             + LOG_TITLE_FUNCTION
264             + "function doTest() {\n"
265             + "  var f = document.getElementById('myStyle');\n"
266             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
267             + "  var rules = s.cssRules || s.rules;\n"
268             + "  try {\n"
269             + "    if (s.addRule)\n"
270             + "      s.addRule('.testStyle1;', '', 0);\n"
271             + "    log('added');\n"
272             + "  } catch(e) { logEx(e); }\n"
273             + "}</script>\n"
274             + "<style id='myStyle'></style>\n"
275             + "</head><body onload='doTest()'>\n"
276             + "</body></html>";
277 
278         loadPageVerifyTitle2(html);
279     }
280 
281     /**
282      * Minimal test for insertRule.
283      * @throws Exception if an error occurs
284      */
285     @Test
286     @Alerts({"1", "false", "0", "div", "color: red;", "2"})
287     public void insertRule() throws Exception {
288         final String html = DOCTYPE_HTML
289             + "<html>\n"
290             + "<head>\n"
291             + "<script>\n"
292             + LOG_TITLE_FUNCTION
293             + "  function doTest() {\n"
294             + "    var f = document.getElementById('myStyle');\n"
295             + "    var s = f.sheet ? f.sheet : f.styleSheet;\n"
296             + "    var rules = s.cssRules || s.rules;\n"
297             + "    log(rules.length);\n"
298             + "    log(s.insertRule == undefined);\n"
299             + "    if (s.insertRule) {\n"
300             + "      log(s.insertRule('div { color: red; }', 0));\n"
301             + "      log(rules[0].selectorText);\n"
302             + "      log(rules[0].style.cssText);\n"
303             + "    }\n"
304             + "    log(rules.length);\n"
305             + "  }\n"
306             + "</script>\n"
307             + "<style id='myStyle'>p { vertical-align:top }</style>\n"
308             + "</head>\n"
309             + "<body onload='doTest()'>\n"
310             + "</body></html>";
311 
312         loadPageVerifyTitle2(html);
313     }
314 
315     /**
316      * @throws Exception if an error occurs
317      */
318     @Test
319     @Alerts({"1", "false", "0", "div", "", "2"})
320     public void insertRuleInvalidRule() throws Exception {
321         final String html = DOCTYPE_HTML
322             + "<html>\n"
323             + "<head>\n"
324             + "<script>\n"
325             + LOG_TITLE_FUNCTION
326             + "  function doTest() {\n"
327             + "    var f = document.getElementById('myStyle');\n"
328             + "    var s = f.sheet ? f.sheet : f.styleSheet;\n"
329             + "    var rules = s.cssRules || s.rules;\n"
330             + "    log(rules.length);\n"
331             + "    log(s.insertRule == undefined);\n"
332             + "    if (s.insertRule) {\n"
333             + "      log(s.insertRule('div {invalid}', 0));\n"
334             + "      log(rules[0].selectorText);\n"
335             + "      log(rules[0].style.cssText);\n"
336             + "    }\n"
337             + "    log(rules.length);\n"
338             + "  }\n"
339             + "</script>\n"
340             + "<style id='myStyle'>p { vertical-align:top }</style>\n"
341             + "</head>\n"
342             + "<body onload='doTest()'>\n"
343             + "</body></html>";
344 
345         loadPageVerifyTitle2(html);
346     }
347 
348     /**
349      * Test that exception handling in insertRule.
350      * @throws Exception if an error occurs
351      */
352     @Test
353     @Alerts("SyntaxError/DOMException")
354     public void insertInvalidRule() throws Exception {
355         final String html = DOCTYPE_HTML
356             + "<html>\n"
357             + "<head>\n"
358             + "<script>\n"
359             + LOG_TITLE_FUNCTION
360             + "function doTest() {\n"
361             + "  var f = document.getElementById('myStyle');\n"
362             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
363             + "  var rules = s.cssRules || s.rules;\n"
364             + "  try {\n"
365             + "    if (s.insertRule)\n"
366             + "      s.insertRule('.testStyle1', 0);\n"
367             + "    log('inserted');\n"
368             + "  } catch(e) { logEx(e); }\n"
369             + "}</script>\n"
370             + "<style id='myStyle'></style>\n"
371             + "</head><body onload='doTest()'>\n"
372             + "</body></html>";
373 
374         loadPageVerifyTitle2(html);
375     }
376 
377     /**
378      * Minimal test for removeRule / deleteRule.
379      * @throws Exception if an error occurs
380      */
381     @Test
382     @Alerts({"2", "false", "false", "undefined", "1", "div", "color: red;"})
383     public void removeRule_deleteRule() throws Exception {
384         final String html = DOCTYPE_HTML
385             + "<html><head><script>\n"
386             + LOG_TITLE_FUNCTION
387             + "function doTest() {\n"
388             + "  var f = document.getElementById('myStyle');\n"
389             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
390             + "  var rules = s.cssRules || s.rules;\n"
391             + "  log(rules.length);\n"
392             + "  log(s.deleteRule == undefined);\n"
393             + "  log(s.removeRule == undefined);\n"
394             + "  if (s.deleteRule)\n"
395             + "    log(s.deleteRule(0));\n"
396             + "  else\n"
397             + "    log(s.removeRule(0));\n"
398             + "  log(rules.length);\n"
399             + "  log(rules[0].selectorText);\n"
400             + "  log(rules[0].style.cssText);\n"
401             + "}</script>\n"
402             + "<style id='myStyle'>p { vertical-align:top } div { color: red; }</style>\n"
403             + "</head><body onload='doTest()'>\n"
404             + "</body></html>";
405 
406         loadPageVerifyTitle2(html);
407     }
408 
409     /**
410      * Test exception handling in deletRule / removeRule.
411      * @throws Exception if an error occurs
412      */
413     @Test
414     @Alerts("IndexSizeError/DOMException")
415     public void deleteRuleInvalidParam() throws Exception {
416         final String html = DOCTYPE_HTML
417             + "<html>\n"
418             + "<head>\n"
419             + "<script>\n"
420             + LOG_TITLE_FUNCTION
421             + "function doTest() {\n"
422             + "  var f = document.getElementById('myStyle');\n"
423             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
424             + "  var rules = s.cssRules || s.rules;\n"
425             + "  try {\n"
426             + "    if (s.deleteRule)\n"
427             + "      s.deleteRule(19);\n"
428             + "    else\n"
429             + "      s.removeRule(19);\n"
430             + "    log('deleted');\n"
431             + "  } catch(e) { logEx(e); }\n"
432             + "}</script>\n"
433             + "<style id='myStyle'></style>\n"
434             + "</head><body onload='doTest()'>\n"
435             + "</body></html>";
436 
437         loadPageVerifyTitle2(html);
438     }
439 
440     /**
441      * @throws Exception if an error occurs
442      */
443     @Test
444     @Alerts({"2", "1", "div", "color: red;"})
445     public void deleteRuleIgnored() throws Exception {
446         final String html = DOCTYPE_HTML
447             + "<html>\n"
448             + "<head>\n"
449             + "<script>\n"
450             + LOG_TITLE_FUNCTION
451             + "function doTest() {\n"
452             + "  var f = document.getElementById('myStyle');\n"
453             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
454             + "  var rules = s.cssRules || s.rules;\n"
455             + "  log(rules.length);\n"
456             + "  try {\n"
457             + "    if (s.deleteRule)\n"
458             + "      s.deleteRule(0);\n"
459             + "    else\n"
460             + "      s.removeRule(0);\n"
461             + "    log(rules.length);\n"
462             + "    log(rules[0].selectorText);\n"
463             + "    log(rules[0].style.cssText);\n"
464             + "  } catch(err) { logEx(e); }\n"
465             + "}</script>\n"
466             + "<style id='myStyle'>\n"
467             + "  p { vertical-align:top }\n"
468             + "  @unknown div { color: red; }\n"
469             + "  div { color: red; }\n"
470             + "</style>\n"
471             + "</head><body onload='doTest()'>\n"
472             + "</body></html>";
473 
474         loadPageVerifyTitle2(html);
475     }
476 
477     /**
478      * @throws Exception if an error occurs
479      */
480     @Test
481     @Alerts({"2", "1", "p", "vertical-align: top;"})
482     public void deleteRuleIgnoredLast() throws Exception {
483         final String html = DOCTYPE_HTML
484             + "<html>\n"
485             + "<head>\n"
486             + "<script>\n"
487             + LOG_TITLE_FUNCTION
488             + "function doTest() {\n"
489             + "  var f = document.getElementById('myStyle');\n"
490             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
491             + "  var rules = s.cssRules || s.rules;\n"
492             + "  log(rules.length);\n"
493             + "  try {\n"
494             + "    if (s.deleteRule)\n"
495             + "      s.deleteRule(1);\n"
496             + "    else\n"
497             + "      s.removeRule(1);\n"
498             + "    log(rules.length);\n"
499             + "    log(rules[0].selectorText);\n"
500             + "    log(rules[0].style.cssText);\n"
501             + "  } catch(err) { logEx(e); }\n"
502             + "}</script>\n"
503             + "<style id='myStyle'>\n"
504             + "  p { vertical-align:top }\n"
505             + "  @unknown div { color: red; }\n"
506             + "  div { color: red; }\n"
507             + "</style>\n"
508             + "</head><body onload='doTest()'>\n"
509             + "</body></html>";
510 
511         loadPageVerifyTitle2(html);
512     }
513 
514     /**
515      * Test that CSSParser can handle leading whitespace in insertRule.
516      * @throws Exception if an error occurs
517      */
518     @Test
519     @Alerts({"2", ".testStyleDef", "height: 42px;", ".testStyle", "width: 24px;"})
520     public void insertRuleLeadingWhitespace() throws Exception {
521         final String html = DOCTYPE_HTML
522             + "<html><head><script>\n"
523             + LOG_TITLE_FUNCTION
524             + "function doTest() {\n"
525             + "  var f = document.getElementById('myStyle');\n"
526             + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
527             + "  var rules = s.cssRules || s.rules;\n"
528             + "  if (s.insertRule) {\n"
529             + "    s.insertRule('.testStyle { width: 24px; }', 0);\n"
530             + "    s.insertRule(' .testStyleDef { height: 42px; }', 0);\n"
531             + "    log(rules.length);\n"
532             + "    log(rules[0].selectorText);\n"
533             + "    log(rules[0].style.cssText);\n"
534             + "    log(rules[1].selectorText);\n"
535             + "    log(rules[1].style.cssText);\n"
536             + "  }\n"
537             + "}</script>\n"
538             + "<style id='myStyle'></style>\n"
539             + "</head><body onload='doTest()'>\n"
540             + "</body></html>";
541 
542         loadPageVerifyTitle2(html);
543     }
544 
545     /**
546      * @throws Exception on test failure
547      */
548     @Test
549     @Alerts({"false", "false", "true", "true", "false"})
550     public void langCondition() throws Exception {
551         final String htmlSnippet = "<div id='elt2' lang='en'></div>\n"
552                 + "  <div id='elt3' lang='en-GB'></div>\n"
553                 + "  <div id='elt4' lang='english'></div>\n";
554         doTest(":lang(en)", htmlSnippet);
555     }
556 
557     /**
558      * @throws Exception on test failure
559      */
560     @Test
561     @Alerts({"false", "false", "true", "false", "true"})
562     public void langConditionParent() throws Exception {
563         final String htmlSnippet =
564                 "<div id='elt2' lang='en'>\n"
565                 + "  <div id='elt3' lang='de'></div>\n"
566                 + "  <div id='elt4' ></div>\n"
567                 + "</div>\n";
568         doTest(":lang(en)", htmlSnippet);
569     }
570 
571     /**
572      * @throws Exception on test failure
573      */
574     @Test
575     @Alerts({"true", "false"})
576     public void css2_root() throws Exception {
577         doTest(":root", "");
578     }
579 
580     /**
581      * CSS3 pseudo selector :not is not yet supported.
582      * @throws Exception on test failure
583      */
584     @Test
585     @Alerts({"true", "true", "false"})
586     public void css3_not() throws Exception {
587         doTest(":not(span)", "<span id='elt2'></span>");
588     }
589 
590     /**
591      * @throws Exception on test failure
592      */
593     @Test
594     @Alerts({"false", "false", "true", "false", "true", "true", "true", "true"})
595     public void css3_enabled() throws Exception {
596         final String htmlSnippet = "<input id='elt2'>\n"
597             + "<input id='elt3' disabled>\n"
598             + "<input id='elt4' type='checkbox'>\n"
599             + "<button id='elt5' ></button>\n"
600             + "<select id='elt6' ></select>\n"
601             + "<textarea id='elt7' ></textarea>\n";
602         doTest(":enabled", htmlSnippet);
603     }
604 
605     /**
606      * @throws Exception on test failure
607      */
608     @Test
609     @Alerts({"false", "false", "true", "false", "true", "true", "true", "true"})
610     public void css3_disabled() throws Exception {
611         final String htmlSnippet = "<input id='elt2' disabled>\n"
612             + "<input id='elt3'>\n"
613             + "<input id='elt4' type='checkbox' disabled>\n"
614             + "<button id='elt5' disabled></button>\n"
615             + "<select id='elt6' disabled></select>\n"
616             + "<textarea id='elt7' disabled></textarea>\n";
617         doTest(":disabled", htmlSnippet);
618     }
619 
620     /**
621      * @throws Exception on test failure
622      */
623     @Test
624     @Alerts({"false", "false", "false", "false", "true", "false", "true", "false"})
625     public void css3_checked() throws Exception {
626         final String htmlSnippet = "  <input id='elt2'>\n"
627             + "  <input id='elt3' checked>\n"
628             + "  <input id='elt4' type='checkbox' checked>\n"
629             + "  <input id='elt5' type='checkbox'>\n"
630             + "  <input id='elt6' type='radio' checked>\n"
631             + "  <input id='elt7' type='radio'>\n";
632         doTest(":checked", htmlSnippet);
633     }
634 
635     /**
636      * @throws Exception on test failure
637      */
638     @Test
639     @Alerts({"false", "false", "true", "false", "false", "false", "true", "true", "false", "false"})
640     public void css3_required() throws Exception {
641         final String htmlSnippet =
642             "  <input id='elt2' required>\n"
643             + "  <input id='elt3' type='text'>\n"
644             + "  <select id='elt4'></select>\n"
645             + "  <textarea id='elt5'></textarea>\n"
646             + "  <textarea id='elt6' required=false></textarea>\n"
647             + "  <textarea id='elt7' required=true></textarea>\n"
648             + "  <div id='elt8'></div>\n"
649             + "  <div id='elt9' required=true></div>\n";
650         doTest(":required", htmlSnippet);
651     }
652 
653     /**
654      * @throws Exception on test failure
655      */
656     @Test
657     @Alerts({"false", "false", "false", "true", "true", "true", "false", "false", "false", "false"})
658     public void css3_optional() throws Exception {
659         final String htmlSnippet =
660             "  <input id='elt2' required>\n"
661             + "  <input id='elt3' type='text'>\n"
662             + "  <select id='elt4'></select>\n"
663             + "  <textarea id='elt5'></textarea>\n"
664             + "  <textarea id='elt6' required=false></textarea>\n"
665             + "  <textarea id='elt7' required=true></textarea>\n"
666             + "  <div id='elt8'></div>\n"
667             + "  <div id='elt9' required=true></div>\n";
668         doTest(":optional", htmlSnippet);
669     }
670 
671     private void doTest(final String cssSelector, final String htmlSnippet) throws Exception {
672         final String html = DOCTYPE_HTML
673                 + "<html id='elt0'><head>\n"
674                 + "<style>\n"
675                 + cssSelector + " { z-index: 10 }\n"
676                 + "</style>\n"
677                 + "<script>\n"
678                 + LOG_TITLE_FUNCTION
679                 + "  function test() {\n"
680                 + "    var getStyle = function(e) {\n"
681                 + "      return window.getComputedStyle(e, '');\n"
682                 + "    };\n"
683                 + "    var i = 0;\n"
684                 + "    while (true) {\n"
685                 + "      var elt = document.getElementById('elt' + i++);\n"
686                 + "      if (!elt) return;\n"
687                 + "      log(getStyle(elt).zIndex == 10);\n"
688                 + "    }\n"
689                 + "  }\n"
690                 + "</script>\n"
691                 + "</head><body onload='test()'>\n"
692                 + "  <div id='elt1'></div>\n"
693                 + htmlSnippet
694                 + "</body></html>";
695         loadPageVerifyTitle2(html);
696     }
697 
698     /**
699      * Test for handling priority !important.
700      * @see <a href="http://sf.net/support/tracker.php?aid=2880057">Bug 2880057</a>
701      * @throws Exception if an error occurs
702      */
703     @Test
704     @Alerts("width=100")
705     public void important() throws Exception {
706         final String html = DOCTYPE_HTML
707             + "<html>\n"
708             + "<head>\n"
709             + "<script>\n"
710             + LOG_TITLE_FUNCTION
711             + "function doTest() {\n"
712             + "  var e = document.getElementById('style1');\n"
713             + "  log('width=' + e.clientWidth);\n"
714             + "}\n"
715             + "</script>\n"
716             + "<style>\n"
717             + "#style1 {left: 25px; width: 100px !important;}\n"
718             + "#style1 {position: absolute; left: 100px; width: 50px; height: 50px;}\n"
719             + "</style>\n"
720             + "</head><body onload='doTest()'>\n"
721             + "<div id='style1'>Hello</div>\n"
722             + "</body></html>";
723 
724         loadPageVerifyTitle2(html);
725     }
726 
727     /**
728      * Test for handling/ignoring @font-face.
729      * see bug 2984265
730      * @throws Exception if an error occurs
731      */
732     @Test
733     @Alerts("none")
734     public void fontFace() throws Exception {
735         final String html = DOCTYPE_HTML
736             + "<html>\n"
737             + "<head>\n"
738             + "<script>\n"
739             + LOG_TITLE_FUNCTION
740             + "function doTest() {\n"
741             + "  var e = document.getElementById('div1');\n"
742             + "  var s = window.getComputedStyle(e, '');\n"
743             + "  log(s.display);\n"
744             + "}\n"
745             + "</script>\n"
746             + "<style>\n"
747             + "  @font-face { font-family: YanoneKaffeesatz; src: url(/YanoneKaffeesatz-Regular.otf); }\n"
748             + "  body { font-family: YanoneKaffeesatz; }\n"
749             + "  div { display: none; }\n"
750             + "</style>\n"
751             + "</head><body onload='doTest()'>\n"
752             + "<div id='div1'>invisible</div>\n"
753             + "visible\n"
754             + "</body></html>";
755 
756         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
757 
758         loadPageVerifyTitle2(html);
759     }
760 
761     /**
762      * Test that the rule with higher specificity wins.
763      * @throws Exception on test failure
764      */
765     @Test
766     @Alerts("60")
767     public void rulePriority_specificity() throws Exception {
768         final String html = DOCTYPE_HTML
769             + "<html><head>\n"
770             + "<style>\n"
771             + "div { z-index: 60 }\n"
772             + "* { z-index: 10 }\n"
773             + "</style></head>\n"
774             + "<body>\n"
775             + "<div id='it'>hello</div>\n"
776             + "<script>\n"
777             + LOG_TITLE_FUNCTION
778             + "  var getStyle = function(e) {\n"
779             + "    return window.getComputedStyle(e, '');\n"
780             + "  };\n"
781             + "  log(getStyle(document.getElementById('it')).zIndex);\n"
782             + "</script>\n"
783             + "</body></html>";
784 
785         loadPageVerifyTitle2(html);
786     }
787 
788     /**
789      * Test that the rule with higher specificity wins. More complete case.
790      * @throws Exception on test failure
791      */
792     @Test
793     @Alerts("60")
794     public void rulePriority_specificity2() throws Exception {
795         final String html = DOCTYPE_HTML
796             + "<html><head>\n"
797             + "<style>\n"
798             + ".classA .classB .classC { z-index: 60 }\n"
799             + ".classA .classC { z-index: 10 }\n"
800             + "</style></head>\n"
801             + "<body>\n"
802             + "<div class='classA'>\n"
803             + "<div class='classB'>\n"
804             + "<div id='it' class='classC'>hello</div>\n"
805             + "</div>\n"
806             + "</div>\n"
807             + "<script>\n"
808             + LOG_TITLE_FUNCTION
809             + "  var getStyle = function(e) {\n"
810             + "    return window.getComputedStyle(e, '');\n"
811             + "  };\n"
812             + "  log(getStyle(document.getElementById('it')).zIndex);\n"
813             + "</script>\n"
814             + "</body></html>";
815 
816         loadPageVerifyTitle2(html);
817     }
818 
819     /**
820      * Test that the last one wins when selectors have the same specificity.
821      * @throws Exception on test failure
822      */
823     @Test
824     @Alerts({"10", "10"})
825     public void rulePriority_position() throws Exception {
826         final String html = DOCTYPE_HTML
827             + "<html><head>\n"
828             + "<style>\n"
829             + ".classA { z-index: 60 }\n"
830             + ".classB { z-index: 10 }\n"
831             + "</style></head>\n"
832             + "<body>\n"
833             + "<div id='it1' class='classA classB'>hello</div>\n"
834             + "<div id='it2' class='classA classB'>hello</div>\n"
835             + "<script>\n"
836             + LOG_TITLE_FUNCTION
837             + "  var getStyle = function(e) {\n"
838             + "    return window.getComputedStyle(e, '');\n"
839             + "  };\n"
840             + "  log(getStyle(document.getElementById('it1')).zIndex);\n"
841             + "  log(getStyle(document.getElementById('it2')).zIndex);\n"
842             + "</script>\n"
843             + "</body></html>";
844 
845         loadPageVerifyTitle2(html);
846     }
847 
848     /**
849      * @throws Exception if an error occurs
850      */
851     @Test
852     @Alerts({"none", "1"})
853     public void mediaOnStyleTag_noMedia() throws Exception {
854         mediaOnStyleTag("");
855     }
856 
857     /**
858      * @throws Exception if an error occurs
859      */
860     @Test
861     @Alerts({"none", "1"})
862     public void mediaOnStyleTag_whitespace() throws Exception {
863         mediaOnStyleTag(" ");
864     }
865 
866     /**
867      * @throws Exception if an error occurs
868      */
869     @Test
870     @Alerts({"none", "1"})
871     public void mediaOnStyleTag_all() throws Exception {
872         mediaOnStyleTag("all");
873     }
874 
875     /**
876      * @throws Exception if an error occurs
877      */
878     @Test
879     @Alerts({"none", "1"})
880     public void mediaOnStyleTag_screen() throws Exception {
881         mediaOnStyleTag("screen");
882     }
883 
884     /**
885      * @throws Exception if an error occurs
886      */
887     @Test
888     @Alerts({"block", "1"})
889     public void mediaOnStyleTag_print() throws Exception {
890         mediaOnStyleTag("print");
891     }
892 
893     /**
894      * @throws Exception if an error occurs
895      */
896     @Test
897     @Alerts({"none", "1"})
898     public void mediaOnStyleTag_print_not() throws Exception {
899         mediaOnStyleTag("not print");
900     }
901 
902     /**
903      * @throws Exception if an error occurs
904      */
905     @Test
906     @Alerts({"none", "1"})
907     public void mediaOnStyleTag_multipleWithScreen() throws Exception {
908         mediaOnStyleTag("print, screen, tv");
909     }
910 
911     /**
912      * @throws Exception if an error occurs
913      */
914     @Test
915     @Alerts({"block", "1"})
916     public void mediaOnStyleTag_multipleWithoutScreen() throws Exception {
917         mediaOnStyleTag("print, projection, tv");
918     }
919 
920     private void mediaOnStyleTag(final String media) throws Exception {
921         final String html = DOCTYPE_HTML
922             + "<html><head>\n"
923             + "<style media='" + media + "'> div { display: none }</style>\n"
924             + "</head><body>\n"
925             + "<div id='d'>hello</div>\n"
926             + "<script>\n"
927             + LOG_TITLE_FUNCTION
928             + "  var getStyle = function(e) {\n"
929             + "    return window.getComputedStyle(e, '');\n"
930             + "  };\n"
931             + "  log(getStyle(document.getElementById('d')).display);\n"
932             + "  log(document.styleSheets.length);\n"
933             + "</script></body></html>";
934         loadPageVerifyTitle2(html);
935     }
936 
937     /**
938      * @throws Exception if an error occurs
939      */
940     @Test
941     @Alerts({"none", "1"})
942     public void mediaOnLinkTag_noMedia() throws Exception {
943         mediaOnLinkTag("");
944     }
945 
946     /**
947      * @throws Exception if an error occurs
948      */
949     @Test
950     @Alerts({"none", "1"})
951     public void mediaOnLinkTag_whitespace() throws Exception {
952         mediaOnLinkTag(" ");
953     }
954 
955     /**
956      * @throws Exception if an error occurs
957      */
958     @Test
959     @Alerts({"none", "1"})
960     public void mediaOnLinkTag_all() throws Exception {
961         mediaOnLinkTag("all");
962     }
963 
964     /**
965      * @throws Exception if an error occurs
966      */
967     @Test
968     @Alerts({"none", "1"})
969     public void mediaOnLinkTag_screen() throws Exception {
970         mediaOnLinkTag("screen");
971     }
972 
973     /**
974      * @throws Exception if an error occurs
975      */
976     @Test
977     @Alerts({"block", "0"})
978     public void mediaOnLinkTag_notScreen() throws Exception {
979         mediaOnLinkTag("print");
980     }
981 
982     /**
983      * @throws Exception if an error occurs
984      */
985     @Test
986     @Alerts({"none", "1"})
987     public void mediaOnLinkTag_multipleWithScreen() throws Exception {
988         mediaOnLinkTag("print, screen, tv");
989     }
990 
991     /**
992      * @throws Exception if an error occurs
993      */
994     @Test
995     @Alerts({"block", "0"})
996     public void mediaOnLinkTag_multipleWithoutScreen() throws Exception {
997         mediaOnLinkTag("print, projection, tv");
998     }
999 
1000     private void mediaOnLinkTag(final String media) throws Exception {
1001         final String html = DOCTYPE_HTML
1002             + "<html><head>\n"
1003             + "<link rel='stylesheet' media='" + media + "' href='" + URL_SECOND + "'></link>\n"
1004             + "</head><body>\n"
1005             + "<div id='d'>hello</div>\n"
1006             + "<script>\n"
1007             + LOG_TITLE_FUNCTION
1008             + "  var getStyle = function(e) {\n"
1009             + "    return window.getComputedStyle(e, '');\n"
1010             + "  };\n"
1011             + "  log(getStyle(document.getElementById('d')).display);\n"
1012             + "  log(document.styleSheets.length);\n"
1013             + "</script></body></html>";
1014 
1015         getMockWebConnection().setResponse(URL_SECOND, "div { display: none }", MimeType.TEXT_CSS);
1016         loadPageVerifyTitle2(html);
1017     }
1018 
1019     /**
1020      * @throws Exception if an error occurs
1021      */
1022     @Test
1023     @Alerts({"none", "1"})
1024     public void mediaRule_screen() throws Exception {
1025         mediaRule("screen");
1026     }
1027 
1028     /**
1029      * @throws Exception if an error occurs
1030      */
1031     @Test
1032     @Alerts({"block", "1"})
1033     public void mediaRule_notScreen() throws Exception {
1034         mediaRule("print");
1035     }
1036 
1037     /**
1038      * @throws Exception if an error occurs
1039      */
1040     @Test
1041     @Alerts({"none", "1"})
1042     public void mediaRule_multipleWithScreen() throws Exception {
1043         mediaRule("print, screen, tv");
1044     }
1045 
1046     /**
1047      * @throws Exception if an error occurs
1048      */
1049     @Test
1050     @Alerts({"block", "1"})
1051     public void mediaRule_multipleWithoutScreen() throws Exception {
1052         mediaRule("print, projection, tv");
1053     }
1054 
1055     /**
1056      * @throws Exception if an error occurs
1057      */
1058     @Test
1059     @Alerts({"block", "1"})
1060     public void mediaRule_max_width() throws Exception {
1061         mediaRule("screen and (max-width: 123px)");
1062     }
1063 
1064     /**
1065      * @throws Exception if an error occurs
1066      */
1067     @Test
1068     @Alerts({"none", "1"})
1069     public void mediaRule_max_width_match() throws Exception {
1070         mediaRule("screen and (max-width: 10000px)");
1071     }
1072 
1073     /**
1074      * @throws Exception if an error occurs
1075      */
1076     @Test
1077     @Alerts({"block", "1"})
1078     public void mediaRule_max_width_invalid() throws Exception {
1079         mediaRule("screen and (max-width: 5kilo)");
1080     }
1081 
1082     /**
1083      * @throws Exception if an error occurs
1084      */
1085     @Test
1086     @Alerts({"block", "1"})
1087     public void mediaRule_max_width_without_unit() throws Exception {
1088         mediaRule("screen and (max-width: 10000)");
1089     }
1090 
1091     /**
1092      * @throws Exception if an error occurs
1093      */
1094     @Test
1095     @Alerts({"block", "1"})
1096     public void mediaRule_max_width_without_value() throws Exception {
1097         mediaRule("screen and (max-width)");
1098         mediaRule("screen and (max-width:)");
1099     }
1100 
1101     /**
1102      * @throws Exception if an error occurs
1103      */
1104     @Test
1105     @Alerts({"block", "1"})
1106     public void mediaRule_min_width() throws Exception {
1107         mediaRule("screen and (min-width: 10000px)");
1108     }
1109 
1110     /**
1111      * @throws Exception if an error occurs
1112      */
1113     @Test
1114     @Alerts({"none", "1"})
1115     public void mediaRule_min_width_match() throws Exception {
1116         mediaRule("screen and (min-width: 123px)");
1117     }
1118 
1119     /**
1120      * @throws Exception if an error occurs
1121      */
1122     @Test
1123     @Alerts({"block", "1"})
1124     public void mediaRule_min_width_invalid() throws Exception {
1125         mediaRule("screen and (min-width: 5kilo)");
1126     }
1127 
1128     /**
1129      * @throws Exception if an error occurs
1130      */
1131     @Test
1132     @Alerts({"block", "1"})
1133     public void mediaRule_min_width_without_unit() throws Exception {
1134         mediaRule("screen and (min-width: 123)");
1135     }
1136 
1137     /**
1138      * @throws Exception if an error occurs
1139      */
1140     @Test
1141     @Alerts({"block", "1"})
1142     public void mediaRule_min_width_without_value() throws Exception {
1143         mediaRule("screen and (min-width)");
1144         mediaRule("screen and (min-width:)");
1145     }
1146 
1147     /**
1148      * @throws Exception if an error occurs
1149      */
1150     @Test
1151     @Alerts({"block", "1"})
1152     public void mediaRule_max_device_width() throws Exception {
1153         mediaRule("screen and (max-device-width: 123px)");
1154     }
1155 
1156     /**
1157      * @throws Exception if an error occurs
1158      */
1159     @Test
1160     @Alerts({"none", "1"})
1161     public void mediaRule_max_device_width_match() throws Exception {
1162         mediaRule("screen and (max-device-width: 10000px)");
1163     }
1164 
1165     /**
1166      * @throws Exception if an error occurs
1167      */
1168     @Test
1169     @Alerts({"block", "1"})
1170     public void mediaRule_max_device_width_invalid() throws Exception {
1171         mediaRule("screen and (max-device-width: 5kilo)");
1172     }
1173 
1174     /**
1175      * @throws Exception if an error occurs
1176      */
1177     @Test
1178     @Alerts({"block", "1"})
1179     public void mediaRule_max_device_width_without_unit() throws Exception {
1180         mediaRule("screen and (max-device-width: 10000)");
1181     }
1182 
1183     /**
1184      * @throws Exception if an error occurs
1185      */
1186     @Test
1187     @Alerts({"block", "1"})
1188     public void mediaRule_max_device_width_without_value() throws Exception {
1189         mediaRule("screen and (max-device-width)");
1190         mediaRule("screen and (max-device-width:)");
1191     }
1192 
1193     /**
1194      * @throws Exception if an error occurs
1195      */
1196     @Test
1197     @Alerts({"block", "1"})
1198     public void mediaRule_min_device_width() throws Exception {
1199         mediaRule("screen and (min-device-width: 10000px)");
1200     }
1201 
1202     /**
1203      * @throws Exception if an error occurs
1204      */
1205     @Test
1206     @Alerts({"none", "1"})
1207     public void mediaRule_min_device_width_match() throws Exception {
1208         mediaRule("screen and (min-device-width: 123px)");
1209     }
1210 
1211     /**
1212      * @throws Exception if an error occurs
1213      */
1214     @Test
1215     @Alerts({"block", "1"})
1216     public void mediaRule_min_device_width_invalid() throws Exception {
1217         mediaRule("screen and (min-device-width: 5kilo)");
1218     }
1219 
1220     /**
1221      * @throws Exception if an error occurs
1222      */
1223     @Test
1224     @Alerts({"block", "1"})
1225     public void mediaRule_min_device_width_without_unit() throws Exception {
1226         mediaRule("screen and (min-device-width: 123)");
1227     }
1228 
1229     /**
1230      * @throws Exception if an error occurs
1231      */
1232     @Test
1233     @Alerts({"block", "1"})
1234     public void mediaRule_min_device_width_without_value() throws Exception {
1235         mediaRule("screen and (min-device-width)");
1236         mediaRule("screen and (min-device-width:)");
1237     }
1238 
1239     /**
1240      * @throws Exception if an error occurs
1241      */
1242     @Test
1243     @Alerts({"block", "1"})
1244     public void mediaRule_max_height() throws Exception {
1245         mediaRule("screen and (max-height: 123px)");
1246     }
1247 
1248     /**
1249      * @throws Exception if an error occurs
1250      */
1251     @Test
1252     @Alerts({"none", "1"})
1253     public void mediaRule_max_height_match() throws Exception {
1254         mediaRule("screen and (max-height: 10000px)");
1255     }
1256 
1257     /**
1258      * @throws Exception if an error occurs
1259      */
1260     @Test
1261     @Alerts({"block", "1"})
1262     public void mediaRule_max_height_invalid() throws Exception {
1263         mediaRule("screen and (max-height: 5kilo)");
1264     }
1265 
1266     /**
1267      * @throws Exception if an error occurs
1268      */
1269     @Test
1270     @Alerts({"block", "1"})
1271     public void mediaRule_max_height_without_unit() throws Exception {
1272         mediaRule("screen and (max-height: 10000)");
1273     }
1274 
1275     /**
1276      * @throws Exception if an error occurs
1277      */
1278     @Test
1279     @Alerts({"block", "1"})
1280     public void mediaRule_max_height_without_value() throws Exception {
1281         mediaRule("screen and (max-height)");
1282         mediaRule("screen and (max-height:)");
1283     }
1284 
1285     /**
1286      * @throws Exception if an error occurs
1287      */
1288     @Test
1289     @Alerts({"block", "1"})
1290     public void mediaRule_min_height() throws Exception {
1291         mediaRule("screen and (min-height: 10000px)");
1292     }
1293 
1294     /**
1295      * @throws Exception if an error occurs
1296      */
1297     @Test
1298     @Alerts({"none", "1"})
1299     public void mediaRule_min_height_match() throws Exception {
1300         mediaRule("screen and (min-height: 123px)");
1301     }
1302 
1303     /**
1304      * @throws Exception if an error occurs
1305      */
1306     @Test
1307     @Alerts({"block", "1"})
1308     public void mediaRule_min_height_invalid() throws Exception {
1309         mediaRule("screen and (min-height: 5kilo)");
1310     }
1311 
1312     /**
1313      * @throws Exception if an error occurs
1314      */
1315     @Test
1316     @Alerts({"block", "1"})
1317     public void mediaRule_min_height_without_unit() throws Exception {
1318         mediaRule("screen and (min-height: 123)");
1319     }
1320 
1321     /**
1322      * @throws Exception if an error occurs
1323      */
1324     @Test
1325     @Alerts({"block", "1"})
1326     public void mediaRule_min_height_without_value() throws Exception {
1327         mediaRule("screen and (min-height)");
1328         mediaRule("screen and (min-height:)");
1329     }
1330 
1331     /**
1332      * @throws Exception if an error occurs
1333      */
1334     @Test
1335     @Alerts({"block", "1"})
1336     public void mediaRule_max_device_height() throws Exception {
1337         mediaRule("screen and (max-device-height: 123px)");
1338     }
1339 
1340     /**
1341      * @throws Exception if an error occurs
1342      */
1343     @Test
1344     @Alerts({"none", "1"})
1345     public void mediaRule_max_device_height_match() throws Exception {
1346         mediaRule("screen and (max-device-height: 10000px)");
1347     }
1348 
1349     /**
1350      * @throws Exception if an error occurs
1351      */
1352     @Test
1353     @Alerts({"block", "1"})
1354     public void mediaRule_max_device_height_invalid() throws Exception {
1355         mediaRule("screen and (max-device-height: 5kilo)");
1356     }
1357 
1358     /**
1359      * @throws Exception if an error occurs
1360      */
1361     @Test
1362     @Alerts({"block", "1"})
1363     public void mediaRule_max_device_height_without_unit() throws Exception {
1364         mediaRule("screen and (max-device-height: 10000)");
1365     }
1366 
1367     /**
1368      * @throws Exception if an error occurs
1369      */
1370     @Test
1371     @Alerts({"block", "1"})
1372     public void mediaRule_max_device_height_without_value() throws Exception {
1373         mediaRule("screen and (max-device-height)");
1374         mediaRule("screen and (max-device-height:)");
1375     }
1376 
1377     /**
1378      * @throws Exception if an error occurs
1379      */
1380     @Test
1381     @Alerts({"block", "1"})
1382     public void mediaRule_min_device_height() throws Exception {
1383         mediaRule("screen and (min-device-height: 10000px)");
1384     }
1385 
1386     /**
1387      * @throws Exception if an error occurs
1388      */
1389     @Test
1390     @Alerts({"none", "1"})
1391     public void mediaRule_min_device_height_match() throws Exception {
1392         mediaRule("screen and (min-device-height: 123px)");
1393     }
1394 
1395     /**
1396      * @throws Exception if an error occurs
1397      */
1398     @Test
1399     @Alerts({"block", "1"})
1400     public void mediaRule_min_device_height_invalid() throws Exception {
1401         mediaRule("screen and (min-device-height: 5kilo)");
1402     }
1403 
1404     /**
1405      * @throws Exception if an error occurs
1406      */
1407     @Test
1408     @Alerts({"block", "1"})
1409     public void mediaRule_min_device_height_without_unit() throws Exception {
1410         mediaRule("screen and (min-device-height: 123)");
1411     }
1412 
1413     /**
1414      * @throws Exception if an error occurs
1415      */
1416     @Test
1417     @Alerts({"block", "1"})
1418     public void mediaRule_min_device_height_without_value() throws Exception {
1419         mediaRule("screen and (min-device-height)");
1420         mediaRule("screen and (min-device-height:)");
1421     }
1422 
1423     /**
1424      * @throws Exception if an error occurs
1425      */
1426     @Test
1427     @Alerts({"block", "1"})
1428     public void mediaRule_resolution() throws Exception {
1429         mediaRule("screen and (resolution: 4dpi)");
1430     }
1431 
1432     /**
1433      * @throws Exception if an error occurs
1434      */
1435     @Test
1436     @Alerts({"none", "1"})
1437     public void mediaRule_resolution_match() throws Exception {
1438         mediaRule("screen and (resolution: 96dpi)");
1439     }
1440 
1441     /**
1442      * @throws Exception if an error occurs
1443      */
1444     @Test
1445     @Alerts({"block", "1"})
1446     public void mediaRule_resolution_invalid() throws Exception {
1447         mediaRule("screen and (resolution: 5kilo)");
1448     }
1449 
1450     /**
1451      * @throws Exception if an error occurs
1452      */
1453     @Test
1454     @Alerts({"block", "1"})
1455     public void mediaRule_resolution_without_unit() throws Exception {
1456         mediaRule("screen and (resolution: 96)");
1457     }
1458 
1459     /**
1460      * @throws Exception if an error occurs
1461      */
1462     @Test
1463     @Alerts({"none", "1"})
1464     public void mediaRule_resolution_without_value() throws Exception {
1465         mediaRule("screen and (resolution)");
1466     }
1467 
1468     /**
1469      * @throws Exception if an error occurs
1470      */
1471     @Test
1472     @Alerts({"block", "1"})
1473     public void mediaRule_resolution_without_value_empty() throws Exception {
1474         mediaRule("screen and (resolution:)");
1475     }
1476 
1477     /**
1478      * @throws Exception if an error occurs
1479      */
1480     @Test
1481     @Alerts({"block", "1"})
1482     public void mediaRule_max_resolution() throws Exception {
1483         mediaRule("screen and (max-resolution: 90dpi)");
1484     }
1485 
1486     /**
1487      * @throws Exception if an error occurs
1488      */
1489     @Test
1490     @Alerts({"none", "1"})
1491     public void mediaRule_max_resolution_match() throws Exception {
1492         mediaRule("screen and (max-resolution: 10000dpi)");
1493     }
1494 
1495     /**
1496      * @throws Exception if an error occurs
1497      */
1498     @Test
1499     @Alerts({"block", "1"})
1500     public void mediaRule_max_resolution_invalid() throws Exception {
1501         mediaRule("screen and (max-resolution: 5kilo)");
1502     }
1503 
1504     /**
1505      * @throws Exception if an error occurs
1506      */
1507     @Test
1508     @Alerts({"block", "1"})
1509     public void mediaRule_max_resolution_without_unit() throws Exception {
1510         mediaRule("screen and (max-resolution: 10000)");
1511     }
1512 
1513     /**
1514      * @throws Exception if an error occurs
1515      */
1516     @Test
1517     @Alerts({"block", "1"})
1518     public void mediaRule_max_resolution_without_value() throws Exception {
1519         mediaRule("screen and (max-resolution)");
1520         mediaRule("screen and (max-resolution:)");
1521     }
1522 
1523     /**
1524      * @throws Exception if an error occurs
1525      */
1526     @Test
1527     @Alerts({"block", "1"})
1528     public void mediaRule_min_resolution() throws Exception {
1529         mediaRule("screen and (min-resolution: 10000dpi)");
1530     }
1531 
1532     /**
1533      * @throws Exception if an error occurs
1534      */
1535     @Test
1536     @Alerts({"none", "1"})
1537     public void mediaRule_min_resolution_match() throws Exception {
1538         mediaRule("screen and (min-resolution: 10dpi)");
1539     }
1540 
1541     /**
1542      * @throws Exception if an error occurs
1543      */
1544     @Test
1545     @Alerts({"block", "1"})
1546     public void mediaRule_min_resolution_invalid() throws Exception {
1547         mediaRule("screen and (min-resolution: 5kilo)");
1548     }
1549 
1550     /**
1551      * @throws Exception if an error occurs
1552      */
1553     @Test
1554     @Alerts({"block", "1"})
1555     public void mediaRule_min_resolution_without_unit() throws Exception {
1556         mediaRule("screen and (min-resolution: 10)");
1557     }
1558 
1559     /**
1560      * @throws Exception if an error occurs
1561      */
1562     @Test
1563     @Alerts({"block", "1"})
1564     public void mediaRule_min_resolution_without_value() throws Exception {
1565         mediaRule("screen and (min-resolution)");
1566         mediaRule("screen and (min-resolution:)");
1567     }
1568 
1569     /**
1570      * @throws Exception if an error occurs
1571      */
1572     @Test
1573     @Alerts({"block", "1"})
1574     public void mediaRule_portrait() throws Exception {
1575         mediaRule("screen and (orientation: portrait)");
1576     }
1577 
1578     /**
1579      * @throws Exception if an error occurs
1580      */
1581     @Test
1582     @Alerts({"none", "1"})
1583     public void mediaRule_portrait_not() throws Exception {
1584         mediaRule("not screen and (orientation: portrait)");
1585     }
1586 
1587     /**
1588      * @throws Exception if an error occurs
1589      */
1590     @Test
1591     @Alerts({"none", "1"})
1592     public void mediaRule_landscape() throws Exception {
1593         mediaRule("screen and (orientation: landscape)");
1594     }
1595 
1596     /**
1597      * @throws Exception if an error occurs
1598      */
1599     @Test
1600     @Alerts({"block", "1"})
1601     public void mediaRule_landscape_not() throws Exception {
1602         mediaRule("not screen and (orientation: landscape)");
1603     }
1604 
1605     /**
1606      * @throws Exception if an error occurs
1607      */
1608     @Test
1609     @Alerts({"block", "1"})
1610     public void mediaRule_invalidOrientation() throws Exception {
1611         mediaRule("screen and (orientation: unknown)");
1612     }
1613 
1614     /**
1615      * @throws Exception if an error occurs
1616      */
1617     @Test
1618     @Alerts({"none", "1"})
1619     public void mediaRule_orientation_without_value() throws Exception {
1620         mediaRule("screen and (orientation)");
1621     }
1622 
1623     /**
1624      * @throws Exception if an error occurs
1625      */
1626     @Test
1627     @Alerts({"block", "1"})
1628     public void mediaRule_orientation_without_value_empty() throws Exception {
1629         mediaRule("screen and (orientation:)");
1630     }
1631 
1632     private void mediaRule(final String media) throws Exception {
1633         final String html = DOCTYPE_HTML
1634             + "<html>\n"
1635             + "<head>\n"
1636             + "  <style> @media " + media + " { div { display: none } }</style>\n"
1637             + "</head>\n"
1638             + "<body>\n"
1639             + "  <div id='d'>hello</div>\n"
1640             + "  <script>\n"
1641             + LOG_TITLE_FUNCTION
1642             + "    var getStyle = function(e) {\n"
1643             + "      return window.getComputedStyle(e, '');\n"
1644             + "    };\n"
1645             + "    log(getStyle(document.getElementById('d')).display);\n"
1646             + "    log(document.styleSheets.length);\n"
1647             + "  </script>\n"
1648             + "</body></html>";
1649         loadPageVerifyTitle2(html);
1650     }
1651 
1652     /**
1653      * @throws Exception if an error occurs
1654      */
1655     @Test
1656     @Alerts("rgb(255, 0, 0)")
1657     public void veryBig() throws Exception {
1658         final WebDriver driver = getWebDriver();
1659 
1660         int maxInMemory = 0;
1661         if (driver instanceof HtmlUnitDriver) {
1662             final WebClient webClient = getWebClient();
1663             maxInMemory = webClient.getOptions().getMaxInMemory();
1664         }
1665 
1666         final String html = DOCTYPE_HTML
1667             + "<html>\n"
1668             + "  <head>\n"
1669             + "    <link href='" + URL_FIRST + "style.css' rel='stylesheet'></link>\n"
1670             + "  </head>\n"
1671             + "  <body>\n"
1672             + "    <a href='second.html'>second page</a>\n"
1673             + "  </body>\n"
1674             + "</html>";
1675 
1676         final String html2 = "<html>\n"
1677                 + "  <head>\n"
1678                 + "    <link href='" + URL_FIRST + "style.css' rel='stylesheet'></link>\n"
1679                 + "  </head>\n"
1680                 + "  <body class='someRed'>\n"
1681                 + "    <script>\n"
1682                 + "      var getStyle = function(e) {\n"
1683                 + "        return window.getComputedStyle(e, '');\n"
1684                 + "      };\n"
1685                 + "      alert(getStyle(document.body).color);\n"
1686                 + "    </script>\n"
1687                 + "  </body>\n"
1688                 + "</html>";
1689 
1690         final MockWebConnection conn = getMockWebConnection();
1691         final List<NameValuePair> headers2 = new ArrayList<>();
1692         headers2.add(new NameValuePair("Last-Modified", "Sun, 15 Jul 2007 20:46:27 GMT"));
1693         final String bigContent = ".someRed { color: red; }" + StringUtils.repeat(' ', maxInMemory);
1694         conn.setResponse(new URL(URL_FIRST, "style2.css"), bigContent, 200, "OK", MimeType.TEXT_CSS, headers2);
1695         conn.setResponse(new URL(URL_FIRST, "second.html"), html2);
1696 
1697         final List<NameValuePair> headers1 = new ArrayList<>();
1698         headers1.add(new NameValuePair("Location", "style2.css"));
1699         conn.setResponse(new URL(URL_FIRST, "style.css"), "", 302, "Moved", MimeType.TEXT_CSS, headers1);
1700 
1701         loadPage2(html, new URL(URL_FIRST, "test.html"));
1702         driver.findElement(By.linkText("second page")).click();
1703         verifyAlerts(driver, getExpectedAlerts());
1704     }
1705 
1706     /**
1707      * Test that calling insertRule before retrieving the rules works.
1708      * @throws Exception if an error occurs
1709      */
1710     @Test
1711     @Alerts("inserted")
1712     public void insertRuleWithoutGetRules() throws Exception {
1713         final String html = DOCTYPE_HTML
1714                 + "<html>\n"
1715                 + "<head>\n"
1716                 + "<script>\n"
1717                 + LOG_TITLE_FUNCTION
1718                 + "function doTest() {\n"
1719                 + "  var f = document.getElementById('myStyle');\n"
1720                 + "  var s = f.sheet ? f.sheet : f.styleSheet;\n"
1721                 + "  try {\n"
1722                 + "    if (s.insertRule) {\n"
1723                 + "      s.insertRule('.testStyle1 { color: red; }', 0);\n"
1724                 + "    } else {\n"
1725                 + "      s.addRule('.testStyle1', 'color: red;', 0);\n"
1726                 + "    }\n"
1727                 + "    log('inserted');\n"
1728                 + "  } catch(err) { logEx(e); }\n"
1729                 + "}</script>\n"
1730                 + "<style id='myStyle'></style>\n"
1731                 + "</head>\n"
1732                 + "<body onload='doTest()'>\n"
1733                 + "</body></html>";
1734 
1735         loadPageVerifyTitle2(html);
1736     }
1737 
1738     /**
1739      * @throws Exception if the test fails
1740      */
1741     @Test
1742     public void isDisplayed() throws Exception {
1743         final String html = "<!DOCTYPE html>\n"
1744                 + "<head>\n"
1745                 + "<style>\n"
1746                 + "  .tab div {\n"
1747                 + "    display: none;\n"
1748                 + "  }\n"
1749                 + "\n"
1750                 + "  .tab div:target {\n"
1751                 + "    display: block;\n"
1752                 + "  }\n"
1753                 + "</style></head><body>\n"
1754                 + "<div class='tab'>\n"
1755                 + "  <div id='anchor'>\n"
1756                 + "    <p>Content</p>\n"
1757                 + "  </div>\n"
1758                 + "</div>\n"
1759                 + "</body></html>";
1760         getMockWebConnection().setDefaultResponse(html);
1761         final WebDriver webDriver = loadPage2(html);
1762         assertFalse(webDriver.findElement(By.id("anchor")).isDisplayed());
1763         webDriver.get(URL_FIRST + "#anchor");
1764         assertTrue(webDriver.findElement(By.id("anchor")).isDisplayed());
1765     }
1766 
1767     /**
1768      * @throws Exception if the test fails
1769      */
1770     @Test
1771     public void indexLowercaseElement() throws Exception {
1772         final String html = "<!DOCTYPE html>\n"
1773                 + "<head>\n"
1774                 + "<style>\n"
1775                 + "  div { display: none; }\n"
1776                 + "</style>"
1777                 + "</head>\n"
1778                 + "<body>\n"
1779                 + "  <div id='di'>\n"
1780                 + "    <p>Content</p>\n"
1781                 + "  </div>\n"
1782                 + "</body></html>";
1783 
1784         final WebDriver webDriver = loadPage2(html);
1785         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1786     }
1787 
1788     /**
1789      * @throws Exception if the test fails
1790      */
1791     @Test
1792     public void indexUppercaseElement() throws Exception {
1793         final String html = "<!DOCTYPE html>\n"
1794                 + "<head>\n"
1795                 + "<style>\n"
1796                 + "  div { display: none; }\n"
1797                 + "</style>"
1798                 + "</head>\n"
1799                 + "<body>\n"
1800                 + "  <DIV id='di'>\n"
1801                 + "    <p>Content</p>\n"
1802                 + "  </DIV>\n"
1803                 + "</body></html>";
1804 
1805         final WebDriver webDriver = loadPage2(html);
1806         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1807     }
1808 
1809     /**
1810      * @throws Exception if the test fails
1811      */
1812     @Test
1813     public void indexUppercaseRule() throws Exception {
1814         final String html = "<!DOCTYPE html>\n"
1815                 + "<head>\n"
1816                 + "<style>\n"
1817                 + "  DIV { display: none; }\n"
1818                 + "</style>"
1819                 + "</head>\n"
1820                 + "<body>\n"
1821                 + "  <div id='di'>\n"
1822                 + "    <p>Content</p>\n"
1823                 + "  </div>\n"
1824                 + "</body></html>";
1825 
1826         final WebDriver webDriver = loadPage2(html);
1827         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1828     }
1829 
1830     /**
1831      * @throws Exception if the test fails
1832      */
1833     @Test
1834     public void indexLowercaseClass() throws Exception {
1835         final String html = "<!DOCTYPE html>\n"
1836                 + "<head>\n"
1837                 + "<style>\n"
1838                 + "  .cls { display: none; }\n"
1839                 + "</style>"
1840                 + "</head>\n"
1841                 + "<body>\n"
1842                 + "  <div id='di' class='cls'>\n"
1843                 + "    <p>Content</p>\n"
1844                 + "  </div>\n"
1845                 + "</body></html>";
1846 
1847         final WebDriver webDriver = loadPage2(html);
1848         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1849     }
1850 
1851     /**
1852      * @throws Exception if the test fails
1853      */
1854     @Test
1855     public void indexUppercaseElementClass() throws Exception {
1856         final String html = "<!DOCTYPE html>\n"
1857                 + "<head>\n"
1858                 + "<style>\n"
1859                 + "  .cls { display: none; }\n"
1860                 + "</style>"
1861                 + "</head>\n"
1862                 + "<body>\n"
1863                 + "  <div id='di' class='CLS'>\n"
1864                 + "    <p>Content</p>\n"
1865                 + "  </div>\n"
1866                 + "</body></html>";
1867 
1868         final WebDriver webDriver = loadPage2(html);
1869         assertTrue(webDriver.findElement(By.id("di")).isDisplayed());
1870     }
1871 
1872     /**
1873      * @throws Exception if the test fails
1874      */
1875     @Test
1876     public void indexUppercaseRuleClass() throws Exception {
1877         final String html = "<!DOCTYPE html>\n"
1878                 + "<head>\n"
1879                 + "<style>\n"
1880                 + "  .CLS { display: none; }\n"
1881                 + "</style>"
1882                 + "</head>\n"
1883                 + "<body>\n"
1884                 + "  <div id='di' class='cls'>\n"
1885                 + "    <p>Content</p>\n"
1886                 + "  </div>\n"
1887                 + "</body></html>";
1888 
1889         final WebDriver webDriver = loadPage2(html);
1890         assertTrue(webDriver.findElement(By.id("di")).isDisplayed());
1891     }
1892 
1893     /**
1894      * @throws Exception if the test fails
1895      */
1896     @Test
1897     public void indexUppercaseClass() throws Exception {
1898         final String html = "<!DOCTYPE html>\n"
1899                 + "<head>\n"
1900                 + "<style>\n"
1901                 + "  .CLS { display: none; }\n"
1902                 + "</style>"
1903                 + "</head>\n"
1904                 + "<body>\n"
1905                 + "  <div id='di' class='CLS'>\n"
1906                 + "    <p>Content</p>\n"
1907                 + "  </div>\n"
1908                 + "</body></html>";
1909 
1910         final WebDriver webDriver = loadPage2(html);
1911         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1912     }
1913 
1914     /**
1915      * @throws Exception if the test fails
1916      */
1917     @Test
1918     public void indexLowercase2Class() throws Exception {
1919         final String html = "<!DOCTYPE html>\n"
1920                 + "<head>\n"
1921                 + "<style>\n"
1922                 + "  div.cls { display: none; }\n"
1923                 + "</style>"
1924                 + "</head>\n"
1925                 + "<body>\n"
1926                 + "  <div id='di' class='cls'>\n"
1927                 + "    <p>Content</p>\n"
1928                 + "  </div>\n"
1929                 + "</body></html>";
1930 
1931         final WebDriver webDriver = loadPage2(html);
1932         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1933     }
1934 
1935     /**
1936      * @throws Exception if the test fails
1937      */
1938     @Test
1939     public void indexUppercase2ElementClass() throws Exception {
1940         final String html = "<!DOCTYPE html>\n"
1941                 + "<head>\n"
1942                 + "<style>\n"
1943                 + "  div.cls { display: none; }\n"
1944                 + "</style>"
1945                 + "</head>\n"
1946                 + "<body>\n"
1947                 + "  <DIV id='di' class='CLS'>\n"
1948                 + "    <p>Content</p>\n"
1949                 + "  </DIV>\n"
1950                 + "</body></html>";
1951 
1952         final WebDriver webDriver = loadPage2(html);
1953         assertTrue(webDriver.findElement(By.id("di")).isDisplayed());
1954     }
1955 
1956     /**
1957      * @throws Exception if the test fails
1958      */
1959     @Test
1960     public void indexUppercase2RuleClass() throws Exception {
1961         final String html = "<!DOCTYPE html>\n"
1962                 + "<head>\n"
1963                 + "<style>\n"
1964                 + "  div.CLS { display: none; }\n"
1965                 + "</style>"
1966                 + "</head>\n"
1967                 + "<body>\n"
1968                 + "  <diV id='di' class='cls'>\n"
1969                 + "    <p>Content</p>\n"
1970                 + "  </diV>\n"
1971                 + "</body></html>";
1972 
1973         final WebDriver webDriver = loadPage2(html);
1974         assertTrue(webDriver.findElement(By.id("di")).isDisplayed());
1975     }
1976 
1977     /**
1978      * @throws Exception if the test fails
1979      */
1980     @Test
1981     public void indexUppercase2Class() throws Exception {
1982         final String html = "<!DOCTYPE html>\n"
1983                 + "<head>\n"
1984                 + "<style>\n"
1985                 + "  DiV.CLS { display: none; }\n"
1986                 + "</style>"
1987                 + "</head>\n"
1988                 + "<body>\n"
1989                 + "  <div id='di' class='CLS'>\n"
1990                 + "    <p>Content</p>\n"
1991                 + "  </div>\n"
1992                 + "</body></html>";
1993 
1994         final WebDriver webDriver = loadPage2(html);
1995         assertFalse(webDriver.findElement(By.id("di")).isDisplayed());
1996     }
1997 
1998     /**
1999      * Test for #1300.
2000      * @throws Exception if the test fails
2001      */
2002     @Test
2003     @Alerts("undefined")
2004     public void brokenExternalCSS() throws Exception {
2005         final String html = DOCTYPE_HTML
2006             + "<html><head>\n"
2007             + "<link rel='stylesheet' type='text/css' href='" + URL_SECOND + "'/>\n"
2008             + "</head>\n"
2009             + "<body>\n"
2010             + "<script>\n"
2011             + LOG_TITLE_FUNCTION
2012             + "  log(document.body.currentStyle);\n"
2013             + "</script>\n"
2014             + "</body>\n"
2015             + "</html>";
2016 
2017         getMockWebConnection().setResponse(URL_SECOND, "body { font-weight: 900\\9; }");
2018         loadPageVerifyTitle2(html);
2019     }
2020 
2021     /**
2022      * Test for #941.
2023      * @throws Exception if the test fails
2024      */
2025     @Test
2026     @Alerts({"true", "false"})
2027     public void widthHeightPercent() throws Exception {
2028         widthHeightPercent(DOCTYPE_HTML);
2029     }
2030 
2031     /**
2032      * Test for #941.
2033      * @throws Exception if the test fails
2034      */
2035     @Test
2036     @Alerts({"true", "true"})
2037     public void widthHeightPercentQuirks() throws Exception {
2038         widthHeightPercent("");
2039     }
2040 
2041     private void widthHeightPercent(final String doctype) throws Exception {
2042         final String html = doctype
2043             + "<html>\n"
2044             + "  <head>"
2045             + "    <style>#testDiv { width: 50%; height: 50%; background-color: blue; }</style>\n"
2046             + "  </head>"
2047             + "  <body>\n"
2048             + "    <div id='testDiv'>Test Div</div>\n"
2049 
2050             + "<script>\n"
2051             + LOG_TITLE_FUNCTION
2052             + "  let elem = document.getElementById('testDiv');\n"
2053             + "  let sty = window.getComputedStyle(elem, null);\n"
2054             + "  let w = (window.innerWidth / 2) - parseInt(sty.width, 10);\n"
2055             + "  log(10 > w);\n"
2056             + "  let h = (window.innerHeight / 2) - parseInt(sty.height, 10);\n"
2057             + "  log(10 > h);\n"
2058             + "</script>\n"
2059 
2060             + "  </body>\n"
2061             + "</html>";
2062 
2063         loadPageVerifyTitle2(html);
2064     }
2065 
2066     /**
2067      * Test for #942.
2068      * @throws Exception if the test fails
2069      */
2070     @Test
2071     @Alerts(CHROME = "break at: 10 664.25 / 621",
2072             EDGE = "break at: 10 664.25 / 630",
2073             FF = "break at: 10 675.2000122070312 / 675",
2074             FF_ESR = "break at: 10 675.2000122070312 / 675")
2075     @HtmlUnitNYI(CHROME = "break at: 16 637 / 605",
2076             EDGE = "break at: 16 637 / 605",
2077             FF = "break at: 15 616 / 605",
2078             FF_ESR = "break at: 15 616 / 605")
2079     public void endlessLoop() throws Exception {
2080         final String html = DOCTYPE_HTML
2081             + "<html>\n"
2082             + "  <head>"
2083             + "  </head>"
2084             + "  <body>\n"
2085             + "    <h1>Scroll me</h1>\n"
2086 
2087             + "<script>\n"
2088             + LOG_TITLE_FUNCTION
2089 
2090             + "  var lastBottom = 0;\n"
2091             + "  for (let i = 0; i < 70; i++) {\n"
2092             + "    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;\n"
2093             + "    let stop = document.documentElement.clientHeight;"
2094             + "    if (windowRelativeBottom > stop) {\n"
2095             + "      log('break at: ' + i + ' ' + windowRelativeBottom + ' / ' + stop);\n"
2096             + "      break;\n"
2097             + "    }\n"
2098             + "    if (lastBottom >= windowRelativeBottom) {\n"
2099             + "      log('error at: ' + i + ' ' + windowRelativeBottom + ' / ' + lastBottom);\n"
2100             + "      break;\n"
2101             + "    }\n"
2102             + "    lastBottom = windowRelativeBottom;\n"
2103 
2104             + "    document.body.insertAdjacentHTML('beforeend', '<h1>H 1</h1>');\n"
2105             + "  }\n"
2106             + "</script>\n"
2107 
2108             + "  </body>\n"
2109             + "</html>";
2110 
2111         loadPageVerifyTitle2(html);
2112     }
2113 }