/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Per-node data used in style calculation.
usecrate::computed_value_flags::ComputedValueFlags; usecrate::context::{SharedStyleContext, StackLimitChecker}; usecrate::dom::TElement; usecrate::invalidation::element::invalidator::InvalidationResult; usecrate::invalidation::element::restyle_hints::RestyleHint; usecrate::properties::ComputedValues; usecrate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT}; usecrate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle}; usecrate::values::specified::TreeCountingFunction; #[cfg(feature = "gecko")] use malloc_size_of::MallocSizeOfOps; use selectors::matching::SelectorCaches; use servo_arc::Arc; use std::ops::{Deref, DerefMut}; use std::{fmt, mem};
#[cfg(debug_assertions)] use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
bitflags! { /// Various flags stored on ElementData. #[derive(Debug, Default)] pubstruct ElementDataFlags: u8 { /// Whether the styles changed for this restyle. const WAS_RESTYLED = 1 << 0; /// Whether the last traversal of this element did not do /// any style computation. This is not true during the initial /// styling pass, nor is it true when we restyle (in which case /// WAS_RESTYLED is set). /// /// This bit always corresponds to the last time the element was /// traversed, so each traversal simply updates it with the appropriate /// value. const TRAVERSED_WITHOUT_STYLING = 1 << 1;
/// Whether the primary style of this element data was reused from /// another element via a rule node comparison. This allows us to /// differentiate between elements that shared styles because they met /// all the criteria of the style sharing cache, compared to elements /// that reused style structs via rule node identity. /// /// The former gives us stronger transitive guarantees that allows us to /// apply the style sharing cache to cousins. const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;
}
}
/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements. /// /// We use an Arc so that sharing these styles via the style sharing cache does /// not require duplicate allocations. We leverage the copy-on-write semantics of /// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations) /// in servo_arc. #[derive(Clone, Debug, Default)] pubstruct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>);
#[derive(Default)] struct EagerPseudoArray(EagerPseudoArrayInner); type EagerPseudoArrayInner = [Option<Arc<ComputedValues>>; EAGER_PSEUDO_COUNT];
impl Deref for EagerPseudoArray { type Target = EagerPseudoArrayInner; fn deref(&self) -> &Self::Target {
&self.0
}
}
// Manually implement `Clone` here because the derived impl of `Clone` for // array types assumes the value inside is `Copy`. impl Clone for EagerPseudoArray { fn clone(&self) -> Self { letmut clone = Self::default(); for i in0..EAGER_PSEUDO_COUNT {
clone[i] = self.0[i].clone();
}
clone
}
}
// Override Debug to print which pseudos we have, and substitute the rule node // for the much-more-verbose ComputedValues stringification. impl fmt::Debug for EagerPseudoArray { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "EagerPseudoArray {{ ")?; for i in0..EAGER_PSEUDO_COUNT { iflet Some(ref values) = self[i] {
write!(
f, "{:?}: {:?}, ",
PseudoElement::from_eager_index(i),
&values.rules
)?;
}
}
write!(f, "}}")
}
}
// Can't use [None; EAGER_PSEUDO_COUNT] here because it complains // about Copy not being implemented for our Arc type. const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None];
impl EagerPseudoStyles { /// Returns whether there are any pseudo styles. pubfn is_empty(&self) -> bool { self.0.is_none()
}
/// Grabs a reference to the list of styles, if they exist. pubfn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> { matchself.0 {
None => None,
Some(ref x) => Some(&x.0),
}
}
/// Grabs a reference to the list of styles or a list of None if /// there are no styles to be had. pubfn as_array(&self) -> &EagerPseudoArrayInner { self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY)
}
/// Returns a reference to the style for a given eager pseudo, if it exists. pubfn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {
debug_assert!(pseudo.is_eager()); self.0
.as_ref()
.and_then(|p| p[pseudo.eager_index()].as_ref())
}
/// Sets the style for the eager pseudo. pubfn set(&mutself, pseudo: &PseudoElement, value: Arc<ComputedValues>) { ifself.0.is_none() { self.0 = Some(Arc::new(Default::default()));
} let arr = Arc::make_mut(self.0.as_mut().unwrap());
arr[pseudo.eager_index()] = Some(value);
}
}
/// The styles associated with a node, including the styles for any /// pseudo-elements. #[derive(Clone, Default)] pubstruct ElementStyles { /// The element's style. pub primary: Option<Arc<ComputedValues>>, /// A list of the styles for the element's eagerly-cascaded pseudo-elements. pub pseudos: EagerPseudoStyles,
}
// There's one of these per rendered elements so it better be small.
size_of_test!(ElementStyles, 16);
/// Information on how this element uses viewport units. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pubenum ViewportUnitUsage { /// No viewport units are used.
None = 0, /// There are viewport units used from regular style rules (which means we /// should re-cascade).
FromDeclaration, /// There are viewport units used from container queries (which means we /// need to re-selector-match).
FromQuery,
}
#[cfg(feature = "gecko")] fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize { // As the method name suggests, we don't measures the ComputedValues // here, because they are measured on the C++ side.
// XXX: measure the EagerPseudoArray itself, but not the ComputedValues // within it.
0
}
}
// We manually implement Debug for ElementStyles so that we can avoid the // verbose stringification of every property in the ComputedValues. We // substitute the rule node instead. impl fmt::Debug for ElementStyles { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f, "ElementStyles {{ primary: {:?}, pseudos: {:?} }}", self.primary.as_ref().map(|x| &x.rules), self.pseudos
)
}
}
/// Style system data associated with an Element. /// /// In Gecko, this hangs directly off the Element. Servo, this is embedded /// inside of layout data, which itself hangs directly off the Element. In /// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety. #[derive(Debug, Default)] pubstruct ElementData { /// The styles for the element and its pseudo-elements. pub styles: ElementStyles,
/// The restyle damage, indicating what kind of layout changes are required /// afte restyling. pub damage: RestyleDamage,
/// The restyle hint, which indicates whether selectors need to be rematched /// for this element, its children, and its descendants. pub hint: RestyleHint,
/// Flags. pub flags: ElementDataFlags,
}
/// A struct that wraps ElementData, giving it the ability of doing thread-safety checks. #[derive(Debug, Default)] pubstruct ElementDataWrapper {
inner: std::cell::UnsafeCell<ElementData>, /// Implements optional (debug_assertions-only) thread-safety checking. #[cfg(debug_assertions)]
refcell: AtomicRefCell<()>,
}
/// A read-only reference to ElementData. #[derive(Debug)] pubstruct ElementDataMut<'a> {
v: &'a mut ElementData, #[cfg(debug_assertions)]
_borrow: AtomicRefMut<'a, ()>,
}
/// A mutable reference to ElementData. #[derive(Debug)] pubstruct ElementDataRef<'a> {
v: &'a ElementData, #[cfg(debug_assertions)]
_borrow: AtomicRef<'a, ()>,
}
impl ElementDataWrapper { /// Gets a non-exclusive reference to this ElementData. #[inline(always)] pubfn borrow(&self) -> ElementDataRef<'_> { #[cfg(debug_assertions)] let borrow = self.refcell.borrow();
ElementDataRef {
v: unsafe { &*self.inner.get() }, #[cfg(debug_assertions)]
_borrow: borrow,
}
}
/// Gets an exclusive reference to this ElementData. #[inline(always)] pubfn borrow_mut(&self) -> ElementDataMut<'_> { #[cfg(debug_assertions)] let borrow = self.refcell.borrow_mut();
ElementDataMut {
v: unsafe { &mut *self.inner.get() }, #[cfg(debug_assertions)]
_borrow: borrow,
}
}
}
impl<'a> Deref for ElementDataRef<'a> { type Target = ElementData; #[inline] fn deref(&self) -> &Self::Target {
&*self.v
}
}
impl<'a> Deref for ElementDataMut<'a> { type Target = ElementData; #[inline] fn deref(&self) -> &Self::Target {
&*self.v
}
}
// There's one of these per rendered elements so it better be small.
size_of_test!(ElementData, 24);
/// The kind of restyle that a single element should do. #[derive(Debug)] pubenum RestyleKind { /// We need to run selector matching plus re-cascade, that is, a full /// restyle.
MatchAndCascade, /// We need to recascade with some replacement rule, such as the style /// attribute, or animation rules.
CascadeWithReplacements(RestyleHint), /// We only need to recascade, for example, because only inherited /// properties in the parent changed.
CascadeOnly,
}
impl ElementData { /// Invalidates style for this element, its descendants, and later siblings, /// based on the snapshot of the element that we took when attributes or /// state changed. pubfn invalidate_style_if_needed<'a, E: TElement>(
&mutself,
element: E,
shared_context: &SharedStyleContext,
stack_limit_checker: Option<&StackLimitChecker>,
selector_caches: &'a mut SelectorCaches,
) -> InvalidationResult { // In animation-only restyle we shouldn't touch snapshot at all. if shared_context.traversal_flags.for_animation_only() { return InvalidationResult::empty();
}
/// Returns true if this element has styles. #[inline] pubfn has_styles(&self) -> bool { self.styles.primary.is_some()
}
/// Returns this element's styles as resolved styles to use for sharing. pubfn share_styles(&self) -> ResolvedElementStyles {
ResolvedElementStyles {
primary: self.share_primary_style(),
pseudos: self.styles.pseudos.clone(),
}
}
/// Returns this element's primary style as a resolved style to use for sharing. pubfn share_primary_style(&self) -> PrimaryStyle { let reused_via_rule_node = self
.flags
.contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
/// Return a copy of the element's primary style as a resolved style with the /// given flags. pubfn clone_style_with_flags(&self, flags: ComputedValueFlags) -> ResolvedStyle { let primary_style = self.styles.primary(); // We are only using this pseudo to find the correct pseudo type so it // does not matter it technically belongs to a different style. let pseudo = primary_style.pseudo();
ResolvedStyle(
primary_style
.deref()
.clone_with_flags(flags, pseudo.as_ref()),
)
}
/// Sets a new set of styles, returning the old ones. pubfn set_styles(&mutself, new_styles: ResolvedElementStyles) -> ElementStyles { self.flags.set(
ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
new_styles.primary.reused_via_rule_node,
);
mem::replace(&mutself.styles, new_styles.into())
}
/// Returns the kind of restyling that we're going to need to do on this /// element, based of the stored restyle hint. pubfn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option<RestyleKind> { let style = matchself.styles.primary {
Some(ref s) => s,
None => return Some(RestyleKind::MatchAndCascade),
};
if shared_context.traversal_flags.for_animation_only() { returnself.restyle_kind_for_animation(shared_context);
}
let hint = self.hint; if hint.is_empty() { return None;
}
if needs_to_match_self(hint, style) { return Some(RestyleKind::MatchAndCascade);
}
if hint.has_replacements() {
debug_assert!(
!hint.has_animation_hint(), "Animation only restyle hint should have already processed"
); return Some(RestyleKind::CascadeWithReplacements(
hint & RestyleHint::replacements(),
));
}
if needs_to_recascade_self(hint, style) { return Some(RestyleKind::CascadeOnly);
}
None
}
/// Returns the kind of restyling for animation-only restyle. fn restyle_kind_for_animation(
&self,
shared_context: &SharedStyleContext,
) -> Option<RestyleKind> {
debug_assert!(shared_context.traversal_flags.for_animation_only());
debug_assert!(self.has_styles());
// FIXME: We should ideally restyle here, but it is a hack to work around our weird // animation-only traversal stuff: If we're display: none and the rules we could // match could change, we consider our style up-to-date. This is because re-cascading with // and old style doesn't guarantee returning the correct animation style (that's // bug 1393323). So if our display changed, and it changed from display: none, we would // incorrectly forget about it and wouldn't be able to correctly style our descendants // later. // XXX Figure out if this still makes sense. let hint = self.hint; ifself.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) { return None;
}
let style = self.styles.primary(); // Return either CascadeWithReplacements or CascadeOnly in case of animation-only restyle. // I.e. animation-only restyle never does selector matching. if hint.has_animation_hint() { return Some(RestyleKind::CascadeWithReplacements(
hint & RestyleHint::for_animations(),
));
}
if needs_to_recascade_self(hint, style) { return Some(RestyleKind::CascadeOnly);
} return None;
}
/// Drops any restyle state from the element. /// /// FIXME(bholley): The only caller of this should probably just assert that the hint is empty /// and call clear_flags_and_damage(). #[inline] pubfn clear_restyle_state(&mutself) { self.hint = RestyleHint::empty(); self.clear_restyle_flags_and_damage();
}
/// Drops restyle flags and damage from the element. #[inline] pubfn clear_restyle_flags_and_damage(&mutself) { self.damage = RestyleDamage::empty(); self.flags.remove(ElementDataFlags::WAS_RESTYLED);
}
/// Mark this element as restyled, which is useful to know whether we need /// to do a post-traversal. pubfn set_restyled(&mutself) { self.flags.insert(ElementDataFlags::WAS_RESTYLED); self.flags
.remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
}
/// Returns true if this element was restyled. #[inline] pubfn is_restyle(&self) -> bool { self.flags.contains(ElementDataFlags::WAS_RESTYLED)
}
/// Mark that we traversed this element without computing any style for it. pubfn set_traversed_without_styling(&mutself) { self.flags
.insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
}
/// Returns whether this element has been part of a restyle. #[inline] pubfn contains_restyle_data(&self) -> bool { self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
}
/// Returns whether it is safe to perform cousin sharing based on the ComputedValues /// identity of the primary style in this ElementData. There are a few subtle things /// to check. /// /// First, if a parent element was already styled and we traversed past it without /// restyling it, that may be because our clever invalidation logic was able to prove /// that the styles of that element would remain unchanged despite changes to the id /// or class attributes. However, style sharing relies on the strong guarantee that all /// the classes and ids up the respective parent chains are identical. As such, if we /// skipped styling for one (or both) of the parents on this traversal, we can't share /// styles across cousins. Note that this is a somewhat conservative check. We could /// tighten it by having the invalidation logic explicitly flag elements for which it /// ellided styling. /// /// Second, we want to only consider elements whose ComputedValues match due to a hit /// in the style sharing cache, rather than due to the rule-node-based reuse that /// happens later in the styling pipeline. The former gives us the stronger guarantees /// we need for style sharing, the latter does not. pubfn safe_for_cousin_sharing(&self) -> bool { ifself.flags.intersects(
ElementDataFlags::TRAVERSED_WITHOUT_STYLING
| ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
) { returnfalse;
} if !self
.styles
.primary()
.get_box()
.clone_container_type()
.is_normal()
{ returnfalse;
} true
}
/// Measures memory usage. #[cfg(feature = "gecko")] pubfn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize { let n = self.styles.size_of_excluding_cvs(ops);
// We may measure more fields in the future if DMD says it's worth it.
n
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.