getComputedStyle-pseudo.html (10458B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <title>CSSOM: Correct resolution of resolved value for display-affected pseudo-elements</title> 4 <link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> 5 <link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values"> 6 <link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> 7 <link rel="author" title="Karl Dubost" href="https://github.com/karlcow"> 8 <script src=/resources/testharness.js></script> 9 <script src=/resources/testharnessreport.js></script> 10 <style> 11 #test { width: 100px; } 12 13 #contents { 14 display: contents; 15 border: 10px solid red; 16 } 17 18 #test::before, 19 #test::after, 20 #contents::before, 21 #contents::after, 22 #flex::before, 23 #flex::after { 24 content: " "; 25 width: 50%; 26 height: 10px; 27 display: block; 28 } 29 #none { 30 display: none; 31 } 32 #none::before, 33 #none::after { 34 content: "Foo"; 35 } 36 #flex { 37 display: flex; 38 } 39 #flex-no-pseudo { 40 display: flex; 41 } 42 #contents-pseudos::before, 43 #contents-pseudos::after { 44 display: contents; 45 content: "foo"; 46 position: absolute; 47 } 48 #contents-pseudos-dynamic::before, 49 #contents-pseudos-dynamic::after { 50 display: block; 51 content: "foo"; 52 position: absolute; 53 } 54 #contents-pseudos-dynamic.contents::before, 55 #contents-pseudos-dynamic.contents::after { 56 display: contents; 57 } 58 #pseudo-invalid::backdrop { 59 color: rgb(0, 128, 0); 60 } 61 #pseudo-invalid::file-selector-button { 62 color: rgb(0, 128, 0); 63 } 64 #pseudo-invalid::grammar-error { 65 color: rgb(0, 128, 0); 66 } 67 #pseudo-invalid::highlight(name) { 68 color: rgb(0, 128, 0); 69 } 70 #pseudo-invalid::marker { 71 color: rgb(0, 128, 0); 72 } 73 #pseudo-invalid::placeholder { 74 color: rgb(0, 128, 0); 75 } 76 #pseudo-invalid::spelling-error { 77 color: rgb(0, 128, 0); 78 } 79 #pseudo-invalid::view-transition { 80 color: rgb(0, 128, 0); 81 } 82 #pseudo-invalid::view-transition-image-pair(name) { 83 color: rgb(0, 128, 0); 84 } 85 #pseudo-invalid::view-transition-group(name) { 86 color: rgb(0, 128, 0); 87 } 88 #pseudo-invalid::view-transition-old(name) { 89 color: rgb(0, 128, 0); 90 } 91 #pseudo-invalid::view-transition-new(name) { 92 color: rgb(0, 128, 0); 93 } 94 #pseudo-invalid { 95 color: rgb(255, 0, 0) 96 } 97 </style> 98 <div id="test"> 99 <div id="contents"></div> 100 <div id="none"></div> 101 <div id="flex"></div> 102 <div id="flex-no-pseudo"></div> 103 <div id="contents-pseudos"></div> 104 <div id="contents-pseudos-dynamic"></div> 105 <ul><li id="pseudo-invalid">Item</li></ul> 106 </div> 107 <script> 108 test(() => { 109 const div = document.getElementById('test'); 110 ["before", "after"].forEach(pseudo => { 111 assert_equals(getComputedStyle(div, pseudo).width, "100px"); 112 }); 113 }, "Resolution of width is correct when pseudo-element argument is ignored (due to no colon)"); 114 115 test(() => { 116 const div = document.getElementById('test'); 117 [ 118 ":before ", 119 "::before ", 120 "::before\t", 121 "::before\f", 122 "::before\n", 123 "::before,", 124 "::before,::after", 125 "::before@after", 126 "::before#after", 127 "::\"before\"", 128 "::before\u0000", 129 "::before-->", 130 "::before0", 131 ].forEach(pseudo => { 132 assert_equals(getComputedStyle(div, pseudo).width, "", pseudo); 133 }); 134 }, "Resolution of width is correct when pseudo-element argument is invalid (due to a trailing token)"); 135 136 test(() => { 137 const div = document.getElementById('test'); 138 [":before", ":after"].forEach(pseudo => { 139 assert_equals(getComputedStyle(div, pseudo).width, "50px"); 140 }); 141 }, "Resolution of width is correct for ::before and ::after pseudo-elements (single-colon)"); 142 143 test(() => { 144 const div = document.getElementById('test'); 145 ["::before", "::after"].forEach(pseudo => { 146 assert_equals(getComputedStyle(div, pseudo).width, "50px"); 147 }); 148 }, "Resolution of width is correct for ::before and ::after pseudo-elements (double-colon)"); 149 150 test(function() { 151 const div = document.getElementById('test'); 152 [":bef\\oRE", "::\\000041fter"].forEach(pseudo => { 153 assert_equals(getComputedStyle(div, pseudo).width, "50px"); 154 }); 155 }, "Pseudo-elements can use the full range of CSS syntax"); 156 157 test(function() { 158 var contents = document.getElementById('contents'); 159 [":before", ":after"].forEach(function(pseudo) { 160 assert_equals(getComputedStyle(contents, pseudo).width, "50px"); 161 }); 162 }, "Resolution of width is correct for ::before and ::after pseudo-elements of display: contents elements"); 163 164 test(function() { 165 var has_no_pseudos = document.body; 166 has_no_pseudos.style.position = "relative"; 167 [":before", ":after"].forEach(function(pseudo) { 168 assert_equals(getComputedStyle(has_no_pseudos, pseudo).position, "static", 169 "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " + 170 "the same style as the originating element"); 171 assert_equals(getComputedStyle(has_no_pseudos, pseudo).width, "auto", 172 "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " + 173 "definite size"); 174 }); 175 }, "Resolution of nonexistent pseudo-element styles"); 176 177 test(function() { 178 var none = document.getElementById('none'); 179 [":before", ":after"].forEach(function(pseudo) { 180 assert_equals(getComputedStyle(none, pseudo).content, "\"Foo\"", 181 "Pseudo-styles of display: none elements should be correct"); 182 }); 183 }, "Resolution of pseudo-element styles in display: none elements"); 184 185 test(function() { 186 var flex = document.getElementById('flex'); 187 [":before", ":after"].forEach(function(pseudo) { 188 assert_equals(getComputedStyle(flex, pseudo).display, "block", 189 "Pseudo-styles of display: flex elements should get blockified"); 190 }); 191 }, "Item-based blockification of pseudo-elements"); 192 193 test(function() { 194 var flexNoPseudo = document.getElementById('flex-no-pseudo'); 195 [":before", ":after"].forEach(function(pseudo) { 196 assert_equals(getComputedStyle(flexNoPseudo, pseudo).display, "block", 197 "Pseudo-styles of display: flex elements should get blockified"); 198 }); 199 }, "Item-based blockification of nonexistent pseudo-elements"); 200 201 test(function() { 202 var contentsPseudos = document.getElementById('contents-pseudos'); 203 [":before", ":after"].forEach(function(pseudo) { 204 assert_equals(getComputedStyle(contentsPseudos, pseudo).display, "contents", 205 "display: contents in " + pseudo + " should get reflected on CSSOM"); 206 assert_equals(getComputedStyle(contentsPseudos, pseudo).width, "auto", 207 pseudo + " with display: contents should have no box"); 208 assert_equals(getComputedStyle(contentsPseudos, pseudo).position, "absolute", 209 "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM"); 210 }); 211 }, "display: contents on pseudo-elements"); 212 213 test(function() { 214 var contentsPseudosDynamic = document.getElementById('contents-pseudos-dynamic'); 215 [":before", ":after"].forEach(function(pseudo) { 216 assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "block", 217 "Check that display for " + pseudo + " is block before change"); 218 }); 219 contentsPseudosDynamic.className = "contents"; 220 [":before", ":after"].forEach(function(pseudo) { 221 assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "contents", 222 "display: contents in " + pseudo + " should get reflected on CSSOM"); 223 assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).width, "auto", 224 pseudo + " with display: contents should have no box"); 225 assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).position, "absolute", 226 "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM"); 227 }); 228 }, "Dynamically change to display: contents on pseudo-elements"); 229 230 test(() => { 231 const div = document.getElementById('test'); 232 // Note that these assertions deliberately avoid assert_[not_]equals to 233 // avoid gCS().length in the failure output. 234 assert_true( 235 getComputedStyle(div, "totallynotapseudo").length != 0, 236 "Should return the element's style for unknown pseudo-elements that don't start with a colon"); 237 assert_true( 238 getComputedStyle(div, "::totallynotapseudo").length == 0, 239 "Should return an empty style for unknown pseudo-elements starting with double-colon"); 240 assert_true( 241 getComputedStyle(div, ":totallynotapseudo").length == 0, 242 "Should return an empty style for unknown pseudo-elements starting with colon"); 243 }, "Unknown pseudo-elements"); 244 245 test(() => { 246 const div = document.getElementById('test'); 247 248 const style1 = getComputedStyle(div, "totallynotapseudo"); 249 assert_throws_dom("NoModificationAllowedError", () => style1.color = "1"); 250 assert_throws_dom("NoModificationAllowedError", () => style1.margin = "10px"); 251 252 const style2 = getComputedStyle(div, "::totallynotapseudo"); 253 assert_throws_dom("NoModificationAllowedError", () => style2.color = "1"); 254 assert_throws_dom("NoModificationAllowedError", () => style2.margin = "10px"); 255 256 const style3 = getComputedStyle(div, ":totallynotapseudo"); 257 assert_throws_dom("NoModificationAllowedError", () => style3.color = "1"); 258 assert_throws_dom("NoModificationAllowedError", () => style3.margin = "10px"); 259 }, "CSSStyleDeclaration is immutable"); 260 261 // If you add a pseudo-element identifier here, don't forget to add the corresponding style rule in 262 // <style> above. 263 [ 264 "backdrop", 265 "file-selector-button", 266 "grammar-error", 267 "highlight(name)", 268 "marker", 269 "placeholder", 270 "spelling-error", 271 "view-transition", 272 "view-transition-image-pair(name)", 273 "view-transition-group(name)", 274 "view-transition-old(name)", 275 "view-transition-new(name)" 276 ].forEach(pseudoIdentifier => { 277 test(() => { 278 assert_implements_optional(CSS.supports(`selector(::${pseudoIdentifier})`), `::${pseudoIdentifier}`); 279 const li = document.querySelector('li'); 280 assert_true( 281 getComputedStyle(li, `:${pseudoIdentifier}`).length == 0, 282 `Should return an empty style for :${pseudoIdentifier}`); 283 assert_true( 284 getComputedStyle(li, pseudoIdentifier).length != 0, 285 `Should return the element style for ${pseudoIdentifier}`); 286 assert_equals( 287 getComputedStyle(li, pseudoIdentifier).color, "rgb(255, 0, 0)", 288 `Should return the element style for ${pseudoIdentifier}`); 289 assert_equals( 290 getComputedStyle(li, `::${pseudoIdentifier}`).color, "rgb(0, 128, 0)", 291 `Should return the ::${pseudoIdentifier} style`); 292 }, `Unknown pseudo-element with a known identifier: ${pseudoIdentifier}`); 293 }); 294 </script>