diff --git a/servo/components/style/stylist.rs b/servo/components/style/stylist.rs index 0ba532ea0b45..d4c6a0cc2ca4 100644 --- a/servo/components/style/stylist.rs +++ b/servo/components/style/stylist.rs @@ -596,6 +596,43 @@ impl From for RuleInclusion { } } +/// `:scope` selector, depending on the use case, can match a shadow host. +/// If used outside of `@scope`, it cannot possibly match the host. +/// Even when inside of `@scope`, it's conditional if the selector will +/// match the shadow host. +#[derive(Clone, Copy, Eq, PartialEq)] +enum ScopeMatchesShadowHost { + NotApplicable, + No, + Yes, +} + +impl Default for ScopeMatchesShadowHost { + fn default() -> Self { + Self::NotApplicable + } +} + +impl ScopeMatchesShadowHost { + fn nest_for_scope(&mut self, matches_shadow_host: bool) { + match *self { + Self::NotApplicable => { + // We're at the outermost `@scope`. + *self = if matches_shadow_host { + Self::Yes + } else { + Self::No + }; + }, + Self::Yes if !matches_shadow_host => { + // Inner `@scope` will not be able to match the shadow host. + *self = Self::No; + }, + _ => (), + } + } +} + /// A struct containing state from ancestor rules like @layer / @import / /// @container / nesting / @scope. struct ContainingRuleState { @@ -604,7 +641,7 @@ struct ContainingRuleState { container_condition_id: ContainerConditionId, in_starting_style: bool, scope_condition_id: ScopeConditionId, - scope_matches_shadow_host: bool, + scope_matches_shadow_host: ScopeMatchesShadowHost, ancestor_selector_lists: SmallVec<[SelectorList; 2]>, } @@ -617,7 +654,7 @@ impl Default for ContainingRuleState { in_starting_style: false, ancestor_selector_lists: Default::default(), scope_condition_id: ScopeConditionId::none(), - scope_matches_shadow_host: true, + scope_matches_shadow_host: Default::default(), } } } @@ -629,7 +666,7 @@ struct SavedContainingRuleState { container_condition_id: ContainerConditionId, in_starting_style: bool, scope_condition_id: ScopeConditionId, - scope_matches_shadow_host: bool, + scope_matches_shadow_host: ScopeMatchesShadowHost, } impl ContainingRuleState { @@ -3204,7 +3241,8 @@ impl CascadeData { } else if potentially_matches_featureless_host .intersects(FeaturelessHostMatches::FOR_SCOPE) { - containing_rule_state.scope_matches_shadow_host + containing_rule_state.scope_matches_shadow_host == + ScopeMatchesShadowHost::Yes } else { false }; @@ -3484,7 +3522,9 @@ impl CascadeData { condition: Some(replaced), implicit_scope_root, }); - containing_rule_state.scope_matches_shadow_host &= matches_shadow_host; + containing_rule_state + .scope_matches_shadow_host + .nest_for_scope(matches_shadow_host); containing_rule_state.scope_condition_id = id; }, // We don't care about any other rule. diff --git a/testing/web-platform/tests/css/css-cascade/scope-featureless-ref.html b/testing/web-platform/tests/css/css-cascade/scope-featureless-ref.html new file mode 100644 index 000000000000..b53c78954465 --- /dev/null +++ b/testing/web-platform/tests/css/css-cascade/scope-featureless-ref.html @@ -0,0 +1,9 @@ + +Reference - :scope Selector Cannot be Featureless Outside of @scope + +
Test
diff --git a/testing/web-platform/tests/css/css-cascade/scope-featureless.html b/testing/web-platform/tests/css/css-cascade/scope-featureless.html new file mode 100644 index 000000000000..ca0b1f021790 --- /dev/null +++ b/testing/web-platform/tests/css/css-cascade/scope-featureless.html @@ -0,0 +1,20 @@ + +:scope Selector Cannot be Featureless Outside of @scope + + + + +
Test