mirror of
https://github.com/servo/servo.git
synced 2025-07-31 19:20:22 +01:00
style: Invalidate parts in nested shadow trees correctly.
Differential Revision: https://phabricator.services.mozilla.com/D54010
This commit is contained in:
parent
e3009a4de9
commit
f8ceb5cb84
6 changed files with 104 additions and 43 deletions
|
@ -523,6 +523,9 @@ pub trait TElement:
|
||||||
/// Returns whether this element has a `part` attribute.
|
/// Returns whether this element has a `part` attribute.
|
||||||
fn has_part_attr(&self) -> bool;
|
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.
|
/// The ID for this element.
|
||||||
fn id(&self) -> Option<&WeakAtom>;
|
fn id(&self) -> Option<&WeakAtom>;
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,16 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
||||||
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
|
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]
|
#[inline]
|
||||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
if !self.has_any(Flags::MaybeClass) {
|
if !self.has_any(Flags::MaybeClass) {
|
||||||
|
|
|
@ -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"))?) })
|
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,
|
/// Given a class or part name, a case sensitivity, and an array of attributes,
|
||||||
/// returns whether the attribute has that name.
|
/// returns whether the attribute has that name.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -1244,8 +1244,12 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_part_attr(&self) -> bool {
|
fn has_part_attr(&self) -> bool {
|
||||||
self.as_node()
|
self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
|
||||||
.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.
|
// FIXME(emilio): we should probably just return a reference to the Atom.
|
||||||
|
@ -2217,25 +2221,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||||
let imported = unsafe {
|
snapshot_helpers::exported_part(self.attrs(), name)
|
||||||
bindings::Gecko_Element_ImportedPart(self.0, name.as_ptr())
|
|
||||||
};
|
|
||||||
if imported.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(unsafe { Atom::from_raw(imported) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||||
let exported = unsafe {
|
snapshot_helpers::imported_part(self.attrs(), name)
|
||||||
bindings::Gecko_Element_ExportedPart(self.0, name.as_ptr())
|
|
||||||
};
|
|
||||||
if exported.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(unsafe { Atom::from_raw(exported) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -62,6 +62,12 @@ pub trait ElementSnapshot: Sized {
|
||||||
/// called if `has_attrs()` returns true.
|
/// called if `has_attrs()` returns true.
|
||||||
fn is_part(&self, name: &Atom) -> bool;
|
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
|
/// A callback that should be called for each class of the snapshot. Should
|
||||||
/// only be called if `has_attrs()` returns true.
|
/// only be called if `has_attrs()` returns true.
|
||||||
fn each_class<F>(&self, _: F)
|
fn each_class<F>(&self, _: F)
|
||||||
|
@ -366,13 +372,17 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||||
// FIXME(emilio): Implement for proper invalidation.
|
match self.snapshot() {
|
||||||
self.element.exported_part(name)
|
Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name),
|
||||||
|
_ => self.element.exported_part(name),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||||
// FIXME(emilio): Implement for proper invalidation.
|
match self.snapshot() {
|
||||||
self.element.imported_part(name)
|
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 {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
|
|
|
@ -129,6 +129,10 @@ enum InvalidationKind {
|
||||||
pub struct Invalidation<'a> {
|
pub struct Invalidation<'a> {
|
||||||
selector: &'a Selector<SelectorImpl>,
|
selector: &'a Selector<SelectorImpl>,
|
||||||
/// The right shadow host from where the rule came from, if any.
|
/// 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>,
|
scope: Option<OpaqueElement>,
|
||||||
/// The offset of the selector pointing to a compound selector.
|
/// The offset of the selector pointing to a compound selector.
|
||||||
///
|
///
|
||||||
|
@ -479,6 +483,47 @@ where
|
||||||
any_descendant
|
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 {
|
fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
|
||||||
if invalidations.is_empty() {
|
if invalidations.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -489,26 +534,7 @@ where
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut any = false;
|
self.invalidate_parts_in_shadow_tree(shadow, invalidations)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
|
fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
|
||||||
|
@ -733,7 +759,7 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
let matching_result = {
|
let matching_result = {
|
||||||
let mut context = self.processor.matching_context();
|
let context = self.processor.matching_context();
|
||||||
context.current_host = invalidation.scope;
|
context.current_host = invalidation.scope;
|
||||||
|
|
||||||
matches_compound_selector_from(
|
matches_compound_selector_from(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue