style: Handle nested slots correctly in slotted matching and invalidation.

The patch and test should be pretty much self-descriptive.

Differential Revision: https://phabricator.services.mozilla.com/D14063
This commit is contained in:
Emilio Cobos Álvarez 2018-12-13 02:17:53 +00:00
parent a519d9ecc3
commit 7f5a9e0f45
3 changed files with 45 additions and 17 deletions

View file

@ -410,6 +410,7 @@ fn next_element_for_combinator<E>(
element: &E, element: &E,
combinator: Combinator, combinator: Combinator,
selector: &SelectorIter<E::Impl>, selector: &SelectorIter<E::Impl>,
context: &MatchingContext<E::Impl>,
) -> Option<E> ) -> Option<E>
where where
E: Element, E: Element,
@ -449,12 +450,21 @@ where
element.containing_shadow_host() element.containing_shadow_host()
}, },
Combinator::SlotAssignment => { Combinator::SlotAssignment => {
debug_assert!(
context.current_host.is_some(),
"Should not be trying to match slotted rules in a non-shadow-tree context"
);
debug_assert!( debug_assert!(
element element
.assigned_slot() .assigned_slot()
.map_or(true, |s| s.is_html_slot_element()) .map_or(true, |s| s.is_html_slot_element())
); );
element.assigned_slot() let scope = context.current_host?;
let mut current_slot = element.assigned_slot()?;
while current_slot.containing_shadow_host().unwrap().opaque() != scope {
current_slot = current_slot.assigned_slot()?;
}
Some(current_slot)
}, },
Combinator::PseudoElement => element.pseudo_element_originating_element(), Combinator::PseudoElement => element.pseudo_element_originating_element(),
} }
@ -511,7 +521,12 @@ where
Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally, Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally,
}; };
let mut next_element = next_element_for_combinator(element, combinator, &selector_iter); let mut next_element = next_element_for_combinator(
element,
combinator,
&selector_iter,
&context,
);
// Stop matching :visited as soon as we find a link, or a combinator for // Stop matching :visited as soon as we find a link, or a combinator for
// something that isn't an ancestor. // something that isn't an ancestor.
@ -575,7 +590,12 @@ where
visited_handling = VisitedHandlingMode::AllLinksUnvisited; visited_handling = VisitedHandlingMode::AllLinksUnvisited;
} }
next_element = next_element_for_combinator(&element, combinator, &selector_iter); next_element = next_element_for_combinator(
&element,
combinator,
&selector_iter,
&context,
);
} }
} }

View file

@ -11,9 +11,8 @@ use crate::parser::SelectorImpl;
use std::fmt::Debug; use std::fmt::Debug;
use std::ptr::NonNull; use std::ptr::NonNull;
/// Opaque representation of an Element, for identity comparisons. We use /// Opaque representation of an Element, for identity comparisons.
/// NonZeroPtrMut to get the NonZero optimization. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct OpaqueElement(NonNull<()>); pub struct OpaqueElement(NonNull<()>);
unsafe impl Send for OpaqueElement {} unsafe impl Send for OpaqueElement {}

View file

@ -471,25 +471,34 @@ where
return false; return false;
} }
let slot = self.element;
self.invalidate_slotted_elements_in_slot(slot, invalidations)
}
fn invalidate_slotted_elements_in_slot(
&mut self,
slot: E,
invalidations: &[Invalidation<'b>],
) -> bool {
let mut any = false; let mut any = false;
let mut sibling_invalidations = InvalidationVector::new(); let mut sibling_invalidations = InvalidationVector::new();
let element = self.element; for node in slot.slotted_nodes() {
for node in element.slotted_nodes() {
let element = match node.as_element() { let element = match node.as_element() {
Some(e) => e, Some(e) => e,
None => continue, None => continue,
}; };
any |= self.invalidate_child( if element.is_html_slot_element() {
element, any |= self.invalidate_slotted_elements_in_slot(element, invalidations);
invalidations, } else {
&mut sibling_invalidations, any |= self.invalidate_child(
DescendantInvalidationKind::Slotted, element,
); invalidations,
&mut sibling_invalidations,
// FIXME(emilio): Need to handle nested slotted nodes if `element` DescendantInvalidationKind::Slotted,
// is itself a <slot>. );
}
debug_assert!( debug_assert!(
sibling_invalidations.is_empty(), sibling_invalidations.is_empty(),