diff --git a/components/style/dom.rs b/components/style/dom.rs index ef571b8df0a6..fbe35e2e49d5 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -523,6 +523,9 @@ pub trait TElement: /// Returns whether this element has a `part` attribute. fn has_part_attr(&self) -> bool; + /// Returns whether this element exports any part from its shadow tree. + fn exports_any_part(&self) -> bool; + /// The ID for this element. fn id(&self) -> Option<&WeakAtom>; diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index cffb78d3e9e0..02707682b4d4 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -193,6 +193,16 @@ impl ElementSnapshot for GeckoElementSnapshot { snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) } + #[inline] + fn exported_part(&self, name: &Atom) -> Option { + snapshot_helpers::exported_part(&*self.mAttrs, name) + } + + #[inline] + fn imported_part(&self, name: &Atom) -> Option { + snapshot_helpers::imported_part(&*self.mAttrs, name) + } + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { if !self.has_any(Flags::MaybeClass) { diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index b8b31bc87dd3..24b3ebb19910 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -82,6 +82,26 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> { Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) }) } +#[inline(always)] +pub(super) fn exported_part(attrs: &[structs::AttrArray_InternalAttr], name: &Atom) -> Option { + let attr = find_attr(attrs, &atom!("exportparts"))?; + let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) }; + if atom.is_null() { + return None; + } + Some(unsafe { Atom::from_raw(atom) }) +} + +#[inline(always)] +pub(super) fn imported_part(attrs: &[structs::AttrArray_InternalAttr], name: &Atom) -> Option { + let attr = find_attr(attrs, &atom!("exportparts"))?; + let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) }; + if atom.is_null() { + return None; + } + Some(unsafe { Atom::from_raw(atom) }) +} + /// Given a class or part name, a case sensitivity, and an array of attributes, /// returns whether the attribute has that name. #[inline(always)] diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 2d1236ce698a..ac47927744b3 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1244,8 +1244,12 @@ impl<'le> TElement for GeckoElement<'le> { #[inline] fn has_part_attr(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasPart) + self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasPart) + } + + #[inline] + fn exports_any_part(&self) -> bool { + snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some() } // FIXME(emilio): we should probably just return a reference to the Atom. @@ -2217,25 +2221,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } #[inline] - fn imported_part(&self, name: &Atom) -> Option { - let imported = unsafe { - bindings::Gecko_Element_ImportedPart(self.0, name.as_ptr()) - }; - if imported.is_null() { - return None; - } - Some(unsafe { Atom::from_raw(imported) }) + fn exported_part(&self, name: &Atom) -> Option { + snapshot_helpers::exported_part(self.attrs(), name) } #[inline] - fn exported_part(&self, name: &Atom) -> Option { - let exported = unsafe { - bindings::Gecko_Element_ExportedPart(self.0, name.as_ptr()) - }; - if exported.is_null() { - return None; - } - Some(unsafe { Atom::from_raw(exported) }) + fn imported_part(&self, name: &Atom) -> Option { + snapshot_helpers::imported_part(self.attrs(), name) } #[inline(always)] diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index e17e42a31c05..bc74527bf161 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -62,6 +62,12 @@ pub trait ElementSnapshot: Sized { /// called if `has_attrs()` returns true. fn is_part(&self, name: &Atom) -> bool; + /// See Element::exported_part. + fn exported_part(&self, name: &Atom) -> Option; + + /// See Element::imported_part. + fn imported_part(&self, name: &Atom) -> Option; + /// A callback that should be called for each class of the snapshot. Should /// only be called if `has_attrs()` returns true. fn each_class(&self, _: F) @@ -366,13 +372,17 @@ where } fn exported_part(&self, name: &Atom) -> Option { - // FIXME(emilio): Implement for proper invalidation. - self.element.exported_part(name) + match self.snapshot() { + Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name), + _ => self.element.exported_part(name), + } } fn imported_part(&self, name: &Atom) -> Option { - // FIXME(emilio): Implement for proper invalidation. - self.element.imported_part(name) + match self.snapshot() { + Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name), + _ => self.element.imported_part(name), + } } fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 9e8309396cdd..8bc898f95878 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -129,6 +129,10 @@ enum InvalidationKind { pub struct Invalidation<'a> { selector: &'a Selector, /// The right shadow host from where the rule came from, if any. + /// + /// This is needed to ensure that we match the selector with the right + /// state, as whether some selectors like :host and ::part() match depends + /// on it. scope: Option, /// The offset of the selector pointing to a compound selector. /// @@ -479,6 +483,47 @@ where any_descendant } + fn invalidate_parts_in_shadow_tree( + &mut self, + shadow: ::ConcreteShadowRoot, + invalidations: &[Invalidation<'b>], + ) -> bool { + debug_assert!(!invalidations.is_empty()); + + let mut any = false; + let mut sibling_invalidations = InvalidationVector::new(); + + for node in shadow.as_node().dom_descendants() { + let element = match node.as_element() { + Some(e) => e, + None => continue, + }; + + if element.has_part_attr() { + any |= self.invalidate_child( + element, + invalidations, + &mut sibling_invalidations, + DescendantInvalidationKind::Part, + ); + debug_assert!( + sibling_invalidations.is_empty(), + "::part() shouldn't have sibling combinators to the right, \ + this makes no sense! {:?}", + sibling_invalidations + ); + } + + if let Some(shadow) = element.shadow_root() { + if element.exports_any_part() { + any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations) + } + } + } + + any + } + fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool { if invalidations.is_empty() { return false; @@ -489,26 +534,7 @@ where None => return false, }; - let mut any = false; - let mut sibling_invalidations = InvalidationVector::new(); - - // FIXME(emilio): We also need to invalidate parts in descendant shadow - // hosts that have exportparts attributes. - for element in shadow.parts() { - any |= self.invalidate_child( - *element, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Part, - ); - debug_assert!( - sibling_invalidations.is_empty(), - "::part() shouldn't have sibling combinators to the right, \ - this makes no sense! {:?}", - sibling_invalidations - ); - } - any + self.invalidate_parts_in_shadow_tree(shadow, invalidations) } fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool { @@ -733,7 +759,7 @@ where ); let matching_result = { - let mut context = self.processor.matching_context(); + let context = self.processor.matching_context(); context.current_host = invalidation.scope; matches_compound_selector_from(