← All issues

CSS revert-rule keyword implementation

cdf8247

Source/WebCore/style/PropertyCascade.cpp

+PropertyCascade::PropertyCascade(const PropertyCascade& parent, RevertRuleTag)
+ : m_matchResult(parent.m_matchResult)
+ , m_includedProperties(normalProperties())
+ , m_maximumOrigin(parent.m_maximumOrigin)
+ , m_rollbackScope(parent.m_rollbackScope)
+ , m_maximumCascadeLayerPriorityForRollback(parent.m_maximumCascadeLayerPriorityForRollback)
+ , m_ruleRollbackDepth(parent.m_ruleRollbackDepth + 1)
+ , m_animationLayer(parent.m_animationLayer)
+ , m_positionTryFallbackProperties(parent.m_positionTryFallbackProperties)
+{
+ buildCascade();
+}
+void PropertyCascade::setDelayingForRuleRollback(CSSPropertyID propertyID, CSSValue& cssValue, const MatchedProperties& matchedProperties, Origin origin)
+{
+ ASSERT(m_ruleRollbackDepth);
+ auto key = [&] -> std::pair<unsigned, AtomString> {
+ if (propertyID == CSSPropertyCustom)
+ return { propertyID, downcast<CSSCustomPropertyValue>(cssValue).name() };
+ return { propertyID, emptyAtom() };
+ }();
+ auto& delayedValues = m_delayedRollbackProperties.ensure(key, [&] {
+ return Deque<DelayedRollbackProperty>();
+ }).iterator->value;
+ delayedValues.prepend(DelayedRollbackProperty { cssValue, matchedProperties, origin });

The CSS cascade algorithm determines which rule's value wins for a given property. WebKit implements this in PropertyCascade, which collects matched rules and resolves winning values. The existing revert keyword resets to the user-agent origin; revert-layer resets to the previous cascade layer. The new revert-rule keyword (CSS Cascading Level 5) is rule-granular: it skips the current rule's contribution for a property and acts as if that rule didn't exist, falling through to whatever the next-lower rule in cascade order produced. Implementing this requires building a shadow "rollback cascade" that represents the cascade state without the current rule, then substituting that cascade's value at apply time.

Cascade order (author origin, same layer):
  Rule A:  color: red;
  Rule B:  color: blue;          ← value that revert-rule resolves to
  Rule C:  color: revert-rule;   ← skip Rule C's contribution

StyleBuilder resolution:
  applyProperty(color)
     └─ value == revert-rule?
           └─ ensureRollbackCascadeForRevertRule()
                 └─ PropertyCascade(parent, RevertRuleTag{})
                       m_ruleRollbackDepth = parent.m_ruleRollbackDepth + 1
                       └─ addMatch: defers values into per-property Deque
                             until rollback depth is satisfied
                 └─ apply color from rollback cascade → "blue"

The implementation adds a new PropertyCascade constructor that increments m_ruleRollbackDepth, a setDelayingForRuleRollback method that defers values into per-property Deques keyed by (propertyID, customPropertyName), and ensureRollbackCascadeForRevertRule in StyleBuilder that constructs the rollback cascade on demand.

New CSS keyword means new parsing surface, new cascade resolution code paths, and new interactions with custom properties, !important, CSS nesting, keyframe animations, and container queries — all under-tested territory for a feature just passing its first WPT suite.

🔒

New cascade resolution code paths and edge cases in rollback depth tracking, custom property deferral, and cache key correctness are worth security investigation.

Subscribe to read more