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