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.html;
16  
17  import java.nio.charset.StandardCharsets;
18  
19  import org.htmlunit.WebDriverTestCase;
20  import org.htmlunit.junit.annotation.Alerts;
21  import org.htmlunit.util.MimeType;
22  import org.junit.jupiter.api.Test;
23  import org.openqa.selenium.WebDriver;
24  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
25  
26  /**
27   * Tests for {@link HTMLStyleElement}.
28   *
29   * @author Marc Guillemot
30   * @author Ronald Brill
31   * @author Frank Danek
32   */
33  public class HTMLStyleElementTest extends WebDriverTestCase {
34  
35      /**
36       * @throws Exception if the test fails
37       */
38      @Test
39      @Alerts({"[object HTMLStyleElement]", "[object CSSStyleSheet]", "undefined"})
40      public void stylesheet() throws Exception {
41          final String html = DOCTYPE_HTML
42              + "<html><head><script>\n"
43              + LOG_TITLE_FUNCTION
44              + "function doTest() {\n"
45              + "  var f = document.getElementById('myStyle');\n"
46              + "  log(f);\n"
47              + "  log(f.sheet);\n"
48              + "  log(f.styleSheet);\n"
49              + "}</script>\n"
50              + "<style id='myStyle'>p: vertical-align:top</style>\n"
51              + "</head><body onload='doTest()'>\n"
52              + "</body></html>";
53  
54          loadPageVerifyTitle2(html);
55      }
56  
57      /**
58       * As of HtmlUnit-2.5 only the first child node of a STYLE was parsed.
59       * @throws Exception if the test fails
60       */
61      @Test
62      @Alerts("2")
63      public void styleChildren() throws Exception {
64          final String html = DOCTYPE_HTML
65              + "<html><head><script>\n"
66              + LOG_TITLE_FUNCTION
67              + "function doTest() {\n"
68              + "  var doc = document;\n"
69              + "  var style = doc.createElement('style');\n"
70              + "  doc.documentElement.firstChild.appendChild(style);\n"
71              + "  style.appendChild(doc.createTextNode('* { z-index: 0; }\\\n'));\n"
72              + "  style.appendChild(doc.createTextNode('DIV { z-index: 10; position: absolute; }\\\n'));\n"
73              + "  if (doc.styleSheets[0].cssRules)\n"
74              + "    rules = doc.styleSheets[0].cssRules;\n"
75              + "  else\n"
76              + "    rules = doc.styleSheets[0].rules;\n"
77              + "  log(rules.length);\n"
78              + "}</script>\n"
79              + "</head><body onload='doTest()'>\n"
80              + "</body></html>";
81  
82          loadPageVerifyTitle2(html);
83      }
84  
85      /**
86       * @throws Exception if the test fails
87       */
88      @Test
89      @Alerts({".a > .t { }", ".b > .t { }", ".c > .t { }"})
90      public void innerHtml() throws Exception {
91          final String html = DOCTYPE_HTML
92              + "<html><head>\n"
93  
94              + "<style id='style_none'>.a > .t { }</style>\n"
95              + "<style type='text/test' id='style_text'>.b > .t { }</style>\n"
96              + "<style type='text/html' id='style_html'>.c > .t { }</style>\n"
97  
98              + "<script>\n"
99              + LOG_TITLE_FUNCTION
100             + "function doTest() {\n"
101             + "  style = document.getElementById('style_none');\n"
102             + "  log(style.innerHTML);\n"
103             + "  style = document.getElementById('style_text');\n"
104             + "  log(style.innerHTML);\n"
105             + "  style = document.getElementById('style_html');\n"
106             + "  log(style.innerHTML);\n"
107             + "}\n"
108             + "</script>\n"
109             + "</head><body onload='doTest()'>\n"
110             + "</body></html>";
111 
112         loadPageVerifyTitle2(html);
113     }
114 
115     /**
116      * @throws Exception if the test fails
117      */
118     @Test
119     @Alerts({"", "text/test", "text/css"})
120     public void type() throws Exception {
121         final String html = DOCTYPE_HTML
122             + "<html><head>\n"
123 
124             + "<style id='style_none'>my { }</style>\n"
125             + "<style type='text/test' id='style_text'>my { }</style>\n"
126             + "<style type='text/css' id='style_css'>my { }</style>\n"
127 
128             + "<script>\n"
129             + LOG_TITLE_FUNCTION
130             + "function doTest() {\n"
131             + "  style = document.getElementById('style_none');\n"
132             + "  log(style.type);\n"
133             + "  style = document.getElementById('style_text');\n"
134             + "  log(style.type);\n"
135             + "  style = document.getElementById('style_css');\n"
136             + "  log(style.type);\n"
137             + "}\n"
138             + "</script>\n"
139             + "</head><body onload='doTest()'>\n"
140             + "</body></html>";
141 
142         loadPageVerifyTitle2(html);
143     }
144 
145     /**
146      * @throws Exception if the test fails
147      */
148     @Test
149     @Alerts({"", "all", "screen, print,test"})
150     public void media() throws Exception {
151         final String html = DOCTYPE_HTML
152             + "<html><head>\n"
153 
154             + "<style id='style_none'>my { }</style>\n"
155             + "<style media='all' id='style_all'>my { }</style>\n"
156             + "<style media='screen, print,test' id='style_some'>my { }</style>\n"
157 
158             + "<script>\n"
159             + LOG_TITLE_FUNCTION
160             + "function doTest() {\n"
161             + "  style = document.getElementById('style_none');\n"
162             + "  log(style.media);\n"
163             + "  style = document.getElementById('style_all');\n"
164             + "  log(style.media);\n"
165             + "  style = document.getElementById('style_some');\n"
166             + "  log(style.media);\n"
167             + "}\n"
168             + "</script>\n"
169             + "</head><body onload='doTest()'>\n"
170             + "</body></html>";
171 
172         loadPageVerifyTitle2(html);
173     }
174 
175     /**
176      * @throws Exception if the test fails
177      */
178     @Test
179     @Alerts({"all", "", "screen:screen", "priNT", "screen, print"})
180     public void media_setter() throws Exception {
181         final String html = DOCTYPE_HTML
182             + "<html><head>\n"
183 
184             + "<style id='myStyle' media='all'>my { }</style>\n"
185 
186             + "<script>\n"
187             + LOG_TITLE_FUNCTION
188             + "function doTest() {\n"
189             + "  style = document.getElementById('myStyle');\n"
190 
191             + "  log(style.media);\n"
192 
193             + "  style.media = '';\n"
194             + "  log(style.media);\n"
195 
196             + "  style.media = 'screen';\n"
197             + "  log(style.media + ':' + style.attributes['media'].value);\n"
198 
199             + "  style.media = 'priNT';\n"
200             + "  log(style.media);\n"
201 
202             + "  style.media = 'screen, print';\n"
203             + "  log(style.media);\n"
204 
205             + "}\n"
206             + "</script>\n"
207             + "</head><body onload='doTest()'>\n"
208             + "</body></html>";
209 
210         loadPageVerifyTitle2(html);
211     }
212 
213     /**
214      * @throws Exception if the test fails
215      */
216     @Test
217     @Alerts({"undefined", "undefined"})
218     public void scoped() throws Exception {
219         final String html = DOCTYPE_HTML
220             + "<html><head>\n"
221 
222             + "<style id='style_none'>my { }</style>\n"
223 
224             + "<script>\n"
225             + LOG_TITLE_FUNCTION
226             + "function doTest() {\n"
227             + "  style = document.getElementById('style_none');\n"
228             + "  log(style.scoped);\n"
229             + "  style = document.getElementById('style_scoped');\n"
230             + "  log(style.scoped);\n"
231             + "}\n"
232             + "</script>\n"
233             + "</head>\n"
234             + "<body onload='doTest()'>\n"
235             + "  <style id='style_scoped' scoped>my { }</style>\n"
236             + "</body></html>";
237 
238         loadPageVerifyTitle2(html);
239     }
240 
241     /**
242      * @throws Exception if the test fails
243      */
244     @Test
245     @Alerts({"undefined", "true", "false"})
246     public void scoped_setter() throws Exception {
247         final String html = DOCTYPE_HTML
248             + "<html><head>\n"
249 
250             + "<style id='myStyle' media='all'>my { }</style>\n"
251 
252             + "<script>\n"
253             + LOG_TITLE_FUNCTION
254             + "function doTest() {\n"
255             + "  style = document.getElementById('myStyle');\n"
256 
257             + "  log(style.scoped);\n"
258 
259             + "  style.scoped = true;\n"
260             + "  log(style.scoped);\n"
261 
262             + "  style.media = false;\n"
263             + "  log(style.media);\n"
264             + "}\n"
265             + "</script>\n"
266             + "</head><body onload='doTest()'>\n"
267             + "</body></html>";
268 
269         loadPageVerifyTitle2(html);
270     }
271 
272     /**
273      * @throws Exception if the test fails
274      */
275     @Test
276     @Alerts({"", "text/css"})
277     public void type_setter() throws Exception {
278         final String html = DOCTYPE_HTML
279             + "<html><head>\n"
280             + "<style id='style_none'></style>\n"
281 
282             + "<script>\n"
283             + LOG_TITLE_FUNCTION
284             + "function doTest() {\n"
285             + "  style = document.getElementById('style_none');\n"
286             + "  log(style.type);\n"
287             + "  style.type = 'text/css';\n"
288             + "  log(style.type);\n"
289             + "}\n"
290             + "</script>\n"
291             + "</head><body onload='doTest()'>\n"
292             + "</body></html>";
293 
294         loadPageVerifyTitle2(html);
295     }
296 
297     /**
298      * @throws Exception if the test fails
299      */
300     @Test
301     @Alerts({"rgb(0, 128, 0)", "false", "rgb(0, 0, 0)"})
302     public void disabled() throws Exception {
303         final String html = DOCTYPE_HTML
304             + "<html><head>\n"
305             + "<style id='myStyle'> .abc { color: green; }</style>\n"
306 
307             + "<script>\n"
308             + LOG_TITLE_FUNCTION
309             + "function doTest() {\n"
310             + "  var div = document.getElementById('myDiv');\n"
311             + "  var style = document.getElementById('myStyle');\n"
312             + "  log(window.getComputedStyle(div, '').color);\n"
313             + "  log(style.disabled);\n"
314             + "  style.disabled = true;\n"
315             + "  log(window.getComputedStyle(div, '').color);\n"
316             + "}\n"
317             + "</script>\n"
318             + "</head><body onload='doTest()'>\n"
319             + "  <div id='myDiv' class='abc'>abcd</div>\n"
320             + "</body></html>";
321 
322         loadPageVerifyTitle2(html);
323     }
324 
325     /**
326      * @throws Exception if the test fails
327      */
328     @Test
329     @Alerts({"rgb(0, 0, 0)", "rgb(0, 128, 0)", "rgb(0, 0, 255)"})
330     public void styleInCdataXHtml() throws Exception {
331         final String html =
332             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
333             + "<!DOCTYPE html PUBLIC \n"
334             + "  \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n"
335             + "  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
336             + "<html xmlns='http://www.w3.org/1999/xhtml' xmlns:xhtml='http://www.w3.org/1999/xhtml'>\n"
337             + "<head>\n"
338             + "  <style>\n"
339             + "    //<![CDATA[\n"
340             + "      #one {color: red;}\n"
341             + "    //]]>\n"
342             + "  </style>\n"
343             + "  <style>/*<![CDATA[*/ #two {color: green;} /*]]>*/</style>\n"
344             + "  <style>\n"
345             + "    <![CDATA[\n"
346             + "      #three {color: blue;}\n"
347             + "    ]]>\n"
348             + "  </style>\n"
349             + "  <script>\n"
350 
351             // do not use LOG_TITLE_FUNCTION here
352             + "    function log(msg) { window.document.title += msg + '\\u00a7'; }\n"
353             + "    function doTest() {\n"
354             + "      var div = document.getElementById('one');\n"
355             + "      log(window.getComputedStyle(div, null).color);\n"
356             + "      div = document.getElementById('two');\n"
357             + "      log(window.getComputedStyle(div, null).color);\n"
358             + "      div = document.getElementById('three');\n"
359             + "      log(window.getComputedStyle(div, null).color);\n"
360             + "    }\n"
361             + "  </script>\n"
362             + "</head>\n"
363             + "<body onload='doTest()'>\n"
364             + "  <div id='one'>one</div>\n"
365             + "  <div id='two'>two</div>\n"
366             + "  <div id='three'>three</div>\n"
367             + "</body></html>";
368 
369         if (getWebDriver() instanceof HtmlUnitDriver) {
370             getWebClient().getOptions().setThrowExceptionOnScriptError(false);
371         }
372         final WebDriver driver = loadPage2(html, URL_FIRST, MimeType.APPLICATION_XHTML, StandardCharsets.UTF_8);
373         verifyTitle2(driver, getExpectedAlerts());
374     }
375 
376     /**
377      * @throws Exception if the test fails
378      */
379     @Test
380     @Alerts({"rgb(0, 0, 0)", "rgb(0, 128, 0)", "rgb(0, 0, 255)"})
381     public void scriptInCdataXml() throws Exception {
382         final String html =
383             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
384             + "<!DOCTYPE html PUBLIC \n"
385             + "  \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n"
386             + "  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
387             + "<html xmlns='http://www.w3.org/1999/xhtml' xmlns:xhtml='http://www.w3.org/1999/xhtml'>\n"
388             + "<head>\n"
389             + "  <style>\n"
390             + "    //<![CDATA[\n"
391             + "      #one {color: red;}\n"
392             + "    //]]>\n"
393             + "  </style>\n"
394             + "  <style>/*<![CDATA[*/ #two {color: green;} /*]]>*/</style>\n"
395             + "  <style>\n"
396             + "    <![CDATA[\n"
397             + "      #three {color: blue;}\n"
398             + "    ]]>\n"
399             + "  </style>\n"
400             + "  <script>\n"
401 
402             // do not use LOG_TITLE_FUNCTION here
403             + "    function log(msg) { window.document.title += msg + '\\u00a7'; }\n"
404             + "    function doTest() {\n"
405             + "      var div = document.getElementById('one');\n"
406             + "      log(window.getComputedStyle(div, null).color);\n"
407             + "      div = document.getElementById('two');\n"
408             + "      log(window.getComputedStyle(div, null).color);\n"
409             + "      div = document.getElementById('three');\n"
410             + "      log(window.getComputedStyle(div, null).color);\n"
411             + "    }\n"
412             + "  </script>\n"
413             + "</head>\n"
414             + "<body onload='doTest()'>\n"
415             + "  <div id='one'>one</div>\n"
416             + "  <div id='two'>two</div>\n"
417             + "  <div id='three'>three</div>\n"
418             + "</body></html>";
419 
420         if (getWebDriver() instanceof HtmlUnitDriver) {
421             getWebClient().getOptions().setThrowExceptionOnScriptError(false);
422         }
423         final WebDriver driver = loadPage2(html, URL_FIRST, MimeType.TEXT_XML, StandardCharsets.UTF_8);
424         verifyTitle2(driver, getExpectedAlerts());
425     }
426 
427     /**
428      * @throws Exception if the test fails
429      */
430     @Test
431     @Alerts({"rgb(0, 0, 0)", "rgb(0, 128, 0)", "rgb(0, 0, 0)"})
432     public void scriptInCdataHtml() throws Exception {
433         final String html = DOCTYPE_HTML
434             + "<html>\n"
435             + "<head>\n"
436             + "  <style>\n"
437             + "    //<![CDATA[\n"
438             + "      #one {color: red;}\n"
439             + "    //]]>\n"
440             + "  </style>\n"
441             + "  <style>/*<![CDATA[*/ #two {color: green;} /*]]>*/</style>\n"
442             + "  <style>\n"
443             + "    <![CDATA[\n"
444             + "      #three {color: blue;}\n"
445             + "    ]]>\n"
446             + "  </style>\n"
447             + "  <script>\n"
448             + LOG_TITLE_FUNCTION
449             + "    function doTest() {\n"
450             + "      var div = document.getElementById('one');\n"
451             + "      log(window.getComputedStyle(div, null).color);\n"
452             + "      div = document.getElementById('two');\n"
453             + "      log(window.getComputedStyle(div, null).color);\n"
454             + "      div = document.getElementById('three');\n"
455             + "      log(window.getComputedStyle(div, null).color);\n"
456             + "    }\n"
457             + "  </script>\n"
458             + "</head>\n"
459             + "<body onload='doTest()'>\n"
460             + "  <div id='one'>one</div>\n"
461             + "  <div id='two'>two</div>\n"
462             + "  <div id='three'>three</div>\n"
463             + "</body></html>";
464 
465         if (getWebDriver() instanceof HtmlUnitDriver) {
466             getWebClient().getOptions().setThrowExceptionOnScriptError(false);
467         }
468         loadPageVerifyTitle2(html);
469     }
470 
471     /**
472      * @throws Exception if the test fails
473      */
474     @Test
475     @Alerts({"#id { color: red }", ":before { content: \"</> HtmlUnit\" }"})
476     public void innerHtml1() throws Exception {
477         final String html = DOCTYPE_HTML
478             + "<html>\n"
479             + "  <head>\n"
480             + "    <style>#id { color: red }</style>\n"
481             + "    <script>\n"
482             + LOG_TEXTAREA_FUNCTION
483             + "      function test() {\n"
484             + "        var style = document.getElementsByTagName('style')[0];\n"
485             + "        log(style.innerHTML);\n"
486             + "        style.innerHTML = ':before { content: \"</> HtmlUnit\" }';\n"
487             + "        log(style.innerHTML);\n"
488             + "      }\n"
489             + "    </script>\n"
490             + "  </head>\n"
491             + "  <body onload='test()'>\n"
492             + LOG_TEXTAREA
493             + "  </body>\n"
494             + "</html>";
495 
496         loadPageVerifyTextArea2(html);
497     }
498 
499     /**
500      * @throws Exception if the test fails
501      */
502     @Test
503     @Alerts({"#id { color: red }", ":before { content: \"<span>Html</span>Unit\" }"})
504     public void innerHtmlTag() throws Exception {
505         final String html = DOCTYPE_HTML
506             + "<html>\n"
507             + "  <head>\n"
508             + "    <style>#id { color: red }</style>\n"
509             + "    <script>\n"
510             + LOG_TEXTAREA_FUNCTION
511             + "      function test() {\n"
512             + "        var style = document.getElementsByTagName('style')[0];\n"
513             + "        log(style.innerHTML);\n"
514             + "        style.innerHTML = ':before { content: \"<span>Html</span>Unit\" }';\n"
515             + "        log(style.innerHTML);\n"
516             + "      }\n"
517             + "    </script>\n"
518             + "  </head>\n"
519             + "  <body onload='test()'>\n"
520             + LOG_TEXTAREA
521             + "  </body>\n"
522             + "</html>";
523 
524         loadPageVerifyTextArea2(html);
525     }
526 
527     /**
528      * @throws Exception if the test fails
529      */
530     @Test
531     @Alerts({"", ":before { content: \"&lt;/> HtmlUnit\" }"})
532     public void innerHtmlEscaping() throws Exception {
533         final String html = DOCTYPE_HTML
534             + "<html>\n"
535             + "  <head>\n"
536             + "    <style></style>\n"
537             + "    <script>\n"
538             + LOG_TEXTAREA_FUNCTION
539             + "      function test() {\n"
540             + "        var style = document.getElementsByTagName('style')[0];\n"
541             + "        log(style.innerHTML);\n"
542             + "        style.innerHTML = ':before { content: \"&lt;/> HtmlUnit\" }';\n"
543             + "        log(style.innerHTML);\n"
544             + "      }\n"
545             + "    </script>\n"
546             + "  </head>\n"
547             + "  <body onload='test()'>\n"
548             + LOG_TEXTAREA
549             + "  </body>\n"
550             + "</html>";
551 
552         loadPageVerifyTextArea2(html);
553     }
554 }