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, 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 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!(
165 " > resulting class invalidations: {:?}",
166 self.buckets.classes
167 );
168 debug!(" > resulting id invalidations: {:?}", self.buckets.ids);
169 debug!(
170 " > resulting local name invalidations: {:?}",
171 self.buckets.local_names
172 );
173 debug!(" > fully_invalid: {}", self.fully_invalid);
174 }
175
176 pub fn flush<E>(&mut self, document_element: Option<E>, snapshots: Option<&SnapshotMap>) -> bool
181 where
182 E: TElement,
183 {
184 debug!(
185 "Stylist::flush({:?}, snapshots: {})",
186 document_element,
187 snapshots.is_some()
188 );
189 let have_invalidations = match document_element {
190 Some(e) => self.process_invalidations(e, snapshots),
191 None => false,
192 };
193 self.clear();
194 have_invalidations
195 }
196
197 pub fn is_empty(&self) -> bool {
199 !self.fully_invalid && self.buckets.is_empty()
200 }
201
202 fn invalidation_kind_for<E>(
203 &self,
204 element: E,
205 snapshot: Option<&Snapshot>,
206 quirks_mode: QuirksMode,
207 ) -> InvalidationKind
208 where
209 E: TElement,
210 {
211 debug_assert!(!self.fully_invalid);
212
213 let mut kind = InvalidationKind::None;
214
215 if !self.buckets.classes.is_empty() {
216 element.each_class(|c| {
217 kind.add(self.buckets.classes.get(c, quirks_mode));
218 });
219
220 if kind.is_scope() {
221 return kind;
222 }
223
224 if let Some(snapshot) = snapshot {
225 snapshot.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 }
234
235 if !self.buckets.ids.is_empty() {
236 if let Some(ref id) = element.id() {
237 kind.add(self.buckets.ids.get(id, quirks_mode));
238 if kind.is_scope() {
239 return kind;
240 }
241 }
242
243 if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {
244 kind.add(self.buckets.ids.get(old_id, quirks_mode));
245 if kind.is_scope() {
246 return kind;
247 }
248 }
249 }
250
251 if !self.buckets.local_names.is_empty() {
252 kind.add(self.buckets.local_names.get(element.local_name()));
253 }
254
255 kind
256 }
257
258 pub fn clear(&mut self) {
260 self.buckets.clear();
261 self.fully_invalid = false;
262 debug_assert!(self.is_empty());
263 }
264
265 fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
266 where
267 E: TElement,
268 {
269 debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
270
271 {
272 let mut data = match element.mutate_data() {
273 Some(data) => data,
274 None => return false,
275 };
276
277 if self.fully_invalid {
278 debug!("process_invalidations: fully_invalid({:?})", element);
279 data.hint.insert(RestyleHint::restyle_subtree());
280 return true;
281 }
282 }
283
284 if self.is_empty() {
285 debug!("process_invalidations: empty invalidation set");
286 return false;
287 }
288
289 let quirks_mode = element.as_node().owner_doc().quirks_mode();
290 self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
291 }
292
293 #[allow(unsafe_code)]
299 fn process_invalidations_in_subtree<E>(
300 &self,
301 element: E,
302 snapshots: Option<&SnapshotMap>,
303 quirks_mode: QuirksMode,
304 ) -> bool
305 where
306 E: TElement,
307 {
308 debug!("process_invalidations_in_subtree({:?})", element);
309 let mut data = match element.mutate_data() {
310 Some(data) => data,
311 None => return false,
312 };
313
314 if !data.has_styles() {
315 return false;
316 }
317
318 if data.hint.contains_subtree() {
319 debug!(
320 "process_invalidations_in_subtree: {:?} was already invalid",
321 element
322 );
323 return false;
324 }
325
326 let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
327 let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());
328
329 match self.invalidation_kind_for(element, snapshot, quirks_mode) {
330 InvalidationKind::None => {},
331 InvalidationKind::Element => {
332 debug!(
333 "process_invalidations_in_subtree: {:?} matched self",
334 element
335 );
336 data.hint.insert(RestyleHint::RESTYLE_SELF);
337 },
338 InvalidationKind::Scope => {
339 debug!(
340 "process_invalidations_in_subtree: {:?} matched subtree",
341 element
342 );
343 data.hint.insert(RestyleHint::restyle_subtree());
344 return true;
345 },
346 }
347
348 let mut any_children_invalid = false;
349
350 for child in element.traversal_children() {
351 let child = match child.as_element() {
352 Some(e) => e,
353 None => continue,
354 };
355
356 any_children_invalid |=
357 self.process_invalidations_in_subtree(child, snapshots, quirks_mode);
358 }
359
360 if any_children_invalid {
361 debug!(
362 "Children of {:?} changed, setting dirty descendants",
363 element
364 );
365 unsafe { element.set_dirty_descendants() }
366 }
367
368 data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid
369 }
370
371 fn scan_component(
374 component: &Component<SelectorImpl>,
375 invalidation: &mut Option<Invalidation>,
376 ) {
377 match *component {
378 Component::LocalName(LocalName {
379 ref name,
380 ref lower_name,
381 }) => {
382 if invalidation.is_none() {
383 *invalidation = Some(Invalidation::LocalName {
384 name: name.clone(),
385 lower_name: lower_name.clone(),
386 });
387 }
388 },
389 Component::Class(ref class) => {
390 if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
391 *invalidation = Some(Invalidation::Class(class.clone()));
392 }
393 },
394 Component::ID(ref id) => {
395 if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
396 *invalidation = Some(Invalidation::ID(id.clone()));
397 }
398 },
399 _ => {
400 },
402 }
403 }
404
405 fn collect_invalidations(
420 &mut self,
421 selector: &Selector<SelectorImpl>,
422 quirks_mode: QuirksMode,
423 ) {
424 debug!(
425 "StylesheetInvalidationSet::collect_invalidations({:?})",
426 selector
427 );
428
429 let mut element_invalidation: Option<Invalidation> = None;
430 let mut subtree_invalidation: Option<Invalidation> = None;
431
432 let mut scan_for_element_invalidation = true;
433 let mut scan_for_subtree_invalidation = false;
434
435 let mut iter = selector.iter();
436
437 loop {
438 for component in &mut iter {
439 if scan_for_element_invalidation {
440 Self::scan_component(component, &mut element_invalidation);
441 } else if scan_for_subtree_invalidation {
442 Self::scan_component(component, &mut subtree_invalidation);
443 }
444 }
445 match iter.next_sequence() {
446 None => break,
447 Some(combinator) => {
448 scan_for_subtree_invalidation = combinator.is_ancestor();
449 },
450 }
451 scan_for_element_invalidation = false;
452 }
453
454 if let Some(s) = subtree_invalidation {
455 debug!(" > Found subtree invalidation: {:?}", s);
456 if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
457 return;
458 }
459 }
460
461 if let Some(s) = element_invalidation {
462 debug!(" > Found element invalidation: {:?}", s);
463 if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
464 return;
465 }
466 }
467
468 debug!(" > Can't handle selector or OOMd, marking fully invalid");
471 self.invalidate_fully()
472 }
473
474 fn insert_invalidation(
475 &mut self,
476 invalidation: Invalidation,
477 kind: InvalidationKind,
478 quirks_mode: QuirksMode,
479 ) -> bool {
480 match invalidation {
481 Invalidation::Class(c) => {
482 let entry = match self.buckets.classes.try_entry(c.0, quirks_mode) {
483 Ok(e) => e,
484 Err(..) => return false,
485 };
486 *entry.or_insert(InvalidationKind::None) |= kind;
487 },
488 Invalidation::ID(i) => {
489 let entry = match self.buckets.ids.try_entry(i.0, quirks_mode) {
490 Ok(e) => e,
491 Err(..) => return false,
492 };
493 *entry.or_insert(InvalidationKind::None) |= kind;
494 },
495 Invalidation::LocalName { name, lower_name } => {
496 let insert_lower = name != lower_name;
497 if self.buckets.local_names.try_reserve(1).is_err() {
498 return false;
499 }
500 let entry = self.buckets.local_names.entry(name);
501 *entry.or_insert(InvalidationKind::None) |= kind;
502 if insert_lower {
503 if self.buckets.local_names.try_reserve(1).is_err() {
504 return false;
505 }
506 let entry = self.buckets.local_names.entry(lower_name);
507 *entry.or_insert(InvalidationKind::None) |= kind;
508 }
509 },
510 }
511
512 true
513 }
514
515 pub fn rule_changed<S>(
521 &mut self,
522 stylesheet: &S,
523 rule: &CssRule,
524 guard: &SharedRwLockReadGuard,
525 device: &Device,
526 quirks_mode: QuirksMode,
527 change_kind: RuleChangeKind,
528 ) where
529 S: StylesheetInDocument,
530 {
531 debug!("StylesheetInvalidationSet::rule_changed");
532 if self.fully_invalid {
533 return;
534 }
535
536 if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
537 debug!(" > Stylesheet was not effective");
538 return; }
540
541 let is_generic_change = change_kind == RuleChangeKind::Generic;
546 self.collect_invalidations_for_rule(rule, guard, device, quirks_mode, is_generic_change);
547 if self.fully_invalid {
548 return;
549 }
550
551 if !is_generic_change && !EffectiveRules::is_effective(guard, device, quirks_mode, rule) {
552 return;
553 }
554
555 let rules = EffectiveRulesIterator::effective_children(device, quirks_mode, guard, rule);
556 for rule in rules {
557 self.collect_invalidations_for_rule(
558 rule,
559 guard,
560 device,
561 quirks_mode,
562 false,
563 );
564 if self.fully_invalid {
565 break;
566 }
567 }
568 }
569
570 fn collect_invalidations_for_rule(
572 &mut self,
573 rule: &CssRule,
574 guard: &SharedRwLockReadGuard,
575 device: &Device,
576 quirks_mode: QuirksMode,
577 is_generic_change: bool,
578 ) {
579 use crate::stylesheets::CssRule::*;
580 debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
581 debug_assert!(!self.fully_invalid, "Not worth being here!");
582
583 match *rule {
584 Style(ref lock) => {
585 if is_generic_change {
586 return self.invalidate_fully();
592 }
593
594 let style_rule = lock.read_with(guard);
595 for selector in style_rule.selectors.slice() {
596 self.collect_invalidations(selector, quirks_mode);
597 if self.fully_invalid {
598 return;
599 }
600 }
601 },
602 NestedDeclarations(..) => {
603 },
605 Namespace(..) => {
606 },
609 LayerStatement(..) => {
610 return self.invalidate_fully();
613 },
614 Document(..) | Import(..) | Media(..) | Supports(..) | Container(..)
615 | LayerBlock(..) | StartingStyle(..) => {
616 },
618 FontFace(..) => {
619 },
622 Page(..) | Margin(..) => {
623 },
626 Keyframes(ref lock) => {
627 if is_generic_change {
628 return self.invalidate_fully();
629 }
630 let keyframes_rule = lock.read_with(guard);
631 if device.animation_name_may_be_referenced(&keyframes_rule.name) {
632 debug!(
633 " > Found @keyframes rule potentially referenced \
634 from the page, marking the whole tree invalid."
635 );
636 self.invalidate_fully();
637 } else {
638 }
640 },
641 CounterStyle(..) | Property(..) | FontFeatureValues(..) | FontPaletteValues(..) => {
642 debug!(" > Found unsupported rule, marking the whole subtree invalid.");
643 self.invalidate_fully();
644 },
645 Scope(..) => {
646 self.invalidate_fully();
649 },
650 PositionTry(..) => {
651 self.invalidate_fully();
654 },
655 }
656 }
657}