Skip to main content

style/
data.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Per-node data used in style calculation.
6
7use crate::computed_value_flags::ComputedValueFlags;
8use crate::context::{SharedStyleContext, StackLimitChecker};
9use crate::dom::TElement;
10use crate::invalidation::element::invalidator::InvalidationResult;
11use crate::invalidation::element::restyle_hints::RestyleHint;
12use crate::properties::ComputedValues;
13use crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT};
14use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};
15use crate::values::specified::TreeCountingFunction;
16#[cfg(feature = "gecko")]
17use malloc_size_of::MallocSizeOfOps;
18use selectors::matching::SelectorCaches;
19use servo_arc::Arc;
20use std::ops::{Deref, DerefMut};
21use std::{fmt, mem};
22
23#[cfg(debug_assertions)]
24use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
25
26bitflags! {
27    /// Various flags stored on ElementData.
28    #[derive(Debug, Default)]
29    pub struct ElementDataFlags: u8 {
30        /// Whether the styles changed for this restyle.
31        const WAS_RESTYLED = 1 << 0;
32        /// Whether the last traversal of this element did not do
33        /// any style computation. This is not true during the initial
34        /// styling pass, nor is it true when we restyle (in which case
35        /// WAS_RESTYLED is set).
36        ///
37        /// This bit always corresponds to the last time the element was
38        /// traversed, so each traversal simply updates it with the appropriate
39        /// value.
40        const TRAVERSED_WITHOUT_STYLING = 1 << 1;
41
42        /// Whether the primary style of this element data was reused from
43        /// another element via a rule node comparison. This allows us to
44        /// differentiate between elements that shared styles because they met
45        /// all the criteria of the style sharing cache, compared to elements
46        /// that reused style structs via rule node identity.
47        ///
48        /// The former gives us stronger transitive guarantees that allows us to
49        /// apply the style sharing cache to cousins.
50        const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;
51    }
52}
53
54/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.
55///
56/// We use an Arc so that sharing these styles via the style sharing cache does
57/// not require duplicate allocations. We leverage the copy-on-write semantics of
58/// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations)
59/// in servo_arc.
60#[derive(Clone, Debug, Default)]
61pub struct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>);
62
63#[derive(Default)]
64struct EagerPseudoArray(EagerPseudoArrayInner);
65type EagerPseudoArrayInner = [Option<Arc<ComputedValues>>; EAGER_PSEUDO_COUNT];
66
67impl Deref for EagerPseudoArray {
68    type Target = EagerPseudoArrayInner;
69    fn deref(&self) -> &Self::Target {
70        &self.0
71    }
72}
73
74impl DerefMut for EagerPseudoArray {
75    fn deref_mut(&mut self) -> &mut Self::Target {
76        &mut self.0
77    }
78}
79
80// Manually implement `Clone` here because the derived impl of `Clone` for
81// array types assumes the value inside is `Copy`.
82impl Clone for EagerPseudoArray {
83    fn clone(&self) -> Self {
84        let mut clone = Self::default();
85        for i in 0..EAGER_PSEUDO_COUNT {
86            clone[i] = self.0[i].clone();
87        }
88        clone
89    }
90}
91
92// Override Debug to print which pseudos we have, and substitute the rule node
93// for the much-more-verbose ComputedValues stringification.
94impl fmt::Debug for EagerPseudoArray {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        write!(f, "EagerPseudoArray {{ ")?;
97        for i in 0..EAGER_PSEUDO_COUNT {
98            if let Some(ref values) = self[i] {
99                write!(
100                    f,
101                    "{:?}: {:?}, ",
102                    PseudoElement::from_eager_index(i),
103                    &values.rules
104                )?;
105            }
106        }
107        write!(f, "}}")
108    }
109}
110
111// Can't use [None; EAGER_PSEUDO_COUNT] here because it complains
112// about Copy not being implemented for our Arc type.
113const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None];
114
115impl EagerPseudoStyles {
116    /// Returns whether there are any pseudo styles.
117    pub fn is_empty(&self) -> bool {
118        self.0.is_none()
119    }
120
121    /// Grabs a reference to the list of styles, if they exist.
122    pub fn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> {
123        match self.0 {
124            None => None,
125            Some(ref x) => Some(&x.0),
126        }
127    }
128
129    /// Grabs a reference to the list of styles or a list of None if
130    /// there are no styles to be had.
131    pub fn as_array(&self) -> &EagerPseudoArrayInner {
132        self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY)
133    }
134
135    /// Returns a reference to the style for a given eager pseudo, if it exists.
136    pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {
137        debug_assert!(pseudo.is_eager());
138        self.0
139            .as_ref()
140            .and_then(|p| p[pseudo.eager_index()].as_ref())
141    }
142
143    /// Sets the style for the eager pseudo.
144    pub fn set(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {
145        if self.0.is_none() {
146            self.0 = Some(Arc::new(Default::default()));
147        }
148        let arr = Arc::make_mut(self.0.as_mut().unwrap());
149        arr[pseudo.eager_index()] = Some(value);
150    }
151}
152
153/// The styles associated with a node, including the styles for any
154/// pseudo-elements.
155#[derive(Clone, Default)]
156pub struct ElementStyles {
157    /// The element's style.
158    pub primary: Option<Arc<ComputedValues>>,
159    /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
160    pub pseudos: EagerPseudoStyles,
161}
162
163// There's one of these per rendered elements so it better be small.
164size_of_test!(ElementStyles, 16);
165
166/// Information on how this element uses viewport units.
167#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
168pub enum ViewportUnitUsage {
169    /// No viewport units are used.
170    None = 0,
171    /// There are viewport units used from regular style rules (which means we
172    /// should re-cascade).
173    FromDeclaration,
174    /// There are viewport units used from container queries (which means we
175    /// need to re-selector-match).
176    FromQuery,
177}
178
179impl ElementStyles {
180    /// Returns the primary style.
181    pub fn get_primary(&self) -> Option<&Arc<ComputedValues>> {
182        self.primary.as_ref()
183    }
184
185    /// Returns the primary style.  Panic if no style available.
186    pub fn primary(&self) -> &Arc<ComputedValues> {
187        self.primary.as_ref().unwrap()
188    }
189
190    /// Whether this element `display` value is `none`.
191    pub fn is_display_none(&self) -> bool {
192        self.primary().get_box().clone_display().is_none()
193    }
194
195    /// Whether this element uses viewport units.
196    pub fn viewport_unit_usage(&self) -> ViewportUnitUsage {
197        fn usage_from_flags(flags: ComputedValueFlags) -> ViewportUnitUsage {
198            if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES) {
199                return ViewportUnitUsage::FromQuery;
200            }
201            if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
202                return ViewportUnitUsage::FromDeclaration;
203            }
204            ViewportUnitUsage::None
205        }
206
207        let primary = self.primary();
208        let mut usage = usage_from_flags(primary.flags);
209
210        // Check cached lazy pseudos on the primary style.
211        primary.each_cached_lazy_pseudo(|style| {
212            usage = std::cmp::max(usage, usage_from_flags(style.flags));
213        });
214
215        for pseudo_style in self.pseudos.as_array() {
216            if let Some(ref pseudo_style) = pseudo_style {
217                usage = std::cmp::max(usage, usage_from_flags(pseudo_style.flags));
218                // Also check cached lazy pseudos on eager pseudo styles.
219                pseudo_style.each_cached_lazy_pseudo(|style| {
220                    usage = std::cmp::max(usage, usage_from_flags(style.flags));
221                });
222            }
223        }
224
225        usage
226    }
227
228    /// Whether this element uses sibling-count() or sibling-index().
229    pub fn uses_tree_counting_function(&self, t: TreeCountingFunction) -> bool {
230        let usage_from_flags = |flags: ComputedValueFlags| -> bool {
231            if t == TreeCountingFunction::SiblingCount
232                && flags.intersects(ComputedValueFlags::USES_SIBLING_COUNT)
233            {
234                return true;
235            }
236            if t == TreeCountingFunction::SiblingIndex
237                && flags.intersects(ComputedValueFlags::USES_SIBLING_INDEX)
238            {
239                return true;
240            }
241            false
242        };
243
244        let primary = self.primary();
245        let mut usage = usage_from_flags(primary.flags);
246
247        for pseudo_style in self.pseudos.as_array() {
248            if let Some(ref pseudo_style) = pseudo_style {
249                usage |= usage_from_flags(pseudo_style.flags);
250            }
251        }
252
253        usage
254    }
255
256    #[cfg(feature = "gecko")]
257    fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
258        // As the method name suggests, we don't measures the ComputedValues
259        // here, because they are measured on the C++ side.
260
261        // XXX: measure the EagerPseudoArray itself, but not the ComputedValues
262        // within it.
263
264        0
265    }
266}
267
268// We manually implement Debug for ElementStyles so that we can avoid the
269// verbose stringification of every property in the ComputedValues. We
270// substitute the rule node instead.
271impl fmt::Debug for ElementStyles {
272    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273        write!(
274            f,
275            "ElementStyles {{ primary: {:?}, pseudos: {:?} }}",
276            self.primary.as_ref().map(|x| &x.rules),
277            self.pseudos
278        )
279    }
280}
281
282/// Style system data associated with an Element.
283///
284/// In Gecko, this hangs directly off the Element. Servo, this is embedded
285/// inside of layout data, which itself hangs directly off the Element. In
286/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
287#[derive(Debug, Default)]
288pub struct ElementData {
289    /// The styles for the element and its pseudo-elements.
290    pub styles: ElementStyles,
291
292    /// The restyle damage, indicating what kind of layout changes are required
293    /// afte restyling.
294    pub damage: RestyleDamage,
295
296    /// The restyle hint, which indicates whether selectors need to be rematched
297    /// for this element, its children, and its descendants.
298    pub hint: RestyleHint,
299
300    /// Flags.
301    pub flags: ElementDataFlags,
302}
303
304/// A struct that wraps ElementData, giving it the ability of doing thread-safety checks.
305#[derive(Debug, Default)]
306pub struct ElementDataWrapper {
307    inner: std::cell::UnsafeCell<ElementData>,
308    /// Implements optional (debug_assertions-only) thread-safety checking.
309    #[cfg(debug_assertions)]
310    refcell: AtomicRefCell<()>,
311}
312
313/// A read-only reference to ElementData.
314#[derive(Debug)]
315pub struct ElementDataMut<'a> {
316    v: &'a mut ElementData,
317    #[cfg(debug_assertions)]
318    _borrow: AtomicRefMut<'a, ()>,
319}
320
321/// A mutable reference to ElementData.
322#[derive(Debug)]
323pub struct ElementDataRef<'a> {
324    v: &'a ElementData,
325    #[cfg(debug_assertions)]
326    _borrow: AtomicRef<'a, ()>,
327}
328
329impl ElementDataWrapper {
330    /// Gets a non-exclusive reference to this ElementData.
331    #[inline(always)]
332    pub fn borrow(&self) -> ElementDataRef<'_> {
333        #[cfg(debug_assertions)]
334        let borrow = self.refcell.borrow();
335        ElementDataRef {
336            v: unsafe { &*self.inner.get() },
337            #[cfg(debug_assertions)]
338            _borrow: borrow,
339        }
340    }
341
342    /// Gets an exclusive reference to this ElementData.
343    #[inline(always)]
344    pub fn borrow_mut(&self) -> ElementDataMut<'_> {
345        #[cfg(debug_assertions)]
346        let borrow = self.refcell.borrow_mut();
347        ElementDataMut {
348            v: unsafe { &mut *self.inner.get() },
349            #[cfg(debug_assertions)]
350            _borrow: borrow,
351        }
352    }
353}
354
355impl<'a> Deref for ElementDataRef<'a> {
356    type Target = ElementData;
357    #[inline]
358    fn deref(&self) -> &Self::Target {
359        &*self.v
360    }
361}
362
363impl<'a> Deref for ElementDataMut<'a> {
364    type Target = ElementData;
365    #[inline]
366    fn deref(&self) -> &Self::Target {
367        &*self.v
368    }
369}
370
371impl<'a> DerefMut for ElementDataMut<'a> {
372    fn deref_mut(&mut self) -> &mut Self::Target {
373        &mut *self.v
374    }
375}
376
377// There's one of these per rendered elements so it better be small.
378size_of_test!(ElementData, 24);
379
380/// The kind of restyle that a single element should do.
381#[derive(Debug)]
382pub enum RestyleKind {
383    /// We need to run selector matching plus re-cascade, that is, a full
384    /// restyle.
385    MatchAndCascade,
386    /// We need to recascade with some replacement rule, such as the style
387    /// attribute, or animation rules.
388    CascadeWithReplacements(RestyleHint),
389    /// We only need to recascade, for example, because only inherited
390    /// properties in the parent changed.
391    CascadeOnly,
392}
393
394fn needs_to_match_self(hint: RestyleHint, style: &ComputedValues) -> bool {
395    if hint.intersects(RestyleHint::RESTYLE_SELF) {
396        return true;
397    }
398    if hint.intersects(RestyleHint::RESTYLE_SELF_IF_PSEUDO) && style.is_pseudo_style() {
399        return true;
400    }
401    if hint.intersects(RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT)
402        && style
403            .flags
404            .intersects(ComputedValueFlags::USES_FONT_RELATIVE_UNITS_ON_CONTAINER_QUERIES)
405    {
406        return true;
407    }
408    hint.intersects(
409        RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES
410            | RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER,
411    ) && style
412        .flags
413        .intersects(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY)
414}
415
416fn needs_to_recascade_self(hint: RestyleHint, style: &ComputedValues) -> bool {
417    if hint.intersects(RestyleHint::RECASCADE_SELF) {
418        return true;
419    }
420    if hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE)
421        && style
422            .flags
423            .contains(ComputedValueFlags::INHERITS_RESET_STYLE)
424    {
425        return true;
426    }
427    if hint.intersects(RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT)
428        && style
429            .flags
430            .contains(ComputedValueFlags::USES_FONT_RELATIVE_UNITS)
431    {
432        return true;
433    }
434    return false;
435}
436
437impl ElementData {
438    /// Invalidates style for this element, its descendants, and later siblings,
439    /// based on the snapshot of the element that we took when attributes or
440    /// state changed.
441    pub fn invalidate_style_if_needed<'a, E: TElement>(
442        &mut self,
443        element: E,
444        shared_context: &SharedStyleContext,
445        stack_limit_checker: Option<&StackLimitChecker>,
446        selector_caches: &'a mut SelectorCaches,
447    ) -> InvalidationResult {
448        // In animation-only restyle we shouldn't touch snapshot at all.
449        if shared_context.traversal_flags.for_animation_only() {
450            return InvalidationResult::empty();
451        }
452
453        use crate::invalidation::element::invalidator::TreeStyleInvalidator;
454        use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor;
455
456        debug!(
457            "invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
458             handled_snapshot: {}, pseudo: {:?}",
459            element,
460            shared_context.traversal_flags,
461            element.has_snapshot(),
462            element.handled_snapshot(),
463            element.implemented_pseudo_element()
464        );
465
466        if !element.has_snapshot() || element.handled_snapshot() {
467            return InvalidationResult::empty();
468        }
469
470        let mut processor =
471            StateAndAttrInvalidationProcessor::new(shared_context, element, self, selector_caches);
472
473        let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor);
474
475        let result = invalidator.invalidate();
476
477        unsafe { element.set_handled_snapshot() }
478        debug_assert!(element.handled_snapshot());
479
480        result
481    }
482
483    /// Returns true if this element has styles.
484    #[inline]
485    pub fn has_styles(&self) -> bool {
486        self.styles.primary.is_some()
487    }
488
489    /// Returns this element's styles as resolved styles to use for sharing.
490    pub fn share_styles(&self) -> ResolvedElementStyles {
491        ResolvedElementStyles {
492            primary: self.share_primary_style(),
493            pseudos: self.styles.pseudos.clone(),
494        }
495    }
496
497    /// Returns this element's primary style as a resolved style to use for sharing.
498    pub fn share_primary_style(&self) -> PrimaryStyle {
499        let reused_via_rule_node = self
500            .flags
501            .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
502
503        PrimaryStyle {
504            style: ResolvedStyle(self.styles.primary().clone()),
505            reused_via_rule_node,
506        }
507    }
508
509    /// Return a copy of the element's primary style as a resolved style with the
510    /// given flags.
511    pub fn clone_style_with_flags(&self, flags: ComputedValueFlags) -> ResolvedStyle {
512        let primary_style = self.styles.primary();
513        // We are only using this pseudo to find the correct pseudo type so it
514        // does not matter it technically belongs to a different style.
515        let pseudo = primary_style.pseudo();
516        ResolvedStyle(
517            primary_style
518                .deref()
519                .clone_with_flags(flags, pseudo.as_ref()),
520        )
521    }
522
523    /// Sets a new set of styles, returning the old ones.
524    pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {
525        self.flags.set(
526            ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
527            new_styles.primary.reused_via_rule_node,
528        );
529        mem::replace(&mut self.styles, new_styles.into())
530    }
531
532    /// Returns the kind of restyling that we're going to need to do on this
533    /// element, based of the stored restyle hint.
534    pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option<RestyleKind> {
535        let style = match self.styles.primary {
536            Some(ref s) => s,
537            None => return Some(RestyleKind::MatchAndCascade),
538        };
539
540        if shared_context.traversal_flags.for_animation_only() {
541            return self.restyle_kind_for_animation(shared_context);
542        }
543
544        let hint = self.hint;
545        if hint.is_empty() {
546            return None;
547        }
548
549        if needs_to_match_self(hint, style) {
550            return Some(RestyleKind::MatchAndCascade);
551        }
552
553        if hint.has_replacements() {
554            debug_assert!(
555                !hint.has_animation_hint(),
556                "Animation only restyle hint should have already processed"
557            );
558            return Some(RestyleKind::CascadeWithReplacements(
559                hint & RestyleHint::replacements(),
560            ));
561        }
562
563        if needs_to_recascade_self(hint, style) {
564            return Some(RestyleKind::CascadeOnly);
565        }
566
567        None
568    }
569
570    /// Returns the kind of restyling for animation-only restyle.
571    fn restyle_kind_for_animation(
572        &self,
573        shared_context: &SharedStyleContext,
574    ) -> Option<RestyleKind> {
575        debug_assert!(shared_context.traversal_flags.for_animation_only());
576        debug_assert!(self.has_styles());
577
578        // FIXME: We should ideally restyle here, but it is a hack to work around our weird
579        // animation-only traversal stuff: If we're display: none and the rules we could
580        // match could change, we consider our style up-to-date. This is because re-cascading with
581        // and old style doesn't guarantee returning the correct animation style (that's
582        // bug 1393323). So if our display changed, and it changed from display: none, we would
583        // incorrectly forget about it and wouldn't be able to correctly style our descendants
584        // later.
585        // XXX Figure out if this still makes sense.
586        let hint = self.hint;
587        if self.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) {
588            return None;
589        }
590
591        let style = self.styles.primary();
592        // Return either CascadeWithReplacements or CascadeOnly in case of animation-only restyle.
593        // I.e. animation-only restyle never does selector matching.
594        if hint.has_animation_hint() {
595            return Some(RestyleKind::CascadeWithReplacements(
596                hint & RestyleHint::for_animations(),
597            ));
598        }
599
600        if needs_to_recascade_self(hint, style) {
601            return Some(RestyleKind::CascadeOnly);
602        }
603        return None;
604    }
605
606    /// Drops any restyle state from the element.
607    ///
608    /// FIXME(bholley): The only caller of this should probably just assert that the hint is empty
609    /// and call clear_flags_and_damage().
610    #[inline]
611    pub fn clear_restyle_state(&mut self) {
612        self.hint = RestyleHint::empty();
613        self.clear_restyle_flags_and_damage();
614    }
615
616    /// Drops restyle flags and damage from the element.
617    #[inline]
618    pub fn clear_restyle_flags_and_damage(&mut self) {
619        self.damage = RestyleDamage::empty();
620        self.flags.remove(ElementDataFlags::WAS_RESTYLED);
621    }
622
623    /// Mark this element as restyled, which is useful to know whether we need
624    /// to do a post-traversal.
625    pub fn set_restyled(&mut self) {
626        self.flags.insert(ElementDataFlags::WAS_RESTYLED);
627        self.flags
628            .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
629    }
630
631    /// Returns true if this element was restyled.
632    #[inline]
633    pub fn is_restyle(&self) -> bool {
634        self.flags.contains(ElementDataFlags::WAS_RESTYLED)
635    }
636
637    /// Mark that we traversed this element without computing any style for it.
638    pub fn set_traversed_without_styling(&mut self) {
639        self.flags
640            .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
641    }
642
643    /// Returns whether this element has been part of a restyle.
644    #[inline]
645    pub fn contains_restyle_data(&self) -> bool {
646        self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
647    }
648
649    /// Returns whether it is safe to perform cousin sharing based on the ComputedValues
650    /// identity of the primary style in this ElementData. There are a few subtle things
651    /// to check.
652    ///
653    /// First, if a parent element was already styled and we traversed past it without
654    /// restyling it, that may be because our clever invalidation logic was able to prove
655    /// that the styles of that element would remain unchanged despite changes to the id
656    /// or class attributes. However, style sharing relies on the strong guarantee that all
657    /// the classes and ids up the respective parent chains are identical. As such, if we
658    /// skipped styling for one (or both) of the parents on this traversal, we can't share
659    /// styles across cousins. Note that this is a somewhat conservative check. We could
660    /// tighten it by having the invalidation logic explicitly flag elements for which it
661    /// ellided styling.
662    ///
663    /// Second, we want to only consider elements whose ComputedValues match due to a hit
664    /// in the style sharing cache, rather than due to the rule-node-based reuse that
665    /// happens later in the styling pipeline. The former gives us the stronger guarantees
666    /// we need for style sharing, the latter does not.
667    pub fn safe_for_cousin_sharing(&self) -> bool {
668        if self.flags.intersects(
669            ElementDataFlags::TRAVERSED_WITHOUT_STYLING
670                | ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
671        ) {
672            return false;
673        }
674        if !self
675            .styles
676            .primary()
677            .get_box()
678            .clone_container_type()
679            .is_normal()
680        {
681            return false;
682        }
683        true
684    }
685
686    /// Measures memory usage.
687    #[cfg(feature = "gecko")]
688    pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
689        let n = self.styles.size_of_excluding_cvs(ops);
690
691        // We may measure more fields in the future if DMD says it's worth it.
692
693        n
694    }
695}