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