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 
7 use crate::context::{SharedStyleContext, StackLimitChecker};
8 use crate::dom::TElement;
9 use crate::invalidation::element::invalidator::InvalidationResult;
10 use crate::invalidation::element::restyle_hints::RestyleHint;
11 use crate::properties::ComputedValues;
12 use crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT};
13 use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};
14 #[cfg(feature = "gecko")]
15 use malloc_size_of::MallocSizeOfOps;
16 use selectors::NthIndexCache;
17 use servo_arc::Arc;
18 use std::fmt;
19 use std::mem;
20 use std::ops::{Deref, DerefMut};
21 
22 bitflags! {
23     /// Various flags stored on ElementData.
24     #[derive(Default)]
25     pub struct ElementDataFlags: u8 {
26         /// Whether the styles changed for this restyle.
27         const WAS_RESTYLED = 1 << 0;
28         /// Whether the last traversal of this element did not do
29         /// any style computation. This is not true during the initial
30         /// styling pass, nor is it true when we restyle (in which case
31         /// WAS_RESTYLED is set).
32         ///
33         /// This bit always corresponds to the last time the element was
34         /// traversed, so each traversal simply updates it with the appropriate
35         /// value.
36         const TRAVERSED_WITHOUT_STYLING = 1 << 1;
37 
38         /// Whether the primary style of this element data was reused from
39         /// another element via a rule node comparison. This allows us to
40         /// differentiate between elements that shared styles because they met
41         /// all the criteria of the style sharing cache, compared to elements
42         /// that reused style structs via rule node identity.
43         ///
44         /// The former gives us stronger transitive guarantees that allows us to
45         /// apply the style sharing cache to cousins.
46         const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;
47     }
48 }
49 
50 /// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.
ship_label(PsppireScanf * box,const char ** s,const char_directives * dirs,size_t dir_idx)51 ///
52 /// We use an Arc so that sharing these styles via the style sharing cache does
53 /// not require duplicate allocations. We leverage the copy-on-write semantics of
54 /// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations)
55 /// in servo_arc.
56 #[derive(Clone, Debug, Default)]
57 pub struct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>);
58 
59 #[derive(Default)]
60 struct EagerPseudoArray(EagerPseudoArrayInner);
61 type EagerPseudoArrayInner = [Option<Arc<ComputedValues>>; EAGER_PSEUDO_COUNT];
62 
63 impl Deref for EagerPseudoArray {
64     type Target = EagerPseudoArrayInner;
65     fn deref(&self) -> &Self::Target {
66         &self.0
67     }
68 }
69 
70 impl DerefMut for EagerPseudoArray {
71     fn deref_mut(&mut self) -> &mut Self::Target {
72         &mut self.0
73     }
74 }
75 
76 // Manually implement `Clone` here because the derived impl of `Clone` for
77 // array types assumes the value inside is `Copy`.
78 impl Clone for EagerPseudoArray {
79     fn clone(&self) -> Self {
80         let mut clone = Self::default();
81         for i in 0..EAGER_PSEUDO_COUNT {
82             clone[i] = self.0[i].clone();
83         }
84         clone
85     }
guts(PsppireScanf * scanf)86 }
87 
88 // Override Debug to print which pseudos we have, and substitute the rule node
89 // for the much-more-verbose ComputedValues stringification.
90 impl fmt::Debug for EagerPseudoArray {
91     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92         write!(f, "EagerPseudoArray {{ ")?;
93         for i in 0..EAGER_PSEUDO_COUNT {
94             if let Some(ref values) = self[i] {
95                 write!(
96                     f,
97                     "{:?}: {:?}, ",
98                     PseudoElement::from_eager_index(i),
99                     &values.rules
100                 )?;
101             }
102         }
103         write!(f, "}}")
104     }
105 }
106 
107 // Can't use [None; EAGER_PSEUDO_COUNT] here because it complains
108 // about Copy not being implemented for our Arc type.
109 #[cfg(feature = "gecko")]
110 const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None];
111 #[cfg(feature = "servo")]
112 const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None];
113 
114 impl EagerPseudoStyles {
115     /// Returns whether there are any pseudo styles.
116     pub fn is_empty(&self) -> bool {
117         self.0.is_none()
118     }
119 
120     /// Grabs a reference to the list of styles, if they exist.
121     pub fn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> {
122         match self.0 {
123             None => None,
124             Some(ref x) => Some(&x.0),
125         }
126     }
127 
128     /// Grabs a reference to the list of styles or a list of None if
129     /// there are no styles to be had.
130     pub fn as_array(&self) -> &EagerPseudoArrayInner {
131         self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY)
132     }
133 
134     /// Returns a reference to the style for a given eager pseudo, if it exists.
135     pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {
136         debug_assert!(pseudo.is_eager());
137         self.0
138             .as_ref()
139             .and_then(|p| p[pseudo.eager_index()].as_ref())
140     }
141 
142     /// Sets the style for the eager pseudo.
143     pub fn set(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {
144         if self.0.is_none() {
145             self.0 = Some(Arc::new(Default::default()));
146         }
147         let arr = Arc::make_mut(self.0.as_mut().unwrap());
148         arr[pseudo.eager_index()] = Some(value);
149     }
150 }
151 
set_mnemonic(PsppireScanf * scanf)152 /// The styles associated with a node, including the styles for any
153 /// pseudo-elements.
154 #[derive(Clone, Default)]
155 pub struct ElementStyles {
156     /// The element's style.
157     pub primary: Option<Arc<ComputedValues>>,
158     /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
159     pub pseudos: EagerPseudoStyles,
160 }
161 
162 impl ElementStyles {
163     /// Returns the primary style.
164     pub fn get_primary(&self) -> Option<&Arc<ComputedValues>> {
165         self.primary.as_ref()
166     }
167 
168     /// Returns the primary style.  Panic if no style available.
169     pub fn primary(&self) -> &Arc<ComputedValues> {
170         self.primary.as_ref().unwrap()
171     }
172 
173     /// Whether this element `display` value is `none`.
174     pub fn is_display_none(&self) -> bool {
175         self.primary().get_box().clone_display().is_none()
176     }
177 
178     /// Whether this element uses viewport units.
psppire_scanf_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)179     pub fn uses_viewport_units(&self) -> bool {
180         use crate::computed_value_flags::ComputedValueFlags;
181 
182         if self.primary().flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
183             return true;
184         }
185 
186         for pseudo_style in self.pseudos.as_array() {
187             if let Some(ref pseudo_style) = pseudo_style {
188                 if pseudo_style.flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
189                     return true;
190                 }
191             }
192         }
193 
194         false
195     }
196 
197     #[cfg(feature = "gecko")]
198     fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
199         // As the method name suggests, we don't measures the ComputedValues
200         // here, because they are measured on the C++ side.
201 
202         // XXX: measure the EagerPseudoArray itself, but not the ComputedValues
203         // within it.
204 
205         0
206     }
207 }
psppire_scanf_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)208 
209 // We manually implement Debug for ElementStyles so that we can avoid the
210 // verbose stringification of every property in the ComputedValues. We
211 // substitute the rule node instead.
212 impl fmt::Debug for ElementStyles {
213     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214         write!(
215             f,
216             "ElementStyles {{ primary: {:?}, pseudos: {:?} }}",
217             self.primary.as_ref().map(|x| &x.rules),
218             self.pseudos
219         )
220     }
221 }
222 
223 /// Style system data associated with an Element.
224 ///
225 /// In Gecko, this hangs directly off the Element. Servo, this is embedded
226 /// inside of layout data, which itself hangs directly off the Element. In
227 /// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
228 #[derive(Debug, Default)]
229 pub struct ElementData {
230     /// The styles for the element and its pseudo-elements.
231     pub styles: ElementStyles,
232 
233     /// The restyle damage, indicating what kind of layout changes are required
234     /// afte restyling.
235     pub damage: RestyleDamage,
236 
237     /// The restyle hint, which indicates whether selectors need to be rematched
238     /// for this element, its children, and its descendants.
psppire_scanf_dispose(GObject * obj)239     pub hint: RestyleHint,
240 
241     /// Flags.
242     pub flags: ElementDataFlags,
243 }
244 
245 /// The kind of restyle that a single element should do.
246 #[derive(Debug)]
247 pub enum RestyleKind {
248     /// We need to run selector matching plus re-cascade, that is, a full
249     /// restyle.
250     MatchAndCascade,
251     /// We need to recascade with some replacement rule, such as the style
252     /// attribute, or animation rules.
253     CascadeWithReplacements(RestyleHint),
254     /// We only need to recascade, for example, because only inherited
255     /// properties in the parent changed.
256     CascadeOnly,
257 }
258 
259 impl ElementData {
260     /// Invalidates style for this element, its descendants, and later siblings,
261     /// based on the snapshot of the element that we took when attributes or
262     /// state changed.
263     pub fn invalidate_style_if_needed<'a, E: TElement>(
264         &mut self,
265         element: E,
266         shared_context: &SharedStyleContext,
267         stack_limit_checker: Option<&StackLimitChecker>,
268         nth_index_cache: &mut NthIndexCache,
psppire_scanf_class_init(PsppireScanfClass * class)269     ) -> InvalidationResult {
270         // In animation-only restyle we shouldn't touch snapshot at all.
271         if shared_context.traversal_flags.for_animation_only() {
272             return InvalidationResult::empty();
273         }
274 
275         use crate::invalidation::element::invalidator::TreeStyleInvalidator;
276         use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor;
277 
278         debug!(
279             "invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
280              handled_snapshot: {}, pseudo: {:?}",
281             element,
282             shared_context.traversal_flags,
283             element.has_snapshot(),
284             element.handled_snapshot(),
285             element.implemented_pseudo_element()
286         );
287 
288         if !element.has_snapshot() || element.handled_snapshot() {
289             return InvalidationResult::empty();
290         }
291 
292         let mut processor =
293             StateAndAttrInvalidationProcessor::new(shared_context, element, self, nth_index_cache);
294 
295         let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor);
296 
297         let result = invalidator.invalidate();
298 
299         unsafe { element.set_handled_snapshot() }
300         debug_assert!(element.handled_snapshot());
301 
302         result
303     }
304 
305     /// Returns true if this element has styles.
306     #[inline]
307     pub fn has_styles(&self) -> bool {
308         self.styles.primary.is_some()
309     }
310 
311     /// Returns this element's styles as resolved styles to use for sharing.
312     pub fn share_styles(&self) -> ResolvedElementStyles {
313         ResolvedElementStyles {
314             primary: self.share_primary_style(),
315             pseudos: self.styles.pseudos.clone(),
316         }
317     }
318 
319     /// Returns this element's primary style as a resolved style to use for sharing.
320     pub fn share_primary_style(&self) -> PrimaryStyle {
321         let reused_via_rule_node = self
322             .flags
323             .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
324 
325         PrimaryStyle {
326             style: ResolvedStyle(self.styles.primary().clone()),
327             reused_via_rule_node,
328         }
329     }
330 
331     /// Sets a new set of styles, returning the old ones.
psppire_scanf_init(PsppireScanf * w)332     pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {
333         if new_styles.primary.reused_via_rule_node {
334             self.flags
335                 .insert(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
336         } else {
337             self.flags
338                 .remove(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
339         }
340         mem::replace(&mut self.styles, new_styles.into())
341     }
342 
343     /// Returns the kind of restyling that we're going to need to do on this
344     /// element, based of the stored restyle hint.
345     pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> RestyleKind {
346         if shared_context.traversal_flags.for_animation_only() {
psppire_scanf_get_child(PsppireScanf * w,gint n)347             return self.restyle_kind_for_animation(shared_context);
348         }
349 
350         if !self.has_styles() {
351             return RestyleKind::MatchAndCascade;
352         }
353 
354         if self.hint.match_self() {
355             return RestyleKind::MatchAndCascade;
356         }
357 
358         if self.hint.has_replacements() {
359             debug_assert!(
360                 !self.hint.has_animation_hint(),
361                 "Animation only restyle hint should have already processed"
362             );
363             return RestyleKind::CascadeWithReplacements(self.hint & RestyleHint::replacements());
psppire_scanf_new(const gchar * fmt,...)364         }
365 
366         debug_assert!(
367             self.hint.has_recascade_self(),
368             "We definitely need to do something: {:?}!",
369             self.hint
370         );
371         return RestyleKind::CascadeOnly;
372     }
373 
374     /// Returns the kind of restyling for animation-only restyle.
375     fn restyle_kind_for_animation(&self, shared_context: &SharedStyleContext) -> RestyleKind {
376         debug_assert!(shared_context.traversal_flags.for_animation_only());
377         debug_assert!(
378             self.has_styles(),
379             "Unstyled element shouldn't be traversed during \
380              animation-only traversal"
381         );
382 
383         // return either CascadeWithReplacements or CascadeOnly in case of
384         // animation-only restyle. I.e. animation-only restyle never does
385         // selector matching.
386         if self.hint.has_animation_hint() {
387             return RestyleKind::CascadeWithReplacements(self.hint & RestyleHint::for_animations());
388         }
389 
390         return RestyleKind::CascadeOnly;
391     }
392 
393     /// Drops any restyle state from the element.
394     ///
395     /// FIXME(bholley): The only caller of this should probably just assert that
396     /// the hint is empty and call clear_flags_and_damage().
397     #[inline]
398     pub fn clear_restyle_state(&mut self) {
399         self.hint = RestyleHint::empty();
400         self.clear_restyle_flags_and_damage();
401     }
402 
403     /// Drops restyle flags and damage from the element.
404     #[inline]
405     pub fn clear_restyle_flags_and_damage(&mut self) {
406         self.damage = RestyleDamage::empty();
407         self.flags.remove(ElementDataFlags::WAS_RESTYLED);
408     }
409 
410     /// Mark this element as restyled, which is useful to know whether we need
411     /// to do a post-traversal.
412     pub fn set_restyled(&mut self) {
413         self.flags.insert(ElementDataFlags::WAS_RESTYLED);
414         self.flags
415             .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
416     }
417 
418     /// Returns true if this element was restyled.
419     #[inline]
420     pub fn is_restyle(&self) -> bool {
421         self.flags.contains(ElementDataFlags::WAS_RESTYLED)
422     }
423 
424     /// Mark that we traversed this element without computing any style for it.
425     pub fn set_traversed_without_styling(&mut self) {
426         self.flags
427             .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
428     }
429 
430     /// Returns whether this element has been part of a restyle.
431     #[inline]
432     pub fn contains_restyle_data(&self) -> bool {
433         self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
434     }
435 
436     /// Returns whether it is safe to perform cousin sharing based on the ComputedValues
437     /// identity of the primary style in this ElementData. There are a few subtle things
438     /// to check.
439     ///
440     /// First, if a parent element was already styled and we traversed past it without
441     /// restyling it, that may be because our clever invalidation logic was able to prove
442     /// that the styles of that element would remain unchanged despite changes to the id
443     /// or class attributes. However, style sharing relies on the strong guarantee that all
444     /// the classes and ids up the respective parent chains are identical. As such, if we
445     /// skipped styling for one (or both) of the parents on this traversal, we can't share
446     /// styles across cousins. Note that this is a somewhat conservative check. We could
447     /// tighten it by having the invalidation logic explicitly flag elements for which it
448     /// ellided styling.
449     ///
450     /// Second, we want to only consider elements whose ComputedValues match due to a hit
451     /// in the style sharing cache, rather than due to the rule-node-based reuse that
452     /// happens later in the styling pipeline. The former gives us the stronger guarantees
453     /// we need for style sharing, the latter does not.
454     pub fn safe_for_cousin_sharing(&self) -> bool {
455         !self.flags.intersects(
456             ElementDataFlags::TRAVERSED_WITHOUT_STYLING |
457                 ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
458         )
459     }
460 
461     /// Measures memory usage.
462     #[cfg(feature = "gecko")]
463     pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
464         let n = self.styles.size_of_excluding_cvs(ops);
465 
466         // We may measure more fields in the future if DMD says it's worth it.
467 
468         n
469     }
470 }
471