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::simple_buckets_map::SimpleBucketsMap;
18use crate::stylesheets::{CssRule, CssRuleRef, CustomMediaMap, StylesheetInDocument};
19use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
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 Generic = 0,
31 Insertion,
33 Removal,
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 custom_media: &CustomMediaMap,
133 stylesheet: &S,
134 guard: &SharedRwLockReadGuard,
135 ) where
136 S: StylesheetInDocument,
137 {
138 debug!("StylesheetInvalidationSet::collect_invalidations_for");
139 if self.fully_invalid {
140 debug!(" > Fully invalid already");
141 return;
142 }
143
144 if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, custom_media, guard)
145 {
146 debug!(" > Stylesheet was not effective");
147 return; }
149
150 let quirks_mode = device.quirks_mode();
151 for rule in stylesheet
152 .contents(guard)
153 .effective_rules(device, custom_media, guard)
154 {
155 self.collect_invalidations_for_rule(
156 rule,
157 guard,
158 device,
159 quirks_mode,
160 false,
161 &[],
164 );
165 if self.fully_invalid {
166 break;
167 }
168 }
169
170 self.shrink_if_needed();
171
172 debug!(
173 " > resulting class invalidations: {:?}",
174 self.buckets.classes
175 );
176 debug!(" > resulting id invalidations: {:?}", self.buckets.ids);
177 debug!(
178 " > resulting local name invalidations: {:?}",
179 self.buckets.local_names
180 );
181 debug!(" > fully_invalid: {}", self.fully_invalid);
182 }
183
184 pub fn flush<E>(&mut self, document_element: Option<E>, snapshots: Option<&SnapshotMap>) -> bool
189 where
190 E: TElement,
191 {
192 debug!(
193 "Stylist::flush({:?}, snapshots: {})",
194 document_element,
195 snapshots.is_some()
196 );
197 let have_invalidations = match document_element {
198 Some(e) => self.process_invalidations(e, snapshots),
199 None => false,
200 };
201 self.clear();
202 have_invalidations
203 }
204
205 pub fn is_empty(&self) -> bool {
207 !self.fully_invalid && self.buckets.is_empty()
208 }
209
210 fn invalidation_kind_for<E>(
211 &self,
212 element: E,
213 snapshot: Option<&Snapshot>,
214 quirks_mode: QuirksMode,
215 ) -> InvalidationKind
216 where
217 E: TElement,
218 {
219 debug_assert!(!self.fully_invalid);
220
221 let mut kind = InvalidationKind::None;
222
223 if !self.buckets.classes.is_empty() {
224 element.each_class(|c| {
225 kind.add(self.buckets.classes.get(c, quirks_mode));
226 });
227
228 if kind.is_scope() {
229 return kind;
230 }
231
232 if let Some(snapshot) = snapshot {
233 snapshot.each_class(|c| {
234 kind.add(self.buckets.classes.get(c, quirks_mode));
235 });
236
237 if kind.is_scope() {
238 return kind;
239 }
240 }
241 }
242
243 if !self.buckets.ids.is_empty() {
244 if let Some(ref id) = element.id() {
245 kind.add(self.buckets.ids.get(id, quirks_mode));
246 if kind.is_scope() {
247 return kind;
248 }
249 }
250
251 if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {
252 kind.add(self.buckets.ids.get(old_id, quirks_mode));
253 if kind.is_scope() {
254 return kind;
255 }
256 }
257 }
258
259 if !self.buckets.local_names.is_empty() {
260 kind.add(self.buckets.local_names.get(element.local_name()));
261 }
262
263 kind
264 }
265
266 pub fn clear(&mut self) {
268 self.buckets.clear();
269 self.fully_invalid = false;
270 debug_assert!(self.is_empty());
271 }
272
273 fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
274 where
275 E: TElement,
276 {
277 debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
278
279 {
280 let mut data = match element.mutate_data() {
281 Some(data) => data,
282 None => return false,
283 };
284
285 if self.fully_invalid {
286 debug!("process_invalidations: fully_invalid({:?})", element);
287 data.hint.insert(RestyleHint::restyle_subtree());
288 return true;
289 }
290 }
291
292 if self.is_empty() {
293 debug!("process_invalidations: empty invalidation set");
294 return false;
295 }
296
297 let quirks_mode = element.as_node().owner_doc().quirks_mode();
298 self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
299 }
300
301 #[allow(unsafe_code)]
307 fn process_invalidations_in_subtree<E>(
308 &self,
309 element: E,
310 snapshots: Option<&SnapshotMap>,
311 quirks_mode: QuirksMode,
312 ) -> bool
313 where
314 E: TElement,
315 {
316 debug!("process_invalidations_in_subtree({:?})", element);
317 let mut data = match element.mutate_data() {
318 Some(data) => data,
319 None => return false,
320 };
321
322 if !data.has_styles() {
323 return false;
324 }
325
326 if data.hint.contains_subtree() {
327 debug!(
328 "process_invalidations_in_subtree: {:?} was already invalid",
329 element
330 );
331 return false;
332 }
333
334 let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
335 let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());
336
337 match self.invalidation_kind_for(element, snapshot, quirks_mode) {
338 InvalidationKind::None => {},
339 InvalidationKind::Element => {
340 debug!(
341 "process_invalidations_in_subtree: {:?} matched self",
342 element
343 );
344 data.hint.insert(RestyleHint::RESTYLE_SELF);
345 },
346 InvalidationKind::Scope => {
347 debug!(
348 "process_invalidations_in_subtree: {:?} matched subtree",
349 element
350 );
351 data.hint.insert(RestyleHint::restyle_subtree());
352 return true;
353 },
354 }
355
356 let mut any_children_invalid = false;
357
358 for child in element.traversal_children() {
359 let child = match child.as_element() {
360 Some(e) => e,
361 None => continue,
362 };
363
364 any_children_invalid |=
365 self.process_invalidations_in_subtree(child, snapshots, quirks_mode);
366 }
367
368 if any_children_invalid {
369 debug!(
370 "Children of {:?} changed, setting dirty descendants",
371 element
372 );
373 unsafe { element.set_dirty_descendants() }
374 }
375
376 data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid
377 }
378
379 fn scan_component(
382 component: &Component<SelectorImpl>,
383 invalidation: &mut Option<Invalidation>,
384 ) {
385 match *component {
386 Component::LocalName(LocalName {
387 ref name,
388 ref lower_name,
389 }) => {
390 if invalidation.is_none() {
391 *invalidation = Some(Invalidation::LocalName {
392 name: name.clone(),
393 lower_name: lower_name.clone(),
394 });
395 }
396 },
397 Component::Class(ref class) => {
398 if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
399 *invalidation = Some(Invalidation::Class(class.clone()));
400 }
401 },
402 Component::ID(ref id) => {
403 if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
404 *invalidation = Some(Invalidation::ID(id.clone()));
405 }
406 },
407 _ => {
408 },
410 }
411 }
412
413 fn collect_invalidations(
428 &mut self,
429 selector: &Selector<SelectorImpl>,
430 quirks_mode: QuirksMode,
431 ) {
432 debug!(
433 "StylesheetInvalidationSet::collect_invalidations({:?})",
434 selector
435 );
436
437 let mut element_invalidation: Option<Invalidation> = None;
438 let mut subtree_invalidation: Option<Invalidation> = None;
439
440 let mut scan_for_element_invalidation = true;
441 let mut scan_for_subtree_invalidation = false;
442
443 let mut iter = selector.iter();
444
445 loop {
446 for component in &mut iter {
447 if scan_for_element_invalidation {
448 Self::scan_component(component, &mut element_invalidation);
449 } else if scan_for_subtree_invalidation {
450 Self::scan_component(component, &mut subtree_invalidation);
451 }
452 }
453 match iter.next_sequence() {
454 None => break,
455 Some(combinator) => {
456 scan_for_subtree_invalidation = combinator.is_ancestor();
457 },
458 }
459 scan_for_element_invalidation = false;
460 }
461
462 if let Some(s) = subtree_invalidation {
463 debug!(" > Found subtree invalidation: {:?}", s);
464 if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
465 return;
466 }
467 }
468
469 if let Some(s) = element_invalidation {
470 debug!(" > Found element invalidation: {:?}", s);
471 if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
472 return;
473 }
474 }
475
476 debug!(" > Can't handle selector or OOMd, marking fully invalid");
479 self.invalidate_fully()
480 }
481
482 fn insert_invalidation(
483 &mut self,
484 invalidation: Invalidation,
485 kind: InvalidationKind,
486 quirks_mode: QuirksMode,
487 ) -> bool {
488 match invalidation {
489 Invalidation::Class(c) => {
490 let entry = match self.buckets.classes.try_entry(c.0, quirks_mode) {
491 Ok(e) => e,
492 Err(..) => return false,
493 };
494 *entry.or_insert(InvalidationKind::None) |= kind;
495 },
496 Invalidation::ID(i) => {
497 let entry = match self.buckets.ids.try_entry(i.0, quirks_mode) {
498 Ok(e) => e,
499 Err(..) => return false,
500 };
501 *entry.or_insert(InvalidationKind::None) |= kind;
502 },
503 Invalidation::LocalName { name, lower_name } => {
504 let insert_lower = name != lower_name;
505 if self.buckets.local_names.try_reserve(1).is_err() {
506 return false;
507 }
508 let entry = self.buckets.local_names.entry(name);
509 *entry.or_insert(InvalidationKind::None) |= kind;
510 if insert_lower {
511 if self.buckets.local_names.try_reserve(1).is_err() {
512 return false;
513 }
514 let entry = self.buckets.local_names.entry(lower_name);
515 *entry.or_insert(InvalidationKind::None) |= kind;
516 }
517 },
518 }
519
520 true
521 }
522
523 pub fn rule_changed<S>(
525 &mut self,
526 stylesheet: &S,
527 rule: &CssRule,
528 guard: &SharedRwLockReadGuard,
529 device: &Device,
530 quirks_mode: QuirksMode,
531 custom_media: &CustomMediaMap,
532 change_kind: RuleChangeKind,
533 ancestors: &[CssRuleRef],
534 ) where
535 S: StylesheetInDocument,
536 {
537 debug!("StylesheetInvalidationSet::rule_changed");
538 if self.fully_invalid {
539 return;
540 }
541
542 if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, custom_media, guard)
543 {
544 debug!(" > Stylesheet was not effective");
545 return; }
547
548 if ancestors
549 .iter()
550 .any(|r| !EffectiveRules::is_effective(guard, device, quirks_mode, custom_media, r))
551 {
552 debug!(" > Ancestor rules not effective");
553 return;
554 }
555
556 let is_generic_change = change_kind == RuleChangeKind::Generic;
561 self.collect_invalidations_for_rule(
562 rule,
563 guard,
564 device,
565 quirks_mode,
566 is_generic_change,
567 ancestors,
568 );
569 if self.fully_invalid {
570 return;
571 }
572
573 if !is_generic_change
574 && !EffectiveRules::is_effective(guard, device, quirks_mode, custom_media, &rule.into())
575 {
576 return;
577 }
578
579 let rules = EffectiveRulesIterator::effective_children(
580 device,
581 quirks_mode,
582 custom_media,
583 guard,
584 rule,
585 );
586 for rule in rules {
587 self.collect_invalidations_for_rule(
588 rule,
589 guard,
590 device,
591 quirks_mode,
592 false,
593 &[],
595 );
596 if self.fully_invalid {
597 break;
598 }
599 }
600 }
601
602 fn collect_invalidations_for_rule(
604 &mut self,
605 rule: &CssRule,
606 guard: &SharedRwLockReadGuard,
607 device: &Device,
608 quirks_mode: QuirksMode,
609 is_generic_change: bool,
610 ancestors: &[CssRuleRef],
611 ) {
612 use crate::stylesheets::CssRule::*;
613 debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
614 debug_assert!(!self.fully_invalid, "Not worth being here!");
615
616 match *rule {
617 Style(ref lock) => {
618 if is_generic_change {
619 return self.invalidate_fully();
625 }
626
627 let style_rule = lock.read_with(guard);
628 for selector in style_rule.selectors.slice() {
629 self.collect_invalidations(selector, quirks_mode);
630 if self.fully_invalid {
631 return;
632 }
633 }
634 },
635 NestedDeclarations(..) => {
636 if ancestors.iter().any(|r| matches!(r, CssRuleRef::Scope(_))) {
637 self.invalidate_fully();
638 }
639 },
640 Namespace(..) => {
641 },
644 LayerStatement(..) => {
645 return self.invalidate_fully();
648 },
649 Document(..) | Import(..) | Media(..) | Supports(..) | Container(..)
650 | LayerBlock(..) | StartingStyle(..) => {
651 },
653 FontFace(..) => {
654 },
657 Page(..) | Margin(..) => {
658 },
661 Keyframes(ref lock) => {
662 if is_generic_change {
663 return self.invalidate_fully();
664 }
665 let keyframes_rule = lock.read_with(guard);
666 if device.animation_name_may_be_referenced(&keyframes_rule.name) {
667 debug!(
668 " > Found @keyframes rule potentially referenced \
669 from the page, marking the whole tree invalid."
670 );
671 self.invalidate_fully();
672 } else {
673 }
675 },
676 CounterStyle(..) | Property(..) | FontFeatureValues(..) | FontPaletteValues(..) => {
677 debug!(" > Found unsupported rule, marking the whole subtree invalid.");
678 self.invalidate_fully();
679 },
680 Scope(..) => {
681 self.invalidate_fully();
684 },
685 PositionTry(..) => {
686 self.invalidate_fully();
689 },
690 CustomMedia(..) => {
691 self.invalidate_fully();
696 },
697 }
698 }
699}