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