Skip to content

Commit fc0d8fb

Browse files
committed
Detect complex custom property cycles involving multiple loops
https://bugs.webkit.org/show_bug.cgi?id=251269 <rdar://problem/104744972> Reviewed by Simon Fraser. We fail in some complex cycle cases. * LayoutTests/imported/w3c/web-platform-tests/css/css-variables/variable-cycles-expected.txt: * Source/WebCore/style/StyleBuilder.cpp: (WebCore::Style::Builder::applyCustomProperty): Track property cycles with a separate m_inCycleCustomProperties map instead of eagerly computing them to an invalid value on first detection. This allows the detection of multiple cycles passing through the same property. It also simplifies the code. * Source/WebCore/style/StyleBuilderState.h: Canonical link: https://commits.webkit.org/259506@main
1 parent 7535fd2 commit fc0d8fb

File tree

3 files changed

+21
-28
lines changed

3 files changed

+21
-28
lines changed

LayoutTests/imported/w3c/web-platform-tests/css/css-variables/variable-cycles-expected.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ PASS Cycle that starts in the middle of a chain
66
PASS Cycle with extra edge
77
PASS Cycle with extra edge (2)
88
PASS Cycle with extra edge (3)
9-
FAIL Cycle with secondary cycle assert_equals: --a expected "" but got "cycle"
10-
FAIL Cycle with overlapping secondary cycle assert_equals: --a expected "" but got "cycle"
11-
FAIL Cycle with deeper secondary cycle assert_equals: --c expected "" but got "cycle"
9+
PASS Cycle with secondary cycle
10+
PASS Cycle with overlapping secondary cycle
11+
PASS Cycle with deeper secondary cycle
1212
PASS Cycle via fallback
1313

Source/WebCore/style/StyleBuilder.cpp

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,20 @@ void Builder::applyCustomProperty(const AtomString& name)
174174
if (!property.cssValue[SelectorChecker::MatchDefault])
175175
return;
176176

177-
SetForScope levelScope(m_state.m_currentProperty, &property);
178-
179177
Ref customPropertyValue = downcast<CSSCustomPropertyValue>(*property.cssValue[SelectorChecker::MatchDefault]);
180178

181179
bool inCycle = !m_state.m_inProgressCustomProperties.add(name).isNewEntry;
180+
if (inCycle) {
181+
auto isNewCycle = m_state.m_inCycleCustomProperties.add(name).isNewEntry;
182+
if (isNewCycle) {
183+
// Continue resolving dependencies so we detect cycles for them as well.
184+
resolveCustomPropertyValueWithVariableReferences(customPropertyValue.get());
185+
}
186+
return;
187+
}
188+
189+
// There may be multiple cycles through the same property. Avoid interference from any previously detected cycles.
190+
auto savedInCycleProperties = std::exchange(m_state.m_inCycleCustomProperties, { });
182191

183192
auto createInvalidOrUnset = [&] {
184193
// https://drafts.csswg.org/css-variables-2/#invalid-variables
@@ -193,35 +202,18 @@ void Builder::applyCustomProperty(const AtomString& name)
193202
return CSSCustomPropertyValue::createWithID(name, CSSValueUnset);
194203
};
195204

196-
auto valueToApply = [&]() -> RefPtr<CSSCustomPropertyValue> {
197-
if (inCycle)
198-
return createInvalidOrUnset();
199-
200-
auto resolvedValue = resolveCustomPropertyValueWithVariableReferences(customPropertyValue.get());
201-
if (m_state.m_appliedCustomProperties.contains(name))
202-
return nullptr; // There was a cycle and the value was already resolved, so bail.
203-
204-
if (!resolvedValue)
205-
return createInvalidOrUnset();
205+
auto resolvedValue = resolveCustomPropertyValueWithVariableReferences(customPropertyValue.get());
206206

207-
return resolvedValue;
208-
}();
209-
210-
if (!valueToApply) {
211-
ASSERT(!m_state.m_inProgressCustomProperties.contains(name));
212-
return;
213-
}
207+
if (!resolvedValue || m_state.m_inCycleCustomProperties.contains(name))
208+
resolvedValue = createInvalidOrUnset();
214209

210+
SetForScope levelScope(m_state.m_currentProperty, &property);
215211
SetForScope scopedLinkMatchMutation(m_state.m_linkMatch, SelectorChecker::MatchDefault);
216-
applyProperty(CSSPropertyCustom, *valueToApply, SelectorChecker::MatchDefault);
212+
applyProperty(CSSPropertyCustom, *resolvedValue, SelectorChecker::MatchDefault);
217213

218214
m_state.m_inProgressCustomProperties.remove(name);
219215
m_state.m_appliedCustomProperties.add(name);
220-
221-
if (inCycle) {
222-
// Resolve this value so that we reset its dependencies.
223-
resolveCustomPropertyValueWithVariableReferences(customPropertyValue.get());
224-
}
216+
m_state.m_inCycleCustomProperties.formUnion(WTFMove(savedInCycleProperties));
225217
}
226218

227219
inline void Builder::applyCascadeProperty(const PropertyCascade::Property& property)

Source/WebCore/style/StyleBuilderState.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class BuilderState {
134134

135135
HashSet<String> m_appliedCustomProperties;
136136
HashSet<String> m_inProgressCustomProperties;
137+
HashSet<String> m_inCycleCustomProperties;
137138
Bitmap<numCSSProperties> m_inProgressProperties;
138139
Bitmap<numCSSProperties> m_inUnitCycleProperties;
139140

0 commit comments

Comments
 (0)