style: Correct style sharing handling for any element that considered :has() in selector matching

For any element that anchors a `:has()` selector (i.e. Matches a selector that
contains a `:has()` on its rightmost side), we prevent style sharing altogether,
as evaluation of the `:has()` selector is required in the first place to
determine style sharing.

On the other hand, any element matching a rule containing `:has()` without
anchoring it can do style sharing for siblings, but not cousins.

Differential Revision: https://phabricator.services.mozilla.com/D176836
This commit is contained in:
David Shin 2023-05-25 14:35:18 +00:00 committed by Martin Robinson
parent 5c0897c8eb
commit ff8100d396
9 changed files with 91 additions and 6 deletions

View file

@ -146,6 +146,7 @@ where
/// The current element we're anchoring on for evaluating the relative selector.
current_relative_selector_anchor: Option<OpaqueElement>,
pub considered_relative_selector: bool,
quirks_mode: QuirksMode,
needs_selector_flags: NeedsSelectorFlags,
@ -199,6 +200,7 @@ where
pseudo_element_matching_fn: None,
extra_data: Default::default(),
current_relative_selector_anchor: None,
considered_relative_selector: false,
_impl: ::std::marker::PhantomData,
}
}
@ -332,6 +334,7 @@ where
self.current_relative_selector_anchor = Some(anchor);
let result = self.nest(f);
self.current_relative_selector_anchor = original_relative_selector_anchor;
self.considered_relative_selector = true;
result
}

View file

@ -60,13 +60,18 @@ bitflags! {
/// The element has an empty selector, so when a child is appended we
/// might need to restyle the parent completely.
const HAS_EMPTY_SELECTOR = 1 << 4;
/// This element has a relative selector that anchors to it, or may do so
/// if its descendants or its later siblings change.
const ANCHORS_RELATIVE_SELECTOR = 1 << 5;
}
}
impl ElementSelectorFlags {
/// Returns the subset of flags that apply to the element.
pub fn for_self(self) -> ElementSelectorFlags {
self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR)
self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR |
ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR)
}
/// Returns the subset of flags that apply to the parent.
@ -365,6 +370,12 @@ fn matches_relative_selectors<E: Element>(
element: &E,
context: &mut MatchingContext<E::Impl>,
) -> bool {
if context.needs_selector_flags() {
// If we've considered anchoring `:has()` selector while trying to match this element,
// mark it as such, as it has implications on style sharing (See style sharing
// code for further information).
element.apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR);
}
for RelativeSelector {
match_hint,
selector,