aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2019-11-21 10:32:10 +0000
committerEmilio Cobos Álvarez <emilio@crisal.io>2019-11-30 20:45:03 +0100
commitf8ceb5cb8409f434ec4acec8ab9c186bea45bcbe (patch)
tree53a411befd1ed157af6d2126f88991b25f5e1a8a
parente3009a4de9186c648c0a3beef4fdde6ce66b1a3b (diff)
downloadservo-f8ceb5cb8409f434ec4acec8ab9c186bea45bcbe.tar.gz
servo-f8ceb5cb8409f434ec4acec8ab9c186bea45bcbe.zip
style: Invalidate parts in nested shadow trees correctly.
Differential Revision: https://phabricator.services.mozilla.com/D54010
-rw-r--r--components/style/dom.rs3
-rw-r--r--components/style/gecko/snapshot.rs10
-rw-r--r--components/style/gecko/snapshot_helpers.rs20
-rw-r--r--components/style/gecko/wrapper.rs28
-rw-r--r--components/style/invalidation/element/element_wrapper.rs18
-rw-r--r--components/style/invalidation/element/invalidator.rs68
6 files changed, 104 insertions, 43 deletions
diff --git a/components/style/dom.rs b/components/style/dom.rs
index ef571b8df0a..fbe35e2e49d 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 cffb78d3e9e..02707682b4d 100644
--- a/components/style/gecko/snapshot.rs
+++ b/components/style/gecko/snapshot.rs
@@ -194,6 +194,16 @@ impl ElementSnapshot for GeckoElementSnapshot {
}
#[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) {
return false;
diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs
index b8b31bc87dd..24b3ebb1991 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<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)]
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 2d1236ce698..ac47927744b 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<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)]
diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs
index e17e42a31c0..bc74527bf16 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<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)
@@ -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 {
diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs
index 9e8309396cd..8bc898f9587 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<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.
///
@@ -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;
@@ -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(