style: Simplify NAC setup

Make all UA widgets also NAC.

Keep the UA widget flag but break at anonymous subtree boundaries, so
that only nodes inside the UA widget directly (and not NAC from those)
get the flag.

This is important because two callers depend on this difference:

  * The style system, since we still want to match content rules from
    stylesheets in the UA widget. We also match user rules, which is a
    bit sketchy, but that was the previous behavior, will file a
    follow-up for that.

  * The reflector code, since we want the scope for UA widgets to not
    include the NAC nodes inside that UA widget. nsINode::IsInUAWidget
    got it wrong.

After this patch, ChromeOnlyAccess is equivalent to
IsInNativeAnonymousSubtree, so we should probably unify the naming.
That's left for a follow-up patch because I don't have a strong
preference.

Differential Revision: https://phabricator.services.mozilla.com/D174310
This commit is contained in:
Emilio Cobos Álvarez 2023-04-05 09:19:15 +00:00 committed by Martin Robinson
parent 0917ee3f9b
commit 5dd35ac6cd
6 changed files with 87 additions and 113 deletions

View file

@ -388,10 +388,10 @@ pub trait TElement:
true true
} }
/// Whether this element should match user and author rules. /// Whether this element should match user and content rules.
/// ///
/// We use this for Native Anonymous Content in Gecko. /// We use this for Native Anonymous Content in Gecko.
fn matches_user_and_author_rules(&self) -> bool { fn matches_user_and_content_rules(&self) -> bool {
true true
} }
@ -651,11 +651,6 @@ pub trait TElement:
false false
} }
/// Returns true if this element is in a native anonymous subtree.
fn is_in_native_anonymous_subtree(&self) -> bool {
false
}
/// Returns the pseudo-element implemented by this element, if any. /// Returns the pseudo-element implemented by this element, if any.
/// ///
/// Gecko traverses pseudo-elements during the style traversal, and we need /// Gecko traverses pseudo-elements during the style traversal, and we need
@ -791,11 +786,8 @@ pub trait TElement:
use crate::rule_collector::containing_shadow_ignoring_svg_use; use crate::rule_collector::containing_shadow_ignoring_svg_use;
let target = self.rule_hash_target(); let target = self.rule_hash_target();
if !target.matches_user_and_author_rules() { let matches_user_and_content_rules = target.matches_user_and_content_rules();
return false; let mut doc_rules_apply = matches_user_and_content_rules;
}
let mut doc_rules_apply = true;
// Use the same rules to look for the containing host as we do for rule // Use the same rules to look for the containing host as we do for rule
// collection. // collection.
@ -843,9 +835,9 @@ pub trait TElement:
}, },
None => { None => {
// TODO(emilio): Should probably distinguish with // TODO(emilio): Should probably distinguish with
// MatchesDocumentRules::{No,Yes,IfPart} or // MatchesDocumentRules::{No,Yes,IfPart} or something so that we could
// something so that we could skip some work. // skip some work.
doc_rules_apply = true; doc_rules_apply = matches_user_and_content_rules;
break; break;
}, },
} }

View file

@ -325,32 +325,34 @@ impl<'ln> GeckoNode<'ln> {
#[inline] #[inline]
fn is_in_shadow_tree(&self) -> bool { fn is_in_shadow_tree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE; use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0 self.flags() & NODE_IS_IN_SHADOW_TREE != 0
} }
/// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent. /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the
/// Make sure to mirror any modifications in both places. /// same thing.
///
/// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and
/// from profiles it seems that keeping this fast path makes the compiler not inline
/// `flattened_tree_parent` as a whole, so we're not gaining much either.
#[inline] #[inline]
fn flattened_tree_parent_is_parent(&self) -> bool { fn flattened_tree_parent_is_parent(&self) -> bool {
use crate::gecko_bindings::structs::*; use crate::gecko_bindings::structs::*;
let flags = self.flags(); let flags = self.flags();
// FIXME(emilio): The shadow tree condition seems it shouldn't be needed let parent = match self.parent_node() {
// anymore, if we check for slots. Some(p) => p,
if self.is_in_shadow_tree() { None => return true,
};
if parent.is_shadow_root() {
return false; return false;
} }
let parent = unsafe { self.0.mParent.as_ref() }.map(GeckoNode); if let Some(parent) = parent.as_element() {
let parent_el = parent.and_then(|p| p.as_element()); if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {
if flags & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0 && return false;
parent_el.map_or(false, |el| el.is_root()) }
{ if parent.shadow_root().is_some() || parent.is_html_slot_element() {
return false;
}
if let Some(parent) = parent_el {
if parent.shadow_root().is_some() {
return false; return false;
} }
} }
@ -360,10 +362,6 @@ impl<'ln> GeckoNode<'ln> {
#[inline] #[inline]
fn flattened_tree_parent(&self) -> Option<Self> { fn flattened_tree_parent(&self) -> Option<Self> {
// TODO(emilio): Measure and consider not doing this fast-path and take
// always the common path, it's only a function call and from profiles
// it seems that keeping this fast path makes the compiler not inline
// `flattened_tree_parent`.
if self.flattened_tree_parent_is_parent() { if self.flattened_tree_parent_is_parent() {
debug_assert_eq!( debug_assert_eq!(
unsafe { unsafe {
@ -374,12 +372,10 @@ impl<'ln> GeckoNode<'ln> {
self.parent_node(), self.parent_node(),
"Fast path stopped holding!" "Fast path stopped holding!"
); );
return self.parent_node(); return self.parent_node();
} }
// NOTE(emilio): If this call is too expensive, we could manually // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.
// inline more aggressively.
unsafe { unsafe {
bindings::Gecko_GetFlattenedTreeParentNode(self.0) bindings::Gecko_GetFlattenedTreeParentNode(self.0)
.as_ref() .as_ref()
@ -650,20 +646,6 @@ impl<'le> GeckoElement<'le> {
snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class")) snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class"))
} }
#[inline]
fn closest_anon_subtree_root_parent(&self) -> Option<Self> {
debug_assert!(self.is_in_native_anonymous_subtree());
let mut current = *self;
loop {
if current.is_root_of_native_anonymous_subtree() {
return current.traversal_parent();
}
current = current.traversal_parent()?;
}
}
#[inline] #[inline]
fn may_have_anonymous_children(&self) -> bool { fn may_have_anonymous_children(&self) -> bool {
self.as_node() self.as_node()
@ -690,13 +672,13 @@ impl<'le> GeckoElement<'le> {
/// Returns true if this element has descendants for lazy frame construction. /// Returns true if this element has descendants for lazy frame construction.
#[inline] #[inline]
pub fn descendants_need_frames(&self) -> bool { pub fn descendants_need_frames(&self) -> bool {
self.flags() & (NODE_DESCENDANTS_NEED_FRAMES as u32) != 0 self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0
} }
/// Returns true if this element needs lazy frame construction. /// Returns true if this element needs lazy frame construction.
#[inline] #[inline]
pub fn needs_frame(&self) -> bool { pub fn needs_frame(&self) -> bool {
self.flags() & (NODE_NEEDS_FRAME as u32) != 0 self.flags() & NODE_NEEDS_FRAME != 0
} }
/// Returns a reference to the DOM slots for this Element, if they exist. /// Returns a reference to the DOM slots for this Element, if they exist.
@ -754,7 +736,7 @@ impl<'le> GeckoElement<'le> {
fn has_properties(&self) -> bool { fn has_properties(&self) -> bool {
use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES; use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;
(self.flags() & NODE_HAS_PROPERTIES as u32) != 0 self.flags() & NODE_HAS_PROPERTIES != 0
} }
#[inline] #[inline]
@ -822,7 +804,7 @@ impl<'le> GeckoElement<'le> {
#[inline] #[inline]
fn is_root_of_native_anonymous_subtree(&self) -> bool { fn is_root_of_native_anonymous_subtree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT; use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0; return self.flags() & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;
} }
/// Returns true if this node is the shadow root of an use-element shadow tree. /// Returns true if this node is the shadow root of an use-element shadow tree.
@ -905,19 +887,19 @@ fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
use crate::gecko_bindings::structs::*; use crate::gecko_bindings::structs::*;
let mut gecko_flags = 0u32; let mut gecko_flags = 0u32;
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) { if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR as u32; gecko_flags |= NODE_HAS_SLOW_SELECTOR;
} }
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) { if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS as u32; gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS;
} }
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) { if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF as u32; gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF;
} }
if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) { if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR as u32; gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR;
} }
if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) { if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
gecko_flags |= NODE_HAS_EMPTY_SELECTOR as u32; gecko_flags |= NODE_HAS_EMPTY_SELECTOR;
} }
gecko_flags gecko_flags
@ -974,8 +956,7 @@ impl<'le> TElement for GeckoElement<'le> {
// StyleChildrenIterator::IsNeeded does, except that it might return // StyleChildrenIterator::IsNeeded does, except that it might return
// true if we used to (but no longer) have anonymous content from // true if we used to (but no longer) have anonymous content from
// ::before/::after, or nsIAnonymousContentCreators. // ::before/::after, or nsIAnonymousContentCreators.
if self.is_in_native_anonymous_subtree() || if self.is_html_slot_element() ||
self.is_html_slot_element() ||
self.shadow_root().is_some() || self.shadow_root().is_some() ||
self.may_have_anonymous_children() self.may_have_anonymous_children()
{ {
@ -1296,52 +1277,52 @@ impl<'le> TElement for GeckoElement<'le> {
#[inline] #[inline]
fn has_snapshot(&self) -> bool { fn has_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0 self.flags() & ELEMENT_HAS_SNAPSHOT != 0
} }
#[inline] #[inline]
fn handled_snapshot(&self) -> bool { fn handled_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0 self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0
} }
unsafe fn set_handled_snapshot(&self) { unsafe fn set_handled_snapshot(&self) {
debug_assert!(self.has_data()); debug_assert!(self.has_data());
self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32) self.set_flags(ELEMENT_HANDLED_SNAPSHOT)
} }
#[inline] #[inline]
fn has_dirty_descendants(&self) -> bool { fn has_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0
} }
unsafe fn set_dirty_descendants(&self) { unsafe fn set_dirty_descendants(&self) {
debug_assert!(self.has_data()); debug_assert!(self.has_data());
debug!("Setting dirty descendants: {:?}", self); debug!("Setting dirty descendants: {:?}", self);
self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
} }
unsafe fn unset_dirty_descendants(&self) { unsafe fn unset_dirty_descendants(&self) {
self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
} }
#[inline] #[inline]
fn has_animation_only_dirty_descendants(&self) -> bool { fn has_animation_only_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0
} }
unsafe fn set_animation_only_dirty_descendants(&self) { unsafe fn set_animation_only_dirty_descendants(&self) {
self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
} }
unsafe fn unset_animation_only_dirty_descendants(&self) { unsafe fn unset_animation_only_dirty_descendants(&self) {
self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
} }
unsafe fn clear_descendant_bits(&self) { unsafe fn clear_descendant_bits(&self) {
self.unset_flags( self.unset_flags(
ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 | ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO |
NODE_DESCENDANTS_NEED_FRAMES as u32, NODE_DESCENDANTS_NEED_FRAMES,
) )
} }
@ -1349,21 +1330,19 @@ impl<'le> TElement for GeckoElement<'le> {
self.state().intersects(ElementState::VISITED) self.state().intersects(ElementState::VISITED)
} }
/// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree. /// We want to match rules from the same tree in all cases, except for native anonymous content
/// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or
/// pseudo-elements).
#[inline] #[inline]
fn is_in_native_anonymous_subtree(&self) -> bool { fn matches_user_and_content_rules(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; use crate::gecko_bindings::structs::{NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE, NODE_HAS_BEEN_IN_UA_WIDGET};
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0 let flags = self.flags();
} (flags & NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) == 0 || (flags & NODE_HAS_BEEN_IN_UA_WIDGET) != 0
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
!self.is_in_native_anonymous_subtree()
} }
#[inline] #[inline]
fn implemented_pseudo_element(&self) -> Option<PseudoElement> { fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
if !self.is_in_native_anonymous_subtree() { if self.matches_user_and_content_rules() {
return None; return None;
} }
@ -1371,8 +1350,7 @@ impl<'le> TElement for GeckoElement<'le> {
return None; return None;
} }
let pseudo_type = unsafe { bindings::Gecko_GetImplementedPseudo(self.0) }; PseudoElement::from_pseudo_type(unsafe { bindings::Gecko_GetImplementedPseudo(self.0) })
PseudoElement::from_pseudo_type(pseudo_type)
} }
#[inline] #[inline]
@ -1396,10 +1374,10 @@ impl<'le> TElement for GeckoElement<'le> {
unsafe fn clear_data(&self) { unsafe fn clear_data(&self) {
let ptr = self.0.mServoData.get(); let ptr = self.0.mServoData.get();
self.unset_flags( self.unset_flags(
ELEMENT_HAS_SNAPSHOT as u32 | ELEMENT_HAS_SNAPSHOT |
ELEMENT_HANDLED_SNAPSHOT as u32 | ELEMENT_HANDLED_SNAPSHOT |
structs::Element_kAllServoDescendantBits | structs::Element_kAllServoDescendantBits |
NODE_NEEDS_FRAME as u32, NODE_NEEDS_FRAME,
); );
if !ptr.is_null() { if !ptr.is_null() {
debug!("Dropping ElementData for {:?}", self); debug!("Dropping ElementData for {:?}", self);
@ -1826,7 +1804,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline] #[inline]
fn pseudo_element_originating_element(&self) -> Option<Self> { fn pseudo_element_originating_element(&self) -> Option<Self> {
debug_assert!(self.is_pseudo_element()); debug_assert!(self.is_pseudo_element());
self.closest_anon_subtree_root_parent() debug_assert!(!self.matches_user_and_content_rules());
let mut current = *self;
loop {
if current.is_root_of_native_anonymous_subtree() {
return current.traversal_parent();
}
current = current.traversal_parent()?;
}
} }
#[inline] #[inline]
@ -1977,6 +1963,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
.as_node() .as_node()
.parent_node() .parent_node()
.map_or(false, |p| p.is_document())); .map_or(false, |p| p.is_document()));
// XXX this should always return true at this point, shouldn't it?
unsafe { bindings::Gecko_IsRootElement(self.0) } unsafe { bindings::Gecko_IsRootElement(self.0) }
} }
@ -2102,7 +2089,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
true true
}, },
NonTSPseudoClass::MozNativeAnonymous => self.is_in_native_anonymous_subtree(), NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),
NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(), NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(),
NonTSPseudoClass::MozTableBorderNonzero => unsafe { NonTSPseudoClass::MozTableBorderNonzero => unsafe {
bindings::Gecko_IsTableBorderNonzero(self.0) bindings::Gecko_IsTableBorderNonzero(self.0)

View file

@ -73,7 +73,7 @@ where
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
rules: &'a mut ApplicableDeclarationList, rules: &'a mut ApplicableDeclarationList,
context: &'a mut MatchingContext<'b, E::Impl>, context: &'a mut MatchingContext<'b, E::Impl>,
matches_user_and_author_rules: bool, matches_user_and_content_rules: bool,
matches_document_author_rules: bool, matches_document_author_rules: bool,
in_sort_scope: bool, in_sort_scope: bool,
} }
@ -102,7 +102,7 @@ where
MatchingMode::Normal => element.rule_hash_target(), MatchingMode::Normal => element.rule_hash_target(),
}; };
let matches_user_and_author_rules = rule_hash_target.matches_user_and_author_rules(); let matches_user_and_content_rules = rule_hash_target.matches_user_and_content_rules();
// Gecko definitely has pseudo-elements with style attributes, like // Gecko definitely has pseudo-elements with style attributes, like
// ::-moz-color-swatch. // ::-moz-color-swatch.
@ -123,8 +123,8 @@ where
rule_inclusion, rule_inclusion,
context, context,
rules, rules,
matches_user_and_author_rules, matches_user_and_content_rules,
matches_document_author_rules: matches_user_and_author_rules, matches_document_author_rules: matches_user_and_content_rules,
in_sort_scope: false, in_sort_scope: false,
} }
} }
@ -179,7 +179,7 @@ where
} }
fn collect_user_rules(&mut self) { fn collect_user_rules(&mut self) {
if !self.matches_user_and_author_rules { if !self.matches_user_and_content_rules {
return; return;
} }
@ -257,7 +257,7 @@ where
while let Some(slot) = current { while let Some(slot) = current {
debug_assert!( debug_assert!(
self.matches_user_and_author_rules, self.matches_user_and_content_rules,
"We should not slot NAC anywhere" "We should not slot NAC anywhere"
); );
slots.push(slot); slots.push(slot);
@ -292,7 +292,7 @@ where
} }
fn collect_rules_from_containing_shadow_tree(&mut self) { fn collect_rules_from_containing_shadow_tree(&mut self) {
if !self.matches_user_and_author_rules { if !self.matches_user_and_content_rules {
return; return;
} }
@ -341,11 +341,6 @@ where
None => return, None => return,
}; };
debug_assert!(
self.matches_user_and_author_rules,
"NAC should not be a shadow host"
);
let style_data = match shadow.style_data() { let style_data = match shadow.style_data() {
Some(d) => d, Some(d) => d,
None => return, None => return,

View file

@ -584,8 +584,8 @@ impl<E: TElement> StyleSharingCache<E> {
}, },
}; };
if element.is_in_native_anonymous_subtree() { if !element.matches_user_and_content_rules() {
debug!("Failing to insert into the cache: NAC"); debug!("Failing to insert into the cache: no tree rules:");
return; return;
} }
@ -674,8 +674,8 @@ impl<E: TElement> StyleSharingCache<E> {
return None; return None;
} }
if target.is_in_native_anonymous_subtree() { if !target.matches_user_and_content_rules() {
debug!("{:?} Cannot share style: NAC", target.element); debug!("{:?} Cannot share style: content rules", target.element);
return None; return None;
} }
@ -699,7 +699,7 @@ impl<E: TElement> StyleSharingCache<E> {
nth_index_cache: &mut NthIndexCache, nth_index_cache: &mut NthIndexCache,
shared_context: &SharedStyleContext, shared_context: &SharedStyleContext,
) -> Option<ResolvedElementStyles> { ) -> Option<ResolvedElementStyles> {
debug_assert!(!target.is_in_native_anonymous_subtree()); debug_assert!(target.matches_user_and_content_rules());
// Check that we have the same parent, or at least that the parents // Check that we have the same parent, or at least that the parents
// share styles and permit sharing across their children. The latter // share styles and permit sharing across their children. The latter
@ -762,8 +762,8 @@ impl<E: TElement> StyleSharingCache<E> {
return None; return None;
} }
if target.matches_user_and_author_rules() != if target.matches_user_and_content_rules() !=
candidate.element.matches_user_and_author_rules() candidate.element.matches_user_and_content_rules()
{ {
trace!("Miss: User and Author Rules"); trace!("Miss: User and Author Rules");
return None; return None;

View file

@ -219,7 +219,7 @@ where
) -> PrimaryStyle { ) -> PrimaryStyle {
// Before doing the cascade, check the sharing cache and see if we can // Before doing the cascade, check the sharing cache and see if we can
// reuse the style via rule node identity. // reuse the style via rule node identity.
let may_reuse = !self.element.is_in_native_anonymous_subtree() && let may_reuse = self.element.matches_user_and_content_rules() &&
parent_style.is_some() && parent_style.is_some() &&
inputs.rules.is_some(); inputs.rules.is_some();

View file

@ -422,7 +422,7 @@ pub fn recalc_style_at<E, D, F>(
if let Some(restyle_kind) = restyle_kind { if let Some(restyle_kind) = restyle_kind {
child_restyle_requirement = compute_style(traversal_data, context, element, data, restyle_kind); child_restyle_requirement = compute_style(traversal_data, context, element, data, restyle_kind);
if element.is_in_native_anonymous_subtree() { if !element.matches_user_and_content_rules() {
// We must always cascade native anonymous subtrees, since they // We must always cascade native anonymous subtrees, since they
// may have pseudo-elements underneath that would inherit from the // may have pseudo-elements underneath that would inherit from the
// closest non-NAC ancestor instead of us. // closest non-NAC ancestor instead of us.