| <!DOCTYPE html> |
| <meta charset="utf8"> |
| <title>Test that custom property cycles behave correctly</title> |
| <link rel="help" href="https://drafts.csswg.org/css-variables/#cycles"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <main id=main></main> |
| <script> |
| |
| // Test that, for the given list of |declarations|, the computed values |
| // of properties listed in |expected_invalid| are invalid (i.e. empty string), |
| // and the computed values listed in |expected_valid| are *not* invalid |
| // (i.e. not the empty string). |
| function test_cycles(declarations, expected_invalid, expected_valid, description) { |
| test(() => { |
| let element = document.createElement('div'); |
| |
| try { |
| declarations.push('--sanity:valid'); |
| element.style = declarations.join(';'); |
| main.append(element); |
| let cs = getComputedStyle(element); |
| |
| for (let e of expected_invalid) |
| assert_equals(cs.getPropertyValue(e), '', `${e}`); |
| for (let e of expected_valid) |
| assert_not_equals(cs.getPropertyValue(e), '', `${e}`); |
| |
| assert_equals(cs.getPropertyValue('--sanity'), 'valid', '--sanity'); |
| |
| } finally { |
| element.remove(); |
| } |
| }, description); |
| } |
| |
| // (Diagrams produced with graph-easy). |
| |
| // ┌───┐ |
| // │ │ ───┐ |
| // │ a │ │ |
| // │ │ ◀──┘ |
| // └───┘ |
| test_cycles( |
| ['--a:var(--a)'], |
| ['--a'], |
| [], |
| 'Self-cycle'); |
| |
| |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ b │ ─┘ |
| // └───┘ |
| test_cycles( |
| [ |
| '--a:var(--b)', |
| '--b:var(--a)', |
| ], |
| ['--a', '--b'], |
| [], |
| 'Simple a/b cycle'); |
| |
| |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ b │ │ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ ─┘ |
| // └───┘ |
| test_cycles( |
| [ |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle)', |
| '--c:var(--a, cycle)', |
| ], |
| ['--a', '--b', '--c'], |
| [], |
| 'Three-var cycle'); |
| |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ y │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ b │ │ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ ─┘ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--y, valid)', |
| '--y:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle)', |
| '--c:var(--a, cycle)', |
| ], |
| ['--a', '--b', '--c'], |
| ['--x', '--y'], |
| 'Cycle that starts in the middle of a chain'); |
| |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ b │ │ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ ─┘ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ y │ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle)', |
| '--c:var(--a, cycle) var(--y)', |
| '--y:valid' |
| ], |
| ['--a', '--b', '--c'], |
| ['--x', '--y'], |
| 'Cycle with extra edge'); |
| |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ ┌───┐ │ |
| // │ y │ ◀── │ b │ │ |
| // └───┘ └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ ─┘ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle) var(--y)', |
| '--c:var(--a, cycle)', |
| '--y:valid' |
| ], |
| ['--a', '--b', '--c'], |
| ['--x', '--y'], |
| 'Cycle with extra edge (2)'); |
| |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ b │ │ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ ─┘ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ y │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ z │ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle)', |
| '--c:var(--a, cycle) var(--y)', |
| '--y:var(--z)', |
| '--z:valid' |
| ], |
| ['--a', '--b', '--c'], |
| ['--x', '--y', '--z'], |
| 'Cycle with extra edge (3)'); |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // ┌▶ │ b │ ─┘ |
| // │ └───┘ |
| // │ │ |
| // │ │ |
| // │ ▼ |
| // │ ┌───┐ |
| // │ │ c │ |
| // │ └───┘ |
| // │ │ |
| // │ │ |
| // │ ▼ |
| // │ ┌───┐ |
| // └─ │ d │ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle) var(--a, cycle)', |
| '--c:var(--d, cycle)', |
| '--d:var(--b, cycle)', |
| ], |
| ['--a', '--b', '--c', '--d'], |
| ['--x'], |
| 'Cycle with secondary cycle'); |
| |
| |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ a │ ◀┐ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // ┌▶ │ b │ │ |
| // │ └───┘ │ |
| // │ │ │ |
| // │ │ │ |
| // │ ▼ │ |
| // │ ┌───┐ │ |
| // │ │ c │ ─┘ |
| // │ └───┘ |
| // │ │ |
| // │ │ |
| // │ ▼ |
| // │ ┌───┐ |
| // └─ │ d │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // │ y │ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle)', |
| '--b:var(--c, cycle)', |
| '--c:var(--d, cycle) var(--a, cycle)', |
| '--d:var(--b, cycle) var(--y)', |
| '--y:valid' |
| ], |
| ['--a', '--b', '--c', '--d'], |
| ['--x', '--y'], |
| 'Cycle with overlapping secondary cycle'); |
| |
| |
| // ┌──────────────┐ |
| // │ │ |
| // │ ┌───┐ │ |
| // │ │ x │ │ |
| // │ └───┘ │ |
| // │ │ │ |
| // │ │ │ |
| // │ ▼ ▼ |
| // ┌───┐ ┌────────┐ ┌───┐ |
| // │ b │ ◀── │ a │ ──▶ │ y │ |
| // └───┘ └────────┘ └───┘ |
| // │ ▲ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ c │ │ |
| // └───┘ │ |
| // │ │ |
| // │ │ |
| // ▼ │ |
| // ┌───┐ │ |
| // │ d │ ─┘ |
| // └───┘ |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--b, cycle) var(--y, valid) var(--c, cycle)', |
| '--b:var(--a, cycle) ', |
| '--c:var(--d, cycle)', |
| '--d:var(--a, cycle)', |
| '--y:valid', |
| ], |
| ['--a', '--b', '--c', '--d'], |
| ['--x', '--y'], |
| 'Cycle with deeper secondary cycle'); |
| |
| |
| // If we cared about cycles in unused fallbacks, |
| // then --a/--b/--c would be in a cycle in this case: |
| // |
| // ┌───┐ |
| // │ x │ |
| // └───┘ |
| // │ |
| // │ |
| // ▼ |
| // ┌───┐ |
| // ┌─────▶ │ a │ ─┐ |
| // │ └───┘ │ |
| // │ │ │ |
| // │ │ │ |
| // │ ▼ │ |
| // │ ┌───┐ │ |
| // │ ┌─ │ b │ │ |
| // │ │ └───┘ │ |
| // │ │ │ │ |
| // │ │ │ │ |
| // │ │ ▼ │ |
| // │ │ ┌───┐ │ |
| // └────┼─ │ c │ │ |
| // │ └───┘ │ |
| // │ │ │ |
| // │ │ │ |
| // │ ▼ │ |
| // │ ┌───┐ │ |
| // └▶ │ y │ ◀┘ |
| // └───┘ |
| // |
| // However, as of https://github.com/w3c/csswg-drafts/issues/11500, |
| // we no longer care about such cycles. |
| test_cycles( |
| [ |
| '--x:var(--a, valid)', |
| '--a:var(--y, var(--b, cycle))', |
| '--b:var(--y, var(--c, cycle))', |
| '--c:var(--y, var(--a, cycle))', |
| '--y:valid' |
| ], |
| [], // Nothing is invalid. |
| ['--a', '--b', '--c', '--x', '--y'], |
| 'Cycle in unused fallback'); |
| |
| </script> |