at-function-cssom.html (11173B)
1 <!DOCTYPE html> 2 <title>CSS Custom Functions: CSSOM</title> 3 <link rel="help" href="https://drafts.csswg.org/css-mixins-1/#cssom"> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 7 <script> 8 9 test(t => { 10 let sheet = new CSSStyleSheet(); 11 sheet.replaceSync(` 12 @function --foo() {} 13 `); 14 assert_equals(sheet.cssRules.length, 1); 15 let functionRule = sheet.cssRules[0]; 16 assert_true(functionRule instanceof CSSFunctionRule); 17 assert_equals(functionRule.cssRules.length, 0); 18 }, 'Empty CSSFunctionRule'); 19 20 test(t => { 21 let sheet = new CSSStyleSheet(); 22 sheet.replaceSync(` 23 @function --foo() { 24 result: 100px; 25 } 26 `); 27 assert_equals(sheet.cssRules.length, 1); 28 let functionRule = sheet.cssRules[0]; 29 assert_true(functionRule instanceof CSSFunctionRule); 30 assert_equals(functionRule.cssRules.length, 1); 31 let declarationsRule = functionRule.cssRules[0]; 32 assert_true(declarationsRule instanceof CSSFunctionDeclarations); 33 let descriptors = declarationsRule.style; 34 assert_true(descriptors instanceof CSSFunctionDescriptors); 35 }, 'Single CSSFunctionDeclarations'); 36 37 test(t => { 38 let sheet = new CSSStyleSheet(); 39 sheet.replaceSync(` 40 @function --foo() { 41 result: 100px; 42 } 43 `); 44 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 45 assert_true(descriptors instanceof CSSFunctionDescriptors); 46 assert_equals(descriptors.result, '100px'); 47 }, 'CSSFunctionDescriptors (result)'); 48 49 test(t => { 50 let sheet = new CSSStyleSheet(); 51 sheet.replaceSync(` 52 @function --foo() { 53 result: 100px; 54 result: 101px; 55 } 56 `); 57 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 58 assert_true(descriptors instanceof CSSFunctionDescriptors); 59 assert_equals(descriptors.result, '101px'); 60 }, 'CSSFunctionDescriptors (result, repeated)'); 61 62 test(t => { 63 let sheet = new CSSStyleSheet(); 64 sheet.replaceSync(` 65 @function --foo() { 66 --x: 1px; 67 --y: 2px; 68 } 69 `); 70 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 71 assert_true(descriptors instanceof CSSFunctionDescriptors); 72 assert_equals(descriptors.getPropertyValue('--x'), '1px'); 73 assert_equals(descriptors.getPropertyValue('--y'), '2px'); 74 assert_equals(descriptors.getPropertyValue('--unknown'), ''); 75 assert_equals(descriptors.result, ''); 76 }, 'CSSFunctionDescriptors (local variables)'); 77 78 test(t => { 79 let sheet = new CSSStyleSheet(); 80 sheet.replaceSync(` 81 @function --foo() { 82 --x: 1px; 83 --y: 2px; 84 --x: 3px; 85 } 86 `); 87 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 88 assert_true(descriptors instanceof CSSFunctionDescriptors); 89 assert_equals(descriptors.getPropertyValue('--x'), '3px'); 90 assert_equals(descriptors.getPropertyValue('--y'), '2px'); 91 assert_equals(descriptors.getPropertyValue('--unknown'), ''); 92 assert_equals(descriptors.result, ''); 93 }, 'CSSFunctionDescriptors (local variables, repeated)'); 94 95 test(t => { 96 let sheet = new CSSStyleSheet(); 97 sheet.replaceSync(` 98 @function --foo() { 99 --x: 1px; 100 --y: 2px; 101 result: 3px; 102 } 103 `); 104 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 105 assert_true(descriptors instanceof CSSFunctionDescriptors); 106 assert_equals(descriptors.getPropertyValue('--x'), '1px'); 107 assert_equals(descriptors.getPropertyValue('--y'), '2px'); 108 assert_equals(descriptors.getPropertyValue('--unknown'), ''); 109 assert_equals(descriptors.result, '3px'); 110 }, 'CSSFunctionDescriptors (local variables and result)'); 111 112 test(t => { 113 let sheet = new CSSStyleSheet(); 114 sheet.replaceSync(` 115 @function --foo() { 116 --x: 1px; 117 --y: 2px; 118 result: 3px; 119 } 120 `); 121 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 122 assert_true(descriptors instanceof CSSFunctionDescriptors); 123 assert_equals(descriptors.cssText, '--x: 1px; --y: 2px; result: 3px;'); 124 }, 'CSSFunctionDescriptors serialization'); 125 126 test(t => { 127 let sheet = new CSSStyleSheet(); 128 sheet.replaceSync(` 129 @function --foo() { 130 --x: 1px; 131 syntax: "<length>"; 132 --y: 2px; 133 color: red; 134 unknown: unknown; 135 result: 3px; 136 } 137 `); 138 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 139 assert_true(descriptors instanceof CSSFunctionDescriptors); 140 assert_equals(descriptors.length, 3); 141 assert_equals(descriptors.cssText, '--x: 1px; --y: 2px; result: 3px;'); 142 }, 'Unknown descriptors'); 143 144 test(t => { 145 let sheet = new CSSStyleSheet(); 146 sheet.replaceSync(` 147 @function --foo() { 148 --x: 1px; 149 } 150 `); 151 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 152 assert_true(descriptors instanceof CSSFunctionDescriptors); 153 assert_equals(descriptors.length, 1); 154 descriptors.cssText = '--x: 1px; color: red; result: 3px; --y: 2px;'; 155 assert_equals(descriptors.length, 3); 156 assert_equals(descriptors.cssText, '--x: 1px; result: 3px; --y: 2px;'); 157 }, 'Unknown descriptors (mutation)'); 158 159 test(t => { 160 let sheet = new CSSStyleSheet(); 161 sheet.replaceSync(` 162 @function --foo() { 163 --x: 1px; 164 result: 2px; 165 } 166 `); 167 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 168 assert_true(descriptors instanceof CSSFunctionDescriptors); 169 assert_equals(descriptors.length, 2); 170 assert_equals(descriptors.item(0), '--x'); 171 assert_equals(descriptors.item(1), 'result'); 172 }, 'item()'); 173 174 // https://webidl.spec.whatwg.org/#dfn-indexed-property-getter 175 test(t => { 176 let sheet = new CSSStyleSheet(); 177 sheet.replaceSync(` 178 @function --foo() { 179 --x: 1px; 180 result: 2px; 181 } 182 `); 183 let descriptors = sheet.cssRules[0]?.cssRules[0]?.style; 184 assert_true(descriptors instanceof CSSFunctionDescriptors); 185 assert_array_equals(Array.from(descriptors), ['--x', 'result']);; 186 }, 'Indexed property getter'); 187 188 // Conditional rules 189 190 test(t => { 191 let sheet = new CSSStyleSheet(); 192 sheet.replaceSync(` 193 @function --foo() { 194 @supports (width: 100px) { 195 result: 100px; 196 } 197 result: 200px; 198 } 199 `); 200 assert_equals(sheet.cssRules.length, 1); 201 let functionRule = sheet.cssRules[0]; 202 assert_true(functionRule instanceof CSSFunctionRule); 203 assert_equals(functionRule.cssRules.length, 2); 204 let supportsRule = functionRule.cssRules[0]; 205 assert_true(supportsRule instanceof CSSSupportsRule); 206 assert_true(supportsRule.cssRules[0] instanceof CSSFunctionDeclarations); 207 assert_equals(supportsRule.cssRules[0].style?.result, '100px'); 208 209 let declarationsRule = functionRule.cssRules[1]; 210 assert_true(declarationsRule instanceof CSSFunctionDeclarations); 211 let descriptors = declarationsRule.style; 212 assert_true(descriptors instanceof CSSFunctionDescriptors); 213 assert_equals(descriptors.result, '200px'); 214 }, '@supports in body'); 215 216 // CSSFunctionRule 217 218 test(t => { 219 let sheet = new CSSStyleSheet(); 220 sheet.replaceSync(` 221 @function --foo() {} 222 `); 223 assert_equals(sheet.cssRules.length, 1); 224 let functionRule = sheet.cssRules[0]; 225 assert_true(functionRule instanceof CSSFunctionRule); 226 assert_equals(functionRule.name, '--foo'); 227 }, 'CSSFunctionRule.name'); 228 229 test(t => { 230 let sheet = new CSSStyleSheet(); 231 sheet.replaceSync(` 232 @function --f0() {} 233 @function --f1(--x) {} 234 @function --f2(--x <length>) {} 235 @function --f3(--x: 10px) {} 236 @function --f4(--x <length>: 10px) {} 237 @function --f5(--x type(<length> | auto): 10px, --y, --z: red) {} 238 `); 239 assert_equals(sheet.cssRules.length, 6); 240 assert_object_equals(sheet.cssRules[0].getParameters(), []); 241 assert_object_equals(sheet.cssRules[1].getParameters(), [ 242 {name: '--x', type: '*'}, 243 ]); 244 assert_object_equals(sheet.cssRules[2].getParameters(), [ 245 {name: '--x', type: '<length>'}, 246 ]); 247 assert_object_equals(sheet.cssRules[3].getParameters(), [ 248 {name: '--x', type: '*', defaultValue: '10px'}, 249 ]); 250 assert_object_equals(sheet.cssRules[4].getParameters(), [ 251 {name: '--x', type: '<length>', defaultValue: '10px'}, 252 ]); 253 assert_object_equals(sheet.cssRules[5].getParameters(), [ 254 {name: '--x', type: '<length> | auto', defaultValue: '10px'}, 255 {name: '--y', type: '*'}, 256 {name: '--z', type: '*', defaultValue: 'red'}, 257 ], '--f5'); 258 }, 'CSSFunctionRule.getParameters()'); 259 260 test(t => { 261 let sheet = new CSSStyleSheet(); 262 sheet.replaceSync(` 263 @function --f0() {} 264 @function --f1() returns <length> {} 265 @function --f2() returns <length>+ {} 266 @function --f3() returns type(*) {} 267 @function --f4() returns type(<length> | auto) {} 268 `); 269 assert_equals(sheet.cssRules.length, 5); 270 assert_equals(sheet.cssRules[0]?.returnType, '*'); 271 assert_equals(sheet.cssRules[1]?.returnType, '<length>'); 272 assert_equals(sheet.cssRules[2]?.returnType, '<length>+'); 273 assert_equals(sheet.cssRules[3]?.returnType, '*'); 274 assert_equals(sheet.cssRules[4]?.returnType, '<length> | auto'); 275 }, 'CSSFunctionRule.returnType'); 276 277 test(t => { 278 let sheet = new CSSStyleSheet(); 279 // U+0009 CHARACTER TABULATION 280 sheet.replaceSync(`@function --escaped-\\9 -tab(--param-\\9 -tab) { --local-\\9 -tab: 1px; }`); 281 assert_equals(sheet.cssRules.length, 1); 282 let rule = sheet.cssRules[0]; 283 assert_equals(rule.name, '--escaped-\t-tab'); 284 assert_equals(rule.getParameters()[0]?.name, '--param-\t-tab'); 285 assert_equals(rule.cssRules[0].style.getPropertyValue('--local-\t-tab'), '1px'); 286 }, 'CSSFunctionRule escapes'); 287 288 function test_cssText(actual, expected) { 289 expected ??= actual; 290 let name = actual.match(/@function (--[-\w]+)/)[1]; 291 test(t => { 292 let sheet = new CSSStyleSheet(); 293 sheet.replaceSync(actual); 294 assert_equals(sheet.cssRules[0]?.cssText, expected); 295 }, `CSSFunctionRule.cssText (${name})`); 296 } 297 298 // The function name is used by the test description; please make sure it's 299 // unique among all test_cssText cases. 300 test_cssText(`@function --empty() { }`); 301 302 test_cssText(`@function --ret-length() returns <length> { }`); 303 test_cssText(`@function --ret-length-auto() returns type(<length> | auto) { }`); 304 305 test_cssText(`@function --param-single(--x) { }`); 306 test_cssText(`@function --param-typed(--x <length>) { }`); 307 test_cssText(`@function --param-typed-default(--x <length>: 10px) { }`); 308 test_cssText(`@function --param-default(--x: 10px) { }`); 309 test_cssText(`@function --param-multi(--x, --y) { }`); 310 test_cssText(`@function --param-multi-mixed(--x: 10px, --y, --z <length>) { }`); 311 312 test_cssText(`@function --body-result() { result: 10px; }`); 313 test_cssText(`@function --body-locals() { --x: 1px; --y: 2px; result: 10px; }`); 314 315 // Cases that don't round-trip as-is: 316 test_cssText(`@function --param-type-fn(--x type(<length>)) { }`, 317 `@function --param-type-fn(--x <length>) { }`); 318 test_cssText(`@function --param-type-fn-uni(--x type(*)) { }`, 319 `@function --param-type-fn-uni(--x) { }`); 320 test_cssText(`@function --ret-type-fn() returns type(*) { }`, 321 `@function --ret-type-fn() { }`); 322 test_cssText(`@function --ret-type-fn-uni() returns type(*) { }`, 323 `@function --ret-type-fn-uni() { }`); 324 test_cssText(`@function --body-result-multi() { result: 10px; result: 20px; }`, 325 `@function --body-result-multi() { result: 20px; }`); 326 327 // Escapes (U+0009 CHARACTER TABULATION): 328 test_cssText(`@function --escaped-\\9 -tab(--param-\\9 -tab) { --local-\\9 -tab: 1px; }`); 329 </script>