variable-cycles.html (10409B)
1 <!DOCTYPE html> 2 <meta charset="utf8"> 3 <title>Test that custom property cycles behave correctly</title> 4 <link rel="help" href="https://drafts.csswg.org/css-variables/#cycles"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <main id=main></main> 8 <script> 9 10 // Test that, for the given list of |declarations|, the computed values 11 // of properties listed in |expected_invalid| are invalid (i.e. empty string), 12 // and the computed values listed in |expected_valid| are *not* invalid 13 // (i.e. not the empty string). 14 function test_cycles(declarations, expected_invalid, expected_valid, description) { 15 test(() => { 16 let element = document.createElement('div'); 17 18 try { 19 declarations.push('--sanity:valid'); 20 element.style = declarations.join(';'); 21 main.append(element); 22 let cs = getComputedStyle(element); 23 24 for (let e of expected_invalid) 25 assert_equals(cs.getPropertyValue(e), '', `${e}`); 26 for (let e of expected_valid) 27 assert_not_equals(cs.getPropertyValue(e), '', `${e}`); 28 29 assert_equals(cs.getPropertyValue('--sanity'), 'valid', '--sanity'); 30 31 } finally { 32 element.remove(); 33 } 34 }, description); 35 } 36 37 // (Diagrams produced with graph-easy). 38 39 // ┌───┐ 40 // │ │ ───┐ 41 // │ a │ │ 42 // │ │ ◀──┘ 43 // └───┘ 44 test_cycles( 45 ['--a:var(--a)'], 46 ['--a'], 47 [], 48 'Self-cycle'); 49 50 51 // ┌───┐ 52 // │ a │ ◀┐ 53 // └───┘ │ 54 // │ │ 55 // │ │ 56 // ▼ │ 57 // ┌───┐ │ 58 // │ b │ ─┘ 59 // └───┘ 60 test_cycles( 61 [ 62 '--a:var(--b)', 63 '--b:var(--a)', 64 ], 65 ['--a', '--b'], 66 [], 67 'Simple a/b cycle'); 68 69 70 // ┌───┐ 71 // │ a │ ◀┐ 72 // └───┘ │ 73 // │ │ 74 // │ │ 75 // ▼ │ 76 // ┌───┐ │ 77 // │ b │ │ 78 // └───┘ │ 79 // │ │ 80 // │ │ 81 // ▼ │ 82 // ┌───┐ │ 83 // │ c │ ─┘ 84 // └───┘ 85 test_cycles( 86 [ 87 '--a:var(--b, cycle)', 88 '--b:var(--c, cycle)', 89 '--c:var(--a, cycle)', 90 ], 91 ['--a', '--b', '--c'], 92 [], 93 'Three-var cycle'); 94 95 96 // ┌───┐ 97 // │ x │ 98 // └───┘ 99 // │ 100 // │ 101 // ▼ 102 // ┌───┐ 103 // │ y │ 104 // └───┘ 105 // │ 106 // │ 107 // ▼ 108 // ┌───┐ 109 // │ a │ ◀┐ 110 // └───┘ │ 111 // │ │ 112 // │ │ 113 // ▼ │ 114 // ┌───┐ │ 115 // │ b │ │ 116 // └───┘ │ 117 // │ │ 118 // │ │ 119 // ▼ │ 120 // ┌───┐ │ 121 // │ c │ ─┘ 122 // └───┘ 123 test_cycles( 124 [ 125 '--x:var(--y, valid)', 126 '--y:var(--a, valid)', 127 '--a:var(--b, cycle)', 128 '--b:var(--c, cycle)', 129 '--c:var(--a, cycle)', 130 ], 131 ['--a', '--b', '--c'], 132 ['--x', '--y'], 133 'Cycle that starts in the middle of a chain'); 134 135 136 // ┌───┐ 137 // │ x │ 138 // └───┘ 139 // │ 140 // │ 141 // ▼ 142 // ┌───┐ 143 // │ a │ ◀┐ 144 // └───┘ │ 145 // │ │ 146 // │ │ 147 // ▼ │ 148 // ┌───┐ │ 149 // │ b │ │ 150 // └───┘ │ 151 // │ │ 152 // │ │ 153 // ▼ │ 154 // ┌───┐ │ 155 // │ c │ ─┘ 156 // └───┘ 157 // │ 158 // │ 159 // ▼ 160 // ┌───┐ 161 // │ y │ 162 // └───┘ 163 test_cycles( 164 [ 165 '--x:var(--a, valid)', 166 '--a:var(--b, cycle)', 167 '--b:var(--c, cycle)', 168 '--c:var(--a, cycle) var(--y)', 169 '--y:valid' 170 ], 171 ['--a', '--b', '--c'], 172 ['--x', '--y'], 173 'Cycle with extra edge'); 174 175 176 // ┌───┐ 177 // │ x │ 178 // └───┘ 179 // │ 180 // │ 181 // ▼ 182 // ┌───┐ 183 // │ a │ ◀┐ 184 // └───┘ │ 185 // │ │ 186 // │ │ 187 // ▼ │ 188 // ┌───┐ ┌───┐ │ 189 // │ y │ ◀── │ b │ │ 190 // └───┘ └───┘ │ 191 // │ │ 192 // │ │ 193 // ▼ │ 194 // ┌───┐ │ 195 // │ c │ ─┘ 196 // └───┘ 197 test_cycles( 198 [ 199 '--x:var(--a, valid)', 200 '--a:var(--b, cycle)', 201 '--b:var(--c, cycle) var(--y)', 202 '--c:var(--a, cycle)', 203 '--y:valid' 204 ], 205 ['--a', '--b', '--c'], 206 ['--x', '--y'], 207 'Cycle with extra edge (2)'); 208 209 210 // ┌───┐ 211 // │ x │ 212 // └───┘ 213 // │ 214 // │ 215 // ▼ 216 // ┌───┐ 217 // │ a │ ◀┐ 218 // └───┘ │ 219 // │ │ 220 // │ │ 221 // ▼ │ 222 // ┌───┐ │ 223 // │ b │ │ 224 // └───┘ │ 225 // │ │ 226 // │ │ 227 // ▼ │ 228 // ┌───┐ │ 229 // │ c │ ─┘ 230 // └───┘ 231 // │ 232 // │ 233 // ▼ 234 // ┌───┐ 235 // │ y │ 236 // └───┘ 237 // │ 238 // │ 239 // ▼ 240 // ┌───┐ 241 // │ z │ 242 // └───┘ 243 test_cycles( 244 [ 245 '--x:var(--a, valid)', 246 '--a:var(--b, cycle)', 247 '--b:var(--c, cycle)', 248 '--c:var(--a, cycle) var(--y)', 249 '--y:var(--z)', 250 '--z:valid' 251 ], 252 ['--a', '--b', '--c'], 253 ['--x', '--y', '--z'], 254 'Cycle with extra edge (3)'); 255 256 // ┌───┐ 257 // │ x │ 258 // └───┘ 259 // │ 260 // │ 261 // ▼ 262 // ┌───┐ 263 // │ a │ ◀┐ 264 // └───┘ │ 265 // │ │ 266 // │ │ 267 // ▼ │ 268 // ┌───┐ │ 269 // ┌▶ │ b │ ─┘ 270 // │ └───┘ 271 // │ │ 272 // │ │ 273 // │ ▼ 274 // │ ┌───┐ 275 // │ │ c │ 276 // │ └───┘ 277 // │ │ 278 // │ │ 279 // │ ▼ 280 // │ ┌───┐ 281 // └─ │ d │ 282 // └───┘ 283 test_cycles( 284 [ 285 '--x:var(--a, valid)', 286 '--a:var(--b, cycle)', 287 '--b:var(--c, cycle) var(--a, cycle)', 288 '--c:var(--d, cycle)', 289 '--d:var(--b, cycle)', 290 ], 291 ['--a', '--b', '--c', '--d'], 292 ['--x'], 293 'Cycle with secondary cycle'); 294 295 296 // ┌───┐ 297 // │ x │ 298 // └───┘ 299 // │ 300 // │ 301 // ▼ 302 // ┌───┐ 303 // │ a │ ◀┐ 304 // └───┘ │ 305 // │ │ 306 // │ │ 307 // ▼ │ 308 // ┌───┐ │ 309 // ┌▶ │ b │ │ 310 // │ └───┘ │ 311 // │ │ │ 312 // │ │ │ 313 // │ ▼ │ 314 // │ ┌───┐ │ 315 // │ │ c │ ─┘ 316 // │ └───┘ 317 // │ │ 318 // │ │ 319 // │ ▼ 320 // │ ┌───┐ 321 // └─ │ d │ 322 // └───┘ 323 // │ 324 // │ 325 // ▼ 326 // ┌───┐ 327 // │ y │ 328 // └───┘ 329 test_cycles( 330 [ 331 '--x:var(--a, valid)', 332 '--a:var(--b, cycle)', 333 '--b:var(--c, cycle)', 334 '--c:var(--d, cycle) var(--a, cycle)', 335 '--d:var(--b, cycle) var(--y)', 336 '--y:valid' 337 ], 338 ['--a', '--b', '--c', '--d'], 339 ['--x', '--y'], 340 'Cycle with overlapping secondary cycle'); 341 342 343 // ┌──────────────┐ 344 // │ │ 345 // │ ┌───┐ │ 346 // │ │ x │ │ 347 // │ └───┘ │ 348 // │ │ │ 349 // │ │ │ 350 // │ ▼ ▼ 351 // ┌───┐ ┌────────┐ ┌───┐ 352 // │ b │ ◀── │ a │ ──▶ │ y │ 353 // └───┘ └────────┘ └───┘ 354 // │ ▲ 355 // │ │ 356 // ▼ │ 357 // ┌───┐ │ 358 // │ c │ │ 359 // └───┘ │ 360 // │ │ 361 // │ │ 362 // ▼ │ 363 // ┌───┐ │ 364 // │ d │ ─┘ 365 // └───┘ 366 test_cycles( 367 [ 368 '--x:var(--a, valid)', 369 '--a:var(--b, cycle) var(--y, valid) var(--c, cycle)', 370 '--b:var(--a, cycle) ', 371 '--c:var(--d, cycle)', 372 '--d:var(--a, cycle)', 373 '--y:valid', 374 ], 375 ['--a', '--b', '--c', '--d'], 376 ['--x', '--y'], 377 'Cycle with deeper secondary cycle'); 378 379 380 // If we cared about cycles in unused fallbacks, 381 // then --a/--b/--c would be in a cycle in this case: 382 // 383 // ┌───┐ 384 // │ x │ 385 // └───┘ 386 // │ 387 // │ 388 // ▼ 389 // ┌───┐ 390 // ┌─────▶ │ a │ ─┐ 391 // │ └───┘ │ 392 // │ │ │ 393 // │ │ │ 394 // │ ▼ │ 395 // │ ┌───┐ │ 396 // │ ┌─ │ b │ │ 397 // │ │ └───┘ │ 398 // │ │ │ │ 399 // │ │ │ │ 400 // │ │ ▼ │ 401 // │ │ ┌───┐ │ 402 // └────┼─ │ c │ │ 403 // │ └───┘ │ 404 // │ │ │ 405 // │ │ │ 406 // │ ▼ │ 407 // │ ┌───┐ │ 408 // └▶ │ y │ ◀┘ 409 // └───┘ 410 // 411 // However, as of https://github.com/w3c/csswg-drafts/issues/11500, 412 // we no longer care about such cycles. 413 test_cycles( 414 [ 415 '--x:var(--a, valid)', 416 '--a:var(--y, var(--b, cycle))', 417 '--b:var(--y, var(--c, cycle))', 418 '--c:var(--y, var(--a, cycle))', 419 '--y:valid' 420 ], 421 [], // Nothing is invalid. 422 ['--a', '--b', '--c', '--x', '--y'], 423 'Cycle in unused fallback'); 424 425 </script>