forked from mirrors/gecko-dev
Automatic update from web-platform-tests
Handle secondary edges in custom property cycles
The cycle detection in StyleCascade is not correct. Any secondary edges
coming from a cycle are also detected as in-cycle, even if they aren't.
Explanation of how the new cycle detection works:
- AutoLock is used to "lock" a property that's currently being resolved.
- It's instantiated at the start of ResolveCustomProperty,
ResolveVariableReference, and ResolvePendingSubstitution. No other
places.
- All "lock" means is that the property we're resolving is pushed to
CascadeResolver.stack_.
- When we're trying to resolve a var(), a cycle is detected by looking
for a given property in that stack. If the property is found, the
stack index is set as the cycle_start_, and the current size of the
stack is set as the cycle_end_. The idea is that all the properties
in the range [cycle_start_, cycle_end_) are in a cycle, and are
therefore invalid.
- As we come back up the regular call stack, we check the InCycle flag
to see if a (nested) variable resolution has detected a cycle, and if
we're still inside it.
Where the old code went wrong, was that we assumed that once a cycle was
detected, we would not push anything new onto CascadeResolver.stack_
until we were out of the cycle. Hence, once a cycle was detected,
anything reachable from the start of the cycle would be detected as
in-cycle.
This is regression from the StyleCascade project (since M82), although
nobody reported the issue.
Unfortunately this exposed that two tests relied on the buggy cycle
detection. These tests have been temporarily disabled/rebaselined.
Change-Id: If7bbaad084c001c4f3349a7a4050b0a5bd33139b
Fixed: 1187282
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2749195
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Xiaocheng Hu <xiaochengh@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/master@{#862369}
--
wpt-commits: 678cbc9eb44fcc4b303b076cf758e96295d8fee3
wpt-pr: 28035
419 lines
9.9 KiB
HTML
419 lines
9.9 KiB
HTML
<!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');
|
|
|
|
|
|
// ┌───┐
|
|
// │ x │
|
|
// └───┘
|
|
// │
|
|
// │
|
|
// ▼
|
|
// ┌───┐
|
|
// ┌─────▶ │ a │ ─┐
|
|
// │ └───┘ │
|
|
// │ │ │
|
|
// │ │ │
|
|
// │ ▼ │
|
|
// │ ┌───┐ │
|
|
// │ ┌─ │ b │ │
|
|
// │ │ └───┘ │
|
|
// │ │ │ │
|
|
// │ │ │ │
|
|
// │ │ ▼ │
|
|
// │ │ ┌───┐ │
|
|
// └────┼─ │ c │ │
|
|
// │ └───┘ │
|
|
// │ │ │
|
|
// │ │ │
|
|
// │ ▼ │
|
|
// │ ┌───┐ │
|
|
// └▶ │ y │ ◀┘
|
|
// └───┘
|
|
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'
|
|
],
|
|
['--a', '--b', '--c'],
|
|
['--x', '--y'],
|
|
'Cycle via fallback');
|
|
|
|
</script>
|