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