1use crate::derives::*;
8use crate::device::Device;
9use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
10use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
11use crate::stylesheets::{
12 CssRule, CssRuleRef, CustomMediaMap, Origin, OriginSet, PerOrigin, StylesheetInDocument,
13};
14use std::mem;
15
16#[derive(MallocSizeOf)]
18struct StylesheetSetEntry<S>
19where
20 S: StylesheetInDocument + PartialEq + 'static,
21{
22 sheet: S,
24
25 committed: bool,
27}
28
29impl<S> StylesheetSetEntry<S>
30where
31 S: StylesheetInDocument + PartialEq + 'static,
32{
33 fn new(sheet: S) -> Self {
34 Self {
35 sheet,
36 committed: false,
37 }
38 }
39}
40
41#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
43pub enum DataValidity {
44 #[default]
47 Valid = 0,
48
49 CascadeInvalid = 1,
52
53 FullyInvalid = 2,
55}
56
57pub struct DocumentStylesheetFlusher<'a, S>
59where
60 S: StylesheetInDocument + PartialEq + 'static,
61{
62 collections: &'a mut PerOrigin<SheetCollection<S>>,
63}
64
65#[derive(Clone, Copy, Debug)]
67pub enum SheetRebuildKind {
68 Full,
70 CascadeOnly,
72}
73
74impl SheetRebuildKind {
75 pub fn should_rebuild_invalidation(&self) -> bool {
77 matches!(*self, SheetRebuildKind::Full)
78 }
79}
80
81impl<'a, S> DocumentStylesheetFlusher<'a, S>
82where
83 S: StylesheetInDocument + PartialEq + 'static,
84{
85 pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<'_, S> {
87 self.collections.borrow_mut_for_origin(&origin).flush()
88 }
89
90 pub fn origin_sheets(&self, origin: Origin) -> impl Iterator<Item = &S> {
94 self.collections.borrow_for_origin(&origin).iter()
95 }
96}
97
98pub struct SheetCollectionFlusher<'a, S>
101where
102 S: StylesheetInDocument + PartialEq + 'static,
103{
104 entries: &'a mut [StylesheetSetEntry<S>],
107 validity: DataValidity,
108 dirty: bool,
109}
110
111impl<'a, S> SheetCollectionFlusher<'a, S>
112where
113 S: StylesheetInDocument + PartialEq + 'static,
114{
115 #[inline]
117 pub fn dirty(&self) -> bool {
118 self.dirty
119 }
120
121 #[inline]
123 pub fn data_validity(&self) -> DataValidity {
124 self.validity
125 }
126
127 pub fn sheets(&self) -> impl Iterator<Item = &S> {
129 self.entries.iter().map(|entry| &entry.sheet)
130 }
131}
132
133impl<'a, S> SheetCollectionFlusher<'a, S>
134where
135 S: StylesheetInDocument + PartialEq + 'static,
136{
137 pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
145 for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
146 let committed = mem::replace(&mut potential_sheet.committed, true);
147 let rebuild_kind = if !committed {
148 SheetRebuildKind::Full
151 } else {
152 match self.validity {
153 DataValidity::Valid => continue,
154 DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
155 DataValidity::FullyInvalid => SheetRebuildKind::Full,
156 }
157 };
158
159 if !callback(index, &potential_sheet.sheet, rebuild_kind) {
160 return;
161 }
162 }
163 }
164}
165
166#[derive(MallocSizeOf)]
167struct SheetCollection<S>
168where
169 S: StylesheetInDocument + PartialEq + 'static,
170{
171 entries: Vec<StylesheetSetEntry<S>>,
176
177 data_validity: DataValidity,
184
185 dirty: bool,
189}
190
191impl<S> Default for SheetCollection<S>
192where
193 S: StylesheetInDocument + PartialEq + 'static,
194{
195 fn default() -> Self {
196 Self {
197 entries: vec![],
198 data_validity: DataValidity::Valid,
199 dirty: false,
200 }
201 }
202}
203
204impl<S> SheetCollection<S>
205where
206 S: StylesheetInDocument + PartialEq + 'static,
207{
208 fn len(&self) -> usize {
210 self.entries.len()
211 }
212
213 fn get(&self, index: usize) -> Option<&S> {
215 self.entries.get(index).map(|e| &e.sheet)
216 }
217
218 fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
219 let rev_pos = self
220 .entries
221 .iter()
222 .rev()
223 .position(|entry| entry.sheet == *sheet);
224 rev_pos.map(|i| self.entries.len() - i - 1)
225 }
226
227 fn remove(&mut self, sheet: &S) -> bool {
230 let index = self.find_sheet_index(sheet);
231 if cfg!(feature = "gecko") && index.is_none() {
232 return false;
234 }
235 let sheet = self.entries.remove(index.unwrap());
236 if sheet.committed {
244 self.set_data_validity_at_least(DataValidity::FullyInvalid);
245 } else {
246 self.dirty = true;
247 }
248 sheet.committed
249 }
250
251 fn contains(&self, sheet: &S) -> bool {
252 self.entries.iter().any(|e| e.sheet == *sheet)
253 }
254
255 fn append(&mut self, sheet: S) {
257 debug_assert!(!self.contains(&sheet));
258 self.entries.push(StylesheetSetEntry::new(sheet));
259 self.dirty = true;
265 }
266
267 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
268 debug_assert!(!self.contains(&sheet));
269
270 let index = self
271 .find_sheet_index(before_sheet)
272 .expect("`before_sheet` stylesheet not found");
273
274 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
277 self.entries.insert(index, StylesheetSetEntry::new(sheet));
278 }
279
280 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
281 use std::cmp;
282
283 debug_assert_ne!(validity, DataValidity::Valid);
284
285 self.dirty = true;
286 self.data_validity = cmp::max(validity, self.data_validity);
287 }
288
289 fn iter(&self) -> impl Iterator<Item = &S> {
291 self.entries.iter().map(|e| &e.sheet)
292 }
293
294 fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
296 self.entries.iter_mut().map(|e| &mut e.sheet)
297 }
298
299 fn collect_pending_invalidations(
302 &self,
303 invalidations: &mut StylesheetInvalidationSet,
304 device: &Device,
305 custom_media: &CustomMediaMap,
306 guard: &SharedRwLockReadGuard,
307 ) {
308 if !self.dirty {
309 return;
310 }
311 for entry in &self.entries {
312 if entry.committed {
313 continue;
314 }
315 invalidations.collect_invalidations_for(device, custom_media, &entry.sheet, guard);
316 }
317 }
318
319 fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
320 let dirty = mem::replace(&mut self.dirty, false);
321 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
322
323 SheetCollectionFlusher {
324 entries: &mut self.entries,
325 dirty,
326 validity,
327 }
328 }
329}
330
331#[derive(MallocSizeOf)]
333pub struct DocumentStylesheetSet<S>
334where
335 S: StylesheetInDocument + PartialEq + 'static,
336{
337 collections: PerOrigin<SheetCollection<S>>,
339
340 invalidations: StylesheetInvalidationSet,
342}
343
344macro_rules! sheet_set_methods {
351 ($set_name:expr) => {
352 fn collect_invalidations_for(
353 &mut self,
354 device: Option<&Device>,
355 custom_media: &CustomMediaMap,
356 sheet: &S,
357 guard: &SharedRwLockReadGuard,
358 ) {
359 if let Some(device) = device {
360 self.invalidations
361 .collect_invalidations_for(device, custom_media, sheet, guard);
362 }
363 }
364
365 pub fn append_stylesheet(&mut self, sheet: S, guard: &SharedRwLockReadGuard) {
369 debug!(concat!($set_name, "::append_stylesheet"));
370 let collection = self.collection_for(&sheet, guard);
371 collection.append(sheet);
372 }
373
374 pub fn insert_stylesheet_before(
378 &mut self,
379 sheet: S,
380 before_sheet: S,
381 guard: &SharedRwLockReadGuard,
382 ) {
383 debug!(concat!($set_name, "::insert_stylesheet_before"));
384 let collection = self.collection_for(&sheet, guard);
385 collection.insert_before(sheet, &before_sheet);
386 }
387
388 pub fn remove_stylesheet(
392 &mut self,
393 device: Option<&Device>,
394 custom_media: &CustomMediaMap,
395 sheet: S,
396 guard: &SharedRwLockReadGuard,
397 ) {
398 debug!(concat!($set_name, "::remove_stylesheet"));
399 let collection = self.collection_for(&sheet, guard);
400 if collection.remove(&sheet) {
403 self.collect_invalidations_for(device, custom_media, &sheet, guard);
404 }
405 }
406
407 pub fn rule_changed(
410 &mut self,
411 device: Option<&Device>,
412 custom_media: &CustomMediaMap,
413 sheet: &S,
414 rule: &CssRule,
415 guard: &SharedRwLockReadGuard,
416 change_kind: RuleChangeKind,
417 ancestors: &[CssRuleRef],
418 ) {
419 if let Some(device) = device {
420 let quirks_mode = device.quirks_mode();
421 self.invalidations.rule_changed(
422 sheet,
423 rule,
424 guard,
425 device,
426 quirks_mode,
427 custom_media,
428 change_kind,
429 ancestors,
430 );
431 }
432
433 let validity = match change_kind {
434 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
438 DataValidity::FullyInvalid
439 },
440 RuleChangeKind::PositionTryDeclarations | RuleChangeKind::StyleRuleDeclarations => {
455 DataValidity::FullyInvalid
456 },
457 };
458
459 let collection = self.collection_for(&sheet, guard);
460 collection.set_data_validity_at_least(validity);
461 }
462 };
463}
464
465impl<S> Default for DocumentStylesheetSet<S>
466where
467 S: StylesheetInDocument + PartialEq + 'static,
468{
469 fn default() -> Self {
470 Self::new()
471 }
472}
473
474impl<S> DocumentStylesheetSet<S>
475where
476 S: StylesheetInDocument + PartialEq + 'static,
477{
478 pub fn new() -> Self {
480 Self {
481 collections: Default::default(),
482 invalidations: StylesheetInvalidationSet::new(),
483 }
484 }
485
486 fn collection_for(
487 &mut self,
488 sheet: &S,
489 guard: &SharedRwLockReadGuard,
490 ) -> &mut SheetCollection<S> {
491 let origin = sheet.contents(guard).origin;
492 self.collections.borrow_mut_for_origin(&origin)
493 }
494
495 sheet_set_methods!("DocumentStylesheetSet");
496
497 pub fn len(&self) -> usize {
499 self.collections
500 .iter_origins()
501 .fold(0, |s, (item, _)| s + item.len())
502 }
503
504 #[inline]
506 pub fn sheet_count(&self, origin: Origin) -> usize {
507 self.collections.borrow_for_origin(&origin).len()
508 }
509
510 #[inline]
512 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
513 self.collections.borrow_for_origin(&origin).get(index)
514 }
515
516 pub fn has_changed(&self) -> bool {
518 !self.invalidations.is_empty()
519 || self
520 .collections
521 .iter_origins()
522 .any(|(collection, _)| collection.dirty)
523 }
524
525 pub fn flush<'a>(
531 &mut self,
532 device: &Device,
533 guards: &StylesheetGuards,
534 custom_media_for_origin: impl Fn(Origin) -> &'a CustomMediaMap,
535 ) -> (DocumentStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
536 debug!("DocumentStylesheetSet::flush");
537 for (collection, origin) in self.collections.iter_origins() {
538 collection.collect_pending_invalidations(
539 &mut self.invalidations,
540 device,
541 custom_media_for_origin(origin),
542 guards.for_origin(origin),
543 );
544 }
545 (
546 DocumentStylesheetFlusher {
547 collections: &mut self.collections,
548 },
549 std::mem::take(&mut self.invalidations),
550 )
551 }
552
553 #[cfg(feature = "servo")]
555 pub fn flush_without_invalidation(&mut self) -> OriginSet {
556 debug!("DocumentStylesheetSet::flush_without_invalidation");
557
558 let mut origins = OriginSet::empty();
559 std::mem::take(&mut self.invalidations);
560
561 for (collection, origin) in self.collections.iter_mut_origins() {
562 if collection.flush().dirty() {
563 origins |= origin;
564 }
565 }
566
567 origins
568 }
569
570 pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
572 self.collections
573 .iter_origins()
574 .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
575 }
576
577 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
579 self.collections
580 .iter_mut_origins()
581 .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
582 }
583
584 pub fn force_dirty(&mut self, origins: OriginSet) {
587 self.invalidations.invalidate_fully();
588 for origin in origins.iter_origins() {
589 self.collections
591 .borrow_mut_for_origin(&origin)
592 .set_data_validity_at_least(DataValidity::FullyInvalid);
593 }
594 }
595}
596
597#[derive(MallocSizeOf)]
599pub struct AuthorStylesheetSet<S>
600where
601 S: StylesheetInDocument + PartialEq + 'static,
602{
603 collection: SheetCollection<S>,
605 invalidations: StylesheetInvalidationSet,
607}
608
609pub struct AuthorStylesheetFlusher<'a, S>
611where
612 S: StylesheetInDocument + PartialEq + 'static,
613{
614 pub sheets: SheetCollectionFlusher<'a, S>,
616}
617
618impl<S> Default for AuthorStylesheetSet<S>
619where
620 S: StylesheetInDocument + PartialEq + 'static,
621{
622 fn default() -> Self {
623 Self::new()
624 }
625}
626
627impl<S> AuthorStylesheetSet<S>
628where
629 S: StylesheetInDocument + PartialEq + 'static,
630{
631 #[inline]
633 pub fn new() -> Self {
634 Self {
635 collection: Default::default(),
636 invalidations: StylesheetInvalidationSet::new(),
637 }
638 }
639
640 pub fn dirty(&self) -> bool {
642 self.collection.dirty
643 }
644
645 pub fn is_empty(&self) -> bool {
647 self.collection.len() == 0
648 }
649
650 pub fn get(&self, index: usize) -> Option<&S> {
652 self.collection.get(index)
653 }
654
655 pub fn len(&self) -> usize {
657 self.collection.len()
658 }
659
660 fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
661 &mut self.collection
662 }
663
664 sheet_set_methods!("AuthorStylesheetSet");
665
666 pub fn iter(&self) -> impl Iterator<Item = &S> {
668 self.collection.iter()
669 }
670
671 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
673 self.collection.iter_mut()
674 }
675
676 pub fn force_dirty(&mut self) {
678 self.invalidations.invalidate_fully();
679 self.collection
680 .set_data_validity_at_least(DataValidity::FullyInvalid);
681 }
682
683 pub fn flush(
687 &mut self,
688 device: Option<&Device>,
689 custom_media: &CustomMediaMap,
690 guard: &SharedRwLockReadGuard,
691 ) -> (AuthorStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
692 if let Some(device) = device {
693 self.collection.collect_pending_invalidations(
694 &mut self.invalidations,
695 device,
696 custom_media,
697 guard,
698 );
699 }
700 (
701 AuthorStylesheetFlusher {
702 sheets: self.collection.flush(),
703 },
704 std::mem::take(&mut self.invalidations),
705 )
706 }
707}