Skip to content

Commit

Permalink
style: Invalidate parts in nested shadow trees correctly.
Browse files Browse the repository at this point in the history
  • Loading branch information
emilio committed Nov 30, 2019
1 parent e3009a4 commit f8ceb5c
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 43 deletions.
3 changes: 3 additions & 0 deletions components/style/dom.rs
Expand Up @@ -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>;

Expand Down
10 changes: 10 additions & 0 deletions components/style/gecko/snapshot.rs
Expand Up @@ -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<Atom> {
snapshot_helpers::exported_part(&*self.mAttrs, name)
}

#[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::imported_part(&*self.mAttrs, name)
}

#[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_any(Flags::MaybeClass) {
Expand Down
20 changes: 20 additions & 0 deletions components/style/gecko/snapshot_helpers.rs
Expand Up @@ -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<Atom> {
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<Atom> {
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)]
Expand Down
28 changes: 10 additions & 18 deletions components/style/gecko/wrapper.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -2217,25 +2221,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}

#[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> {
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<Atom> {
snapshot_helpers::exported_part(self.attrs(), name)
}

#[inline]
fn exported_part(&self, name: &Atom) -> Option<Atom> {
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<Atom> {
snapshot_helpers::imported_part(self.attrs(), name)
}

#[inline(always)]
Expand Down
18 changes: 14 additions & 4 deletions components/style/invalidation/element/element_wrapper.rs
Expand Up @@ -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<Atom>;

/// See Element::imported_part.
fn imported_part(&self, name: &Atom) -> Option<Atom>;

/// A callback that should be called for each class of the snapshot. Should
/// only be called if `has_attrs()` returns true.
fn each_class<F>(&self, _: F)
Expand Down Expand Up @@ -366,13 +372,17 @@ where
}

fn exported_part(&self, name: &Atom) -> Option<Atom> {
// 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<Atom> {
// 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 {
Expand Down
68 changes: 47 additions & 21 deletions components/style/invalidation/element/invalidator.rs
Expand Up @@ -129,6 +129,10 @@ enum InvalidationKind {
pub struct Invalidation<'a> {
selector: &'a Selector<SelectorImpl>,
/// 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<OpaqueElement>,
/// The offset of the selector pointing to a compound selector.
///
Expand Down Expand Up @@ -479,6 +483,47 @@ where
any_descendant
}

fn invalidate_parts_in_shadow_tree(
&mut self,
shadow: <E::ConcreteNode as TNode>::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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit f8ceb5c

Please sign in to comment.