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: &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(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().iter().flatten() {
248            usage |= usage_from_flags(pseudo_style.flags);
249        }
250
251        usage
252    }
253
254    #[cfg(feature = "gecko")]
255    fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
256        // As the method name suggests, we don't measures the ComputedValues
257        // here, because they are measured on the C++ side.
258
259        // XXX: measure the EagerPseudoArray itself, but not the ComputedValues
260        // within it.
261
262        0
263    }
264}
265
266// We manually implement Debug for ElementStyles so that we can avoid the
267// verbose stringification of every property in the ComputedValues. We
268// substitute the rule node instead.
269impl fmt::Debug for ElementStyles {
270    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271        write!(
272            f,
273            "ElementStyles {{ primary: {:?}, pseudos: {:?} }}",
274            self.primary.as_ref().map(|x| &x.rules),
275            self.pseudos
276        )
277    }
278}
279
280/// Style system data associated with an Element.
281///
282/// In Gecko, this hangs directly off the Element. Servo, this is embedded
283/// inside of layout data, which itself hangs directly off the Element. In
284/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
285#[derive(Debug, Default)]
286pub struct ElementData {
287    /// The styles for the element and its pseudo-elements.
288    pub styles: ElementStyles,
289
290    /// The restyle damage, indicating what kind of layout changes are required
291    /// afte restyling.
292    pub damage: RestyleDamage,
293
294    /// The restyle hint, which indicates whether selectors need to be rematched
295    /// for this element, its children, and its descendants.
296    pub hint: RestyleHint,
297
298    /// Flags.
299    pub flags: ElementDataFlags,
300}
301
302/// A struct that wraps ElementData, giving it the ability of doing thread-safety checks.
303#[derive(Debug, Default)]
304pub struct ElementDataWrapper {
305    inner: std::cell::UnsafeCell<ElementData>,
306    /// Implements optional (debug_assertions-only) thread-safety checking.
307    #[cfg(debug_assertions)]
308    refcell: AtomicRefCell<()>,
309}
310
311/// A read-only reference to ElementData.
312#[derive(Debug)]
313pub struct ElementDataMut<'a> {
314    v: &'a mut ElementData,
315    #[cfg(debug_assertions)]
316    _borrow: AtomicRefMut<'a, ()>,
317}
318
319/// A mutable reference to ElementData.
320#[derive(Debug)]
321pub struct ElementDataRef<'a> {
322    v: &'a ElementData,
323    #[cfg(debug_assertions)]
324    _borrow: AtomicRef<'a, ()>,
325}
326
327impl ElementDataWrapper {
328    /// Gets a non-exclusive reference to this ElementData.
329    #[inline(always)]
330    pub fn borrow(&self) -> ElementDataRef<'_> {
331        #[cfg(debug_assertions)]
332        let borrow = self.refcell.borrow();
333        ElementDataRef {
334            v: unsafe { &*self.inner.get() },
335            #[cfg(debug_assertions)]
336            _borrow: borrow,
337        }
338    }
339
340    /// Gets an exclusive reference to this ElementData.
341    #[inline(always)]
342    pub fn borrow_mut(&self) -> ElementDataMut<'_> {
343        #[cfg(debug_assertions)]
344        let borrow = self.refcell.borrow_mut();
345        ElementDataMut {
346            v: unsafe { &mut *self.inner.get() },
347            #[cfg(debug_assertions)]
348            _borrow: borrow,
349        }
350    }
351}
352
353impl<'a> Deref for ElementDataRef<'a> {
354    type Target = ElementData;
355    #[inline]
356    fn deref(&self) -> &Self::Target {
357        self.v
358    }
359}
360
361impl<'a> Deref for ElementDataMut<'a> {
362    type Target = ElementData;
363    #[inline]
364    fn deref(&self) -> &Self::Target {
365        &*self.v
366    }
367}
368
369impl<'a> DerefMut for ElementDataMut<'a> {
370    fn deref_mut(&mut self) -> &mut Self::Target {
371        &mut *self.v
372    }
373}
374
375// There's one of these per rendered elements so it better be small.
376size_of_test!(ElementData, 24);
377
378/// The kind of restyle that a single element should do.
379#[derive(Debug)]
380pub enum RestyleKind {
381    /// We need to run selector matching plus re-cascade, that is, a full
382    /// restyle.
383    MatchAndCascade,
384    /// We need to recascade with some replacement rule, such as the style
385    /// attribute, or animation rules.
386    CascadeWithReplacements(RestyleHint),
387    /// We only need to recascade, for example, because only inherited
388    /// properties in the parent changed.
389    CascadeOnly,
390}
391
392fn needs_to_match_self(hint: RestyleHint, style: &ComputedValues) -> bool {
393    if hint.intersects(RestyleHint::RESTYLE_SELF) {
394        return true;
395    }
396    if hint.intersects(RestyleHint::RESTYLE_SELF_IF_PSEUDO) && style.is_pseudo_style() {
397        return true;
398    }
399    if hint.intersects(RestyleHint::RESTYLE_IF_AFFECTED_BY_WM_OR_ANCESTOR_FONT)
400        && style
401            .flags
402            .intersects(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS_ON_CONTAINER_QUERIES)
403    {
404        return true;
405    }
406    hint.intersects(
407        RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES
408            | RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER,
409    ) && style
410        .flags
411        .intersects(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY)
412}
413
414fn needs_to_recascade_self(hint: RestyleHint, style: &ComputedValues) -> bool {
415    if hint.intersects(RestyleHint::RECASCADE_SELF) {
416        return true;
417    }
418    if hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE)
419        && style
420            .flags
421            .contains(ComputedValueFlags::INHERITS_RESET_STYLE)
422    {
423        return true;
424    }
425    if hint.intersects(RestyleHint::RESTYLE_IF_AFFECTED_BY_WM_OR_ANCESTOR_FONT)
426        && style
427            .flags
428            .contains(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS)
429    {
430        return true;
431    }
432    false
433}
434
435impl ElementData {
436    /// Invalidates style for this element, its descendants, and later siblings,
437    /// based on the snapshot of the element that we took when attributes or
438    /// state changed.
439    pub fn invalidate_style_if_needed<E: TElement>(
440        &mut self,
441        element: E,
442        shared_context: &SharedStyleContext,
443        stack_limit_checker: Option<&StackLimitChecker>,
444        selector_caches: &mut SelectorCaches,
445    ) -> InvalidationResult {
446        // In animation-only restyle we shouldn't touch snapshot at all.
447        if shared_context.traversal_flags.for_animation_only() {
448            return InvalidationResult::empty();
449        }
450
451        use crate::invalidation::element::invalidator::TreeStyleInvalidator;
452        use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor;
453
454        debug!(
455            "invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
456             handled_snapshot: {}, pseudo: {:?}",
457            element,
458            shared_context.traversal_flags,
459            element.has_snapshot(),
460            element.handled_snapshot(),
461            element.implemented_pseudo_element()
462        );
463
464        if !element.has_snapshot() || element.handled_snapshot() {
465            return InvalidationResult::empty();
466        }
467
468        let mut processor =
469            StateAndAttrInvalidationProcessor::new(shared_context, element, self, selector_caches);
470
471        let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor);
472
473        let result = invalidator.invalidate();
474
475        unsafe { element.set_handled_snapshot() }
476        debug_assert!(element.handled_snapshot());
477
478        result
479    }
480
481    /// Returns true if this element has styles.
482    #[inline]
483    pub fn has_styles(&self) -> bool {
484        self.styles.primary.is_some()
485    }
486
487    /// Returns this element's styles as resolved styles to use for sharing.
488    pub fn share_styles(&self) -> ResolvedElementStyles {
489        ResolvedElementStyles {
490            primary: self.share_primary_style(),
491            pseudos: self.styles.pseudos.clone(),
492        }
493    }
494
495    /// Returns this element's primary style as a resolved style to use for sharing.
496    pub fn share_primary_style(&self) -> PrimaryStyle {
497        let reused_via_rule_node = self
498            .flags
499            .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
500
501        PrimaryStyle {
502            style: ResolvedStyle(self.styles.primary().clone()),
503            reused_via_rule_node,
504        }
505    }
506
507    /// Return a copy of the element's primary style as a resolved style with the
508    /// given flags.
509    pub fn clone_style_with_flags(&self, flags: ComputedValueFlags) -> ResolvedStyle {
510        let primary_style = self.styles.primary();
511        // We are only using this pseudo to find the correct pseudo type so it
512        // does not matter it technically belongs to a different style.
513        let pseudo = primary_style.pseudo();
514        ResolvedStyle(
515            primary_style
516                .deref()
517                .clone_with_flags(flags, pseudo.as_ref()),
518        )
519    }
520
521    /// Sets a new set of styles, returning the old ones.
522    pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {
523        self.flags.set(
524            ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
525            new_styles.primary.reused_via_rule_node,
526        );
527        mem::replace(&mut self.styles, new_styles.into())
528    }
529
530    /// Returns the kind of restyling that we're going to need to do on this
531    /// element, based of the stored restyle hint.
532    pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option<RestyleKind> {
533        let style = match self.styles.primary {
534            Some(ref s) => s,
535            None => return Some(RestyleKind::MatchAndCascade),
536        };
537
538        if shared_context.traversal_flags.for_animation_only() {
539            return self.restyle_kind_for_animation(shared_context);
540        }
541
542        let hint = self.hint;
543        if hint.is_empty() {
544            return None;
545        }
546
547        if needs_to_match_self(hint, style) {
548            return Some(RestyleKind::MatchAndCascade);
549        }
550
551        if hint.has_replacements() {
552            debug_assert!(
553                !hint.has_animation_hint(),
554                "Animation only restyle hint should have already processed"
555            );
556            return Some(RestyleKind::CascadeWithReplacements(
557                hint & RestyleHint::replacements(),
558            ));
559        }
560
561        if needs_to_recascade_self(hint, style) {
562            return Some(RestyleKind::CascadeOnly);
563        }
564
565        None
566    }
567
568    /// Returns the kind of restyling for animation-only restyle.
569    fn restyle_kind_for_animation(
570        &self,
571        shared_context: &SharedStyleContext,
572    ) -> Option<RestyleKind> {
573        debug_assert!(shared_context.traversal_flags.for_animation_only());
574        debug_assert!(self.has_styles());
575
576        // FIXME: We should ideally restyle here, but it is a hack to work around our weird
577        // animation-only traversal stuff: If we're display: none and the rules we could
578        // match could change, we consider our style up-to-date. This is because re-cascading with
579        // and old style doesn't guarantee returning the correct animation style (that's
580        // bug 1393323). So if our display changed, and it changed from display: none, we would
581        // incorrectly forget about it and wouldn't be able to correctly style our descendants
582        // later.
583        // XXX Figure out if this still makes sense.
584        let hint = self.hint;
585        if self.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) {
586            return None;
587        }
588
589        let style = self.styles.primary();
590        // Return either CascadeWithReplacements or CascadeOnly in case of animation-only restyle.
591        // I.e. animation-only restyle never does selector matching.
592        if hint.has_animation_hint() {
593            return Some(RestyleKind::CascadeWithReplacements(
594                hint & RestyleHint::for_animations(),
595            ));
596        }
597
598        if needs_to_recascade_self(hint, style) {
599            return Some(RestyleKind::CascadeOnly);
600        }
601        None
602    }
603
604    /// Drops any restyle state from the element.
605    ///
606    /// FIXME(bholley): The only caller of this should probably just assert that the hint is empty
607    /// and call clear_flags_and_damage().
608    #[inline]
609    pub fn clear_restyle_state(&mut self) {
610        self.hint = RestyleHint::empty();
611        self.clear_restyle_flags_and_damage();
612    }
613
614    /// Drops restyle flags and damage from the element.
615    #[inline]
616    pub fn clear_restyle_flags_and_damage(&mut self) {
617        self.damage = RestyleDamage::empty();
618        self.flags.remove(ElementDataFlags::WAS_RESTYLED);
619    }
620
621    /// Mark this element as restyled, which is useful to know whether we need
622    /// to do a post-traversal.
623    pub fn set_restyled(&mut self) {
624        self.flags.insert(ElementDataFlags::WAS_RESTYLED);
625        self.flags
626            .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
627    }
628
629    /// Returns true if this element was restyled.
630    #[inline]
631    pub fn is_restyle(&self) -> bool {
632        self.flags.contains(ElementDataFlags::WAS_RESTYLED)
633    }
634
635    /// Mark that we traversed this element without computing any style for it.
636    pub fn set_traversed_without_styling(&mut self) {
637        self.flags
638            .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
639    }
640
641    /// Returns whether this element has been part of a restyle.
642    #[inline]
643    pub fn contains_restyle_data(&self) -> bool {
644        self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
645    }
646
647    /// Returns whether it is safe to perform cousin sharing based on the ComputedValues
648    /// identity of the primary style in this ElementData. There are a few subtle things
649    /// to check.
650    ///
651    /// First, if a parent element was already styled and we traversed past it without
652    /// restyling it, that may be because our clever invalidation logic was able to prove
653    /// that the styles of that element would remain unchanged despite changes to the id
654    /// or class attributes. However, style sharing relies on the strong guarantee that all
655    /// the classes and ids up the respective parent chains are identical. As such, if we
656    /// skipped styling for one (or both) of the parents on this traversal, we can't share
657    /// styles across cousins. Note that this is a somewhat conservative check. We could
658    /// tighten it by having the invalidation logic explicitly flag elements for which it
659    /// ellided styling.
660    ///
661    /// Second, we want to only consider elements whose ComputedValues match due to a hit
662    /// in the style sharing cache, rather than due to the rule-node-based reuse that
663    /// happens later in the styling pipeline. The former gives us the stronger guarantees
664    /// we need for style sharing, the latter does not.
665    pub fn safe_for_cousin_sharing(&self) -> bool {
666        if self.flags.intersects(
667            ElementDataFlags::TRAVERSED_WITHOUT_STYLING
668                | ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
669        ) {
670            return false;
671        }
672        if !self
673            .styles
674            .primary()
675            .get_box()
676            .clone_container_type()
677            .is_normal()
678        {
679            return false;
680        }
681        true
682    }
683
684    /// Measures memory usage.
685    #[cfg(feature = "gecko")]
686    pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
687        let n = self.styles.size_of_excluding_cvs(ops);
688
689        // We may measure more fields in the future if DMD says it's worth it.
690
691        n
692    }
693}