1#![deny(unsafe_code)]
9
10use crate::context::QuirksMode;
11use crate::dom::{TDocument, TElement, TNode};
12use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
13use crate::invalidation::element::restyle_hints::RestyleHint;
14use crate::media_queries::Device;
15use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap};
16use crate::shared_lock::SharedRwLockReadGuard;
17use crate::stylesheets::{CssRule, StylesheetInDocument};
18use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
19use crate::simple_buckets_map::SimpleBucketsMap;
20use crate::values::AtomIdent;
21use crate::LocalName as SelectorLocalName;
22use selectors::parser::{Component, LocalName, Selector};
23
24#[repr(u32)]
26#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
27pub enum RuleChangeKind {
28 Insertion,
30 Removal,
32 Generic,
35 StyleRuleDeclarations,
37}
38
39#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
44enum Invalidation {
45 ID(AtomIdent),
47 Class(AtomIdent),
49 LocalName {
51 name: SelectorLocalName,
52 lower_name: SelectorLocalName,
53 },
54}
55
56impl Invalidation {
57 fn is_id(&self) -> bool {
58 matches!(*self, Invalidation::ID(..))
59 }
60
61 fn is_id_or_class(&self) -> bool {
62 matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))
63 }
64}
65
66#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
69enum InvalidationKind {
70 None = 0,
71 Element,
72 Scope,
73}
74
75impl std::ops::BitOrAssign for InvalidationKind {
76 #[inline]
77 fn bitor_assign(&mut self, other: Self) {
78 *self = std::cmp::max(*self, other);
79 }
80}
81
82impl InvalidationKind {
83 #[inline]
84 fn is_scope(self) -> bool {
85 matches!(self, Self::Scope)
86 }
87
88 #[inline]
89 fn add(&mut self, other: Option<&InvalidationKind>) {
90 if let Some(other) = other {
91 *self |= *other;
92 }
93 }
94}
95
96#[derive(Debug, Default, MallocSizeOf)]
101pub struct StylesheetInvalidationSet {
102 buckets: SimpleBucketsMap<InvalidationKind>,
103 fully_invalid: bool,
104}
105
106impl StylesheetInvalidationSet {
107 pub fn new() -> Self {
109 Default::default()
110 }
111
112 pub fn invalidate_fully(&mut self) {
114 debug!("StylesheetInvalidationSet::invalidate_fully");
115 self.clear();
116 self.fully_invalid = true;
117 }
118
119 fn shrink_if_needed(&mut self) {
120 if self.fully_invalid {
121 return;
122 }
123 self.buckets.shrink_if_needed();
124 }
125
126 pub fn collect_invalidations_for<S>(
130 &mut self,
131 device: &Device,
132 stylesheet: &S,
133 guard: &SharedRwLockReadGuard,
134 ) where
135 S: StylesheetInDocument,
136 {
137 debug!("StylesheetInvalidationSet::collect_invalidations_for");
138 if self.fully_invalid {
139 debug!(" > Fully invalid already");
140 return;
141 }
142
143 if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
144 debug!(" > Stylesheet was not effective");
145 return; }
147
148 let quirks_mode = device.quirks_mode();
149 for rule in stylesheet.effective_rules(device, guard) {
150 self.collect_invalidations_for_rule(
151 rule,
152 guard,
153 device,
154 quirks_mode,
155 false,
156 );
157 if self.fully_invalid {
158 break;
159 }
160 }
161
162 self.shrink_if_needed();
163
164 debug!(" > resulting class invalidations: {:?}", self.buckets.classes);
165 debug!(" > resulting id invalidations: {:?}", self.buckets.ids);
166 debug!(
167 " > resulting local name invalidations: {:?}",
168 self.buckets.local_names
169 );
170 debug!(" > fully_invalid: {}", self.fully_invalid);
171 }
172
173 pub fn flush<E>(&mut self, document_element: Option<E>, snapshots: Option<&SnapshotMap>) -> bool
178 where
179 E: TElement,
180 {
181 debug!(
182 "Stylist::flush({:?}, snapshots: {})",
183 document_element,
184 snapshots.is_some()
185 );
186 let have_invalidations = match document_element {
187 Some(e) => self.process_invalidations(e, snapshots),
188 None => false,
189 };
190 self.clear();
191 have_invalidations
192 }
193
194 pub fn is_empty(&self) -> bool {
196 !self.fully_invalid &&
197 self.buckets.is_empty()
198 }
199
200 fn invalidation_kind_for<E>(
201 &self,
202 element: E,
203 snapshot: Option<&Snapshot>,
204 quirks_mode: QuirksMode,
205 ) -> InvalidationKind
206 where
207 E: TElement,
208 {
209 debug_assert!(!self.fully_invalid);
210
211 let mut kind = InvalidationKind::None;
212
213 if !self.buckets.classes.is_empty() {
214 element.each_class(|c| {
215 kind.add(self.buckets.classes.get(c, quirks_mode));
216 });
217
218 if kind.is_scope() {
219 return kind;
220 }
221
222 if let Some(snapshot) = snapshot {
223 snapshot.each_class(|c| {
224 kind.add(self.buckets.classes.get(c, quirks_mode));
225 });
226
227 if kind.is_scope() {
228 return kind;
229 }
230 }
231 }
232
233 if !self.buckets.ids.is_empty() {
234 if let Some(ref id) = element.id() {
235 kind.add(self.buckets.ids.get(id, quirks_mode));
236 if kind.is_scope() {
237 return kind;
238 }
239 }
240
241 if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {
242 kind.add(self.buckets.ids.get(old_id, quirks_mode));
243 if kind.is_scope() {
244 return kind;
245 }
246 }
247 }
248
249 if !self.buckets.local_names.is_empty() {
250 kind.add(self.buckets.local_names.get(element.local_name()));
251 }
252
253 kind
254 }
255
256 pub fn clear(&mut self) {
258 self.buckets.clear();
259 self.fully_invalid = false;
260 debug_assert!(self.is_empty());
261 }
262
263 fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
264 where
265 E: TElement,
266 {
267 debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
268
269 {
270 let mut data = match element.mutate_data() {
271 Some(data) => data,
272 None => return false,
273 };
274
275 if self.fully_invalid {
276 debug!("process_invalidations: fully_invalid({:?})", element);
277 data.hint.insert(RestyleHint::restyle_subtree());
278 return true;
279 }
280 }
281
282 if self.is_empty() {
283 debug!("process_invalidations: empty invalidation set");
284 return false;
285 }
286
287 let quirks_mode = element.as_node().owner_doc().quirks_mode();
288 self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
289 }
290
291 #[allow(unsafe_code)]
297 fn process_invalidations_in_subtree<E>(
298 &self,
299 element: E,
300 snapshots: Option<&SnapshotMap>,
301 quirks_mode: QuirksMode,
302 ) -> bool
303 where
304 E: TElement,
305 {
306 debug!("process_invalidations_in_subtree({:?})", element);
307 let mut data = match element.mutate_data() {
308 Some(data) => data,
309 None => return false,
310 };
311
312 if !data.has_styles() {
313 return false;
314 }
315
316 if data.hint.contains_subtree() {
317 debug!(
318 "process_invalidations_in_subtree: {:?} was already invalid",
319 element
320 );
321 return false;
322 }
323
324 let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
325 let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());
326
327 match self.invalidation_kind_for(element, snapshot, quirks_mode) {
328 InvalidationKind::None => {},
329 InvalidationKind::Element => {
330 debug!(
331 "process_invalidations_in_subtree: {:?} matched self",
332 element
333 );
334 data.hint.insert(RestyleHint::RESTYLE_SELF);
335 },
336 InvalidationKind::Scope => {
337 debug!(
338 "process_invalidations_in_subtree: {:?} matched subtree",
339 element
340 );
341 data.hint.insert(RestyleHint::restyle_subtree());
342 return true;
343 },
344 }
345
346 let mut any_children_invalid = false;
347
348 for child in element.traversal_children() {
349 let child = match child.as_element() {
350 Some(e) => e,
351 None => continue,
352 };
353
354 any_children_invalid |=
355 self.process_invalidations_in_subtree(child, snapshots, quirks_mode);
356 }
357
358 if any_children_invalid {
359 debug!(
360 "Children of {:?} changed, setting dirty descendants",
361 element
362 );
363 unsafe { element.set_dirty_descendants() }
364 }
365
366 data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid
367 }
368
369 fn scan_component(
372 component: &Component<SelectorImpl>,
373 invalidation: &mut Option<Invalidation>,
374 ) {
375 match *component {
376 Component::LocalName(LocalName {
377 ref name,
378 ref lower_name,
379 }) => {
380 if invalidation.is_none() {
381 *invalidation = Some(Invalidation::LocalName {
382 name: name.clone(),
383 lower_name: lower_name.clone(),
384 });
385 }
386 },
387 Component::Class(ref class) => {
388 if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
389 *invalidation = Some(Invalidation::Class(class.clone()));
390 }
391 },
392 Component::ID(ref id) => {
393 if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
394 *invalidation = Some(Invalidation::ID(id.clone()));
395 }
396 },
397 _ => {
398 },
400 }
401 }
402
403 fn collect_invalidations(
418 &mut self,
419 selector: &Selector<SelectorImpl>,
420 quirks_mode: QuirksMode,
421 ) {
422 debug!(
423 "StylesheetInvalidationSet::collect_invalidations({:?})",
424 selector
425 );
426
427 let mut element_invalidation: Option<Invalidation> = None;
428 let mut subtree_invalidation: Option<Invalidation> = None;
429
430 let mut scan_for_element_invalidation = true;
431 let mut scan_for_subtree_invalidation = false;
432
433 let mut iter = selector.iter();
434
435 loop {
436 for component in &mut iter {
437 if scan_for_element_invalidation {
438 Self::scan_component(component, &mut element_invalidation);
439 } else if scan_for_subtree_invalidation {
440 Self::scan_component(component, &mut subtree_invalidation);
441 }
442 }
443 match iter.next_sequence() {
444 None => break,
445 Some(combinator) => {
446 scan_for_subtree_invalidation = combinator.is_ancestor();
447 },
448 }
449 scan_for_element_invalidation = false;
450 }
451
452 if let Some(s) = subtree_invalidation {
453 debug!(" > Found subtree invalidation: {:?}", s);
454 if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
455 return;
456 }
457 }
458
459 if let Some(s) = element_invalidation {
460 debug!(" > Found element invalidation: {:?}", s);
461 if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
462 return;
463 }
464 }
465
466 debug!(" > Can't handle selector or OOMd, marking fully invalid");
469 self.invalidate_fully()
470 }
471
472 fn insert_invalidation(
473 &mut self,
474 invalidation: Invalidation,
475 kind: InvalidationKind,
476 quirks_mode: QuirksMode,
477 ) -> bool {
478 match invalidation {
479 Invalidation::Class(c) => {
480 let entry = match self.buckets.classes.try_entry(c.0, quirks_mode) {
481 Ok(e) => e,
482 Err(..) => return false,
483 };
484 *entry.or_insert(InvalidationKind::None) |= kind;
485 },
486 Invalidation::ID(i) => {
487 let entry = match self.buckets.ids.try_entry(i.0, quirks_mode) {
488 Ok(e) => e,
489 Err(..) => return false,
490 };
491 *entry.or_insert(InvalidationKind::None) |= kind;
492 },
493 Invalidation::LocalName { name, lower_name } => {
494 let insert_lower = name != lower_name;
495 if self.buckets.local_names.try_reserve(1).is_err() {
496 return false;
497 }
498 let entry = self.buckets.local_names.entry(name);
499 *entry.or_insert(InvalidationKind::None) |= kind;
500 if insert_lower {
501 if self.buckets.local_names.try_reserve(1).is_err() {
502 return false;
503 }
504 let entry = self.buckets.local_names.entry(lower_name);
505 *entry.or_insert(InvalidationKind::None) |= kind;
506 }
507 },
508 }
509
510 true
511 }
512
513 pub fn rule_changed<S>(
519 &mut self,
520 stylesheet: &S,
521 rule: &CssRule,
522 guard: &SharedRwLockReadGuard,
523 device: &Device,
524 quirks_mode: QuirksMode,
525 change_kind: RuleChangeKind,
526 ) where
527 S: StylesheetInDocument,
528 {
529 debug!("StylesheetInvalidationSet::rule_changed");
530 if self.fully_invalid {
531 return;
532 }
533
534 if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
535 debug!(" > Stylesheet was not effective");
536 return; }
538
539 let is_generic_change = change_kind == RuleChangeKind::Generic;
544 self.collect_invalidations_for_rule(rule, guard, device, quirks_mode, is_generic_change);
545 if self.fully_invalid {
546 return;
547 }
548
549 if !is_generic_change && !EffectiveRules::is_effective(guard, device, quirks_mode, rule) {
550 return;
551 }
552
553 let rules = EffectiveRulesIterator::effective_children(device, quirks_mode, guard, rule);
554 for rule in rules {
555 self.collect_invalidations_for_rule(
556 rule,
557 guard,
558 device,
559 quirks_mode,
560 false,
561 );
562 if self.fully_invalid {
563 break;
564 }
565 }
566 }
567
568 fn collect_invalidations_for_rule(
570 &mut self,
571 rule: &CssRule,
572 guard: &SharedRwLockReadGuard,
573 device: &Device,
574 quirks_mode: QuirksMode,
575 is_generic_change: bool,
576 ) {
577 use crate::stylesheets::CssRule::*;
578 debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
579 debug_assert!(!self.fully_invalid, "Not worth being here!");
580
581 match *rule {
582 Style(ref lock) => {
583 if is_generic_change {
584 return self.invalidate_fully();
590 }
591
592 let style_rule = lock.read_with(guard);
593 for selector in style_rule.selectors.slice() {
594 self.collect_invalidations(selector, quirks_mode);
595 if self.fully_invalid {
596 return;
597 }
598 }
599 },
600 NestedDeclarations(..) => {
601 },
603 Namespace(..) => {
604 },
607 LayerStatement(..) => {
608 return self.invalidate_fully();
611 },
612 Document(..) | Import(..) | Media(..) | Supports(..) | Container(..) |
613 LayerBlock(..) | StartingStyle(..) => {
614 },
616 FontFace(..) => {
617 },
620 Page(..) | Margin(..) => {
621 },
624 Keyframes(ref lock) => {
625 if is_generic_change {
626 return self.invalidate_fully();
627 }
628 let keyframes_rule = lock.read_with(guard);
629 if device.animation_name_may_be_referenced(&keyframes_rule.name) {
630 debug!(
631 " > Found @keyframes rule potentially referenced \
632 from the page, marking the whole tree invalid."
633 );
634 self.invalidate_fully();
635 } else {
636 }
638 },
639 CounterStyle(..) | Property(..) | FontFeatureValues(..) | FontPaletteValues(..) => {
640 debug!(" > Found unsupported rule, marking the whole subtree invalid.");
641 self.invalidate_fully();
642 },
643 Scope(..) => {
644 self.invalidate_fully();
647 },
648 PositionTry(..) => {
649 self.invalidate_fully();
652 },
653 }
654 }
655}