1use crate::dom::TElement;
8use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
9use crate::media_queries::Device;
10use crate::selector_parser::SnapshotMap;
11use crate::shared_lock::SharedRwLockReadGuard;
12use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, StylesheetInDocument};
13use std::mem;
14
15#[derive(MallocSizeOf)]
17struct StylesheetSetEntry<S>
18where
19    S: StylesheetInDocument + PartialEq + 'static,
20{
21    sheet: S,
23
24    committed: bool,
26}
27
28impl<S> StylesheetSetEntry<S>
29where
30    S: StylesheetInDocument + PartialEq + 'static,
31{
32    fn new(sheet: S) -> Self {
33        Self {
34            sheet,
35            committed: false,
36        }
37    }
38}
39
40#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
42pub enum DataValidity {
43    Valid = 0,
46
47    CascadeInvalid = 1,
50
51    FullyInvalid = 2,
53}
54
55impl Default for DataValidity {
56    fn default() -> Self {
57        DataValidity::Valid
58    }
59}
60
61pub struct DocumentStylesheetFlusher<'a, S>
63where
64    S: StylesheetInDocument + PartialEq + 'static,
65{
66    collections: &'a mut PerOrigin<SheetCollection<S>>,
67    had_invalidations: bool,
68}
69
70#[derive(Clone, Copy, Debug)]
72pub enum SheetRebuildKind {
73    Full,
75    CascadeOnly,
77}
78
79impl SheetRebuildKind {
80    pub fn should_rebuild_invalidation(&self) -> bool {
82        matches!(*self, SheetRebuildKind::Full)
83    }
84}
85
86impl<'a, S> DocumentStylesheetFlusher<'a, S>
87where
88    S: StylesheetInDocument + PartialEq + 'static,
89{
90    pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<'_, S> {
92        self.collections.borrow_mut_for_origin(&origin).flush()
93    }
94
95    pub fn origin_sheets(&self, origin: Origin) -> impl Iterator<Item = &S> {
99        self.collections.borrow_for_origin(&origin).iter()
100    }
101
102    #[inline]
105    pub fn had_invalidations(&self) -> bool {
106        self.had_invalidations
107    }
108}
109
110pub struct SheetCollectionFlusher<'a, S>
113where
114    S: StylesheetInDocument + PartialEq + 'static,
115{
116    entries: &'a mut [StylesheetSetEntry<S>],
119    validity: DataValidity,
120    dirty: bool,
121}
122
123impl<'a, S> SheetCollectionFlusher<'a, S>
124where
125    S: StylesheetInDocument + PartialEq + 'static,
126{
127    #[inline]
129    pub fn dirty(&self) -> bool {
130        self.dirty
131    }
132
133    #[inline]
135    pub fn data_validity(&self) -> DataValidity {
136        self.validity
137    }
138
139    pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
141        self.entries.iter().map(|entry| &entry.sheet)
142    }
143}
144
145impl<'a, S> SheetCollectionFlusher<'a, S>
146where
147    S: StylesheetInDocument + PartialEq + 'static,
148{
149    pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
157        for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
158            let committed = mem::replace(&mut potential_sheet.committed, true);
159            let rebuild_kind = if !committed {
160                SheetRebuildKind::Full
163            } else {
164                match self.validity {
165                    DataValidity::Valid => continue,
166                    DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
167                    DataValidity::FullyInvalid => SheetRebuildKind::Full,
168                }
169            };
170
171            if !callback(index, &potential_sheet.sheet, rebuild_kind) {
172                return;
173            }
174        }
175    }
176}
177
178#[derive(MallocSizeOf)]
179struct SheetCollection<S>
180where
181    S: StylesheetInDocument + PartialEq + 'static,
182{
183    entries: Vec<StylesheetSetEntry<S>>,
188
189    data_validity: DataValidity,
196
197    dirty: bool,
201}
202
203impl<S> Default for SheetCollection<S>
204where
205    S: StylesheetInDocument + PartialEq + 'static,
206{
207    fn default() -> Self {
208        Self {
209            entries: vec![],
210            data_validity: DataValidity::Valid,
211            dirty: false,
212        }
213    }
214}
215
216impl<S> SheetCollection<S>
217where
218    S: StylesheetInDocument + PartialEq + 'static,
219{
220    fn len(&self) -> usize {
222        self.entries.len()
223    }
224
225    fn get(&self, index: usize) -> Option<&S> {
227        self.entries.get(index).map(|e| &e.sheet)
228    }
229
230    fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
231        let rev_pos = self
232            .entries
233            .iter()
234            .rev()
235            .position(|entry| entry.sheet == *sheet);
236        rev_pos.map(|i| self.entries.len() - i - 1)
237    }
238
239    fn remove(&mut self, sheet: &S) {
240        let index = self.find_sheet_index(sheet);
241        if cfg!(feature = "gecko") && index.is_none() {
242            return;
244        }
245        let sheet = self.entries.remove(index.unwrap());
246        if sheet.committed {
254            self.set_data_validity_at_least(DataValidity::FullyInvalid);
255        } else {
256            self.dirty = true;
257        }
258    }
259
260    fn contains(&self, sheet: &S) -> bool {
261        self.entries.iter().any(|e| e.sheet == *sheet)
262    }
263
264    fn append(&mut self, sheet: S) {
266        debug_assert!(!self.contains(&sheet));
267        self.entries.push(StylesheetSetEntry::new(sheet));
268        self.dirty = true;
274    }
275
276    fn insert_before(&mut self, sheet: S, before_sheet: &S) {
277        debug_assert!(!self.contains(&sheet));
278
279        let index = self
280            .find_sheet_index(before_sheet)
281            .expect("`before_sheet` stylesheet not found");
282
283        self.set_data_validity_at_least(DataValidity::CascadeInvalid);
286        self.entries.insert(index, StylesheetSetEntry::new(sheet));
287    }
288
289    fn set_data_validity_at_least(&mut self, validity: DataValidity) {
290        use std::cmp;
291
292        debug_assert_ne!(validity, DataValidity::Valid);
293
294        self.dirty = true;
295        self.data_validity = cmp::max(validity, self.data_validity);
296    }
297
298    fn iter(&self) -> impl Iterator<Item = &S> {
300        self.entries.iter().map(|e| &e.sheet)
301    }
302
303    fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
305        self.entries.iter_mut().map(|e| &mut e.sheet)
306    }
307
308    fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
309        let dirty = mem::replace(&mut self.dirty, false);
310        let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
311
312        SheetCollectionFlusher {
313            entries: &mut self.entries,
314            dirty,
315            validity,
316        }
317    }
318}
319
320#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
322pub struct DocumentStylesheetSet<S>
323where
324    S: StylesheetInDocument + PartialEq + 'static,
325{
326    collections: PerOrigin<SheetCollection<S>>,
328
329    invalidations: StylesheetInvalidationSet,
331}
332
333macro_rules! sheet_set_methods {
340    ($set_name:expr) => {
341        fn collect_invalidations_for(
342            &mut self,
343            device: Option<&Device>,
344            sheet: &S,
345            guard: &SharedRwLockReadGuard,
346        ) {
347            if let Some(device) = device {
348                self.invalidations
349                    .collect_invalidations_for(device, sheet, guard);
350            }
351        }
352
353        pub fn append_stylesheet(
357            &mut self,
358            device: Option<&Device>,
359            sheet: S,
360            guard: &SharedRwLockReadGuard,
361        ) {
362            debug!(concat!($set_name, "::append_stylesheet"));
363            self.collect_invalidations_for(device, &sheet, guard);
364            let collection = self.collection_for(&sheet, guard);
365            collection.append(sheet);
366        }
367
368        pub fn insert_stylesheet_before(
370            &mut self,
371            device: Option<&Device>,
372            sheet: S,
373            before_sheet: S,
374            guard: &SharedRwLockReadGuard,
375        ) {
376            debug!(concat!($set_name, "::insert_stylesheet_before"));
377            self.collect_invalidations_for(device, &sheet, guard);
378
379            let collection = self.collection_for(&sheet, guard);
380            collection.insert_before(sheet, &before_sheet);
381        }
382
383        pub fn remove_stylesheet(
385            &mut self,
386            device: Option<&Device>,
387            sheet: S,
388            guard: &SharedRwLockReadGuard,
389        ) {
390            debug!(concat!($set_name, "::remove_stylesheet"));
391            self.collect_invalidations_for(device, &sheet, guard);
392
393            let collection = self.collection_for(&sheet, guard);
394            collection.remove(&sheet)
395        }
396
397        pub fn rule_changed(
400            &mut self,
401            device: Option<&Device>,
402            sheet: &S,
403            rule: &CssRule,
404            guard: &SharedRwLockReadGuard,
405            change_kind: RuleChangeKind,
406        ) {
407            if let Some(device) = device {
408                let quirks_mode = device.quirks_mode();
409                self.invalidations.rule_changed(
410                    sheet,
411                    rule,
412                    guard,
413                    device,
414                    quirks_mode,
415                    change_kind,
416                );
417            }
418
419            let validity = match change_kind {
420                RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
424                    DataValidity::FullyInvalid
425                },
426                RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
441            };
442
443            let collection = self.collection_for(&sheet, guard);
444            collection.set_data_validity_at_least(validity);
445        }
446    };
447}
448
449impl<S> DocumentStylesheetSet<S>
450where
451    S: StylesheetInDocument + PartialEq + 'static,
452{
453    pub fn new() -> Self {
455        Self {
456            collections: Default::default(),
457            invalidations: StylesheetInvalidationSet::new(),
458        }
459    }
460
461    fn collection_for(
462        &mut self,
463        sheet: &S,
464        guard: &SharedRwLockReadGuard,
465    ) -> &mut SheetCollection<S> {
466        let origin = sheet.contents(guard).origin;
467        self.collections.borrow_mut_for_origin(&origin)
468    }
469
470    sheet_set_methods!("DocumentStylesheetSet");
471
472    pub fn len(&self) -> usize {
474        self.collections
475            .iter_origins()
476            .fold(0, |s, (item, _)| s + item.len())
477    }
478
479    #[inline]
481    pub fn sheet_count(&self, origin: Origin) -> usize {
482        self.collections.borrow_for_origin(&origin).len()
483    }
484
485    #[inline]
487    pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
488        self.collections.borrow_for_origin(&origin).get(index)
489    }
490
491    pub fn has_changed(&self) -> bool {
493        !self.invalidations.is_empty()
494            || self
495                .collections
496                .iter_origins()
497                .any(|(collection, _)| collection.dirty)
498    }
499
500    pub fn flush<E>(
503        &mut self,
504        document_element: Option<E>,
505        snapshots: Option<&SnapshotMap>,
506    ) -> DocumentStylesheetFlusher<'_, S>
507    where
508        E: TElement,
509    {
510        debug!("DocumentStylesheetSet::flush");
511
512        let had_invalidations = self.invalidations.flush(document_element, snapshots);
513
514        DocumentStylesheetFlusher {
515            collections: &mut self.collections,
516            had_invalidations,
517        }
518    }
519
520    #[cfg(feature = "servo")]
522    pub fn flush_without_invalidation(&mut self) -> OriginSet {
523        debug!("DocumentStylesheetSet::flush_without_invalidation");
524
525        let mut origins = OriginSet::empty();
526        self.invalidations.clear();
527
528        for (collection, origin) in self.collections.iter_mut_origins() {
529            if collection.flush().dirty() {
530                origins |= origin;
531            }
532        }
533
534        origins
535    }
536
537    pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
539        self.collections
540            .iter_origins()
541            .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
542    }
543
544    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
546        self.collections
547            .iter_mut_origins()
548            .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
549    }
550
551    pub fn force_dirty(&mut self, origins: OriginSet) {
554        self.invalidations.invalidate_fully();
555        for origin in origins.iter_origins() {
556            self.collections
558                .borrow_mut_for_origin(&origin)
559                .set_data_validity_at_least(DataValidity::FullyInvalid);
560        }
561    }
562}
563
564#[derive(MallocSizeOf)]
566pub struct AuthorStylesheetSet<S>
567where
568    S: StylesheetInDocument + PartialEq + 'static,
569{
570    collection: SheetCollection<S>,
572    invalidations: StylesheetInvalidationSet,
574}
575
576pub struct AuthorStylesheetFlusher<'a, S>
578where
579    S: StylesheetInDocument + PartialEq + 'static,
580{
581    pub sheets: SheetCollectionFlusher<'a, S>,
583    pub had_invalidations: bool,
585}
586
587impl<S> AuthorStylesheetSet<S>
588where
589    S: StylesheetInDocument + PartialEq + 'static,
590{
591    #[inline]
593    pub fn new() -> Self {
594        Self {
595            collection: Default::default(),
596            invalidations: StylesheetInvalidationSet::new(),
597        }
598    }
599
600    pub fn dirty(&self) -> bool {
602        self.collection.dirty
603    }
604
605    pub fn is_empty(&self) -> bool {
607        self.collection.len() == 0
608    }
609
610    pub fn get(&self, index: usize) -> Option<&S> {
612        self.collection.get(index)
613    }
614
615    pub fn len(&self) -> usize {
617        self.collection.len()
618    }
619
620    fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
621        &mut self.collection
622    }
623
624    sheet_set_methods!("AuthorStylesheetSet");
625
626    pub fn iter(&self) -> impl Iterator<Item = &S> {
628        self.collection.iter()
629    }
630
631    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
633        self.collection.iter_mut()
634    }
635
636    pub fn force_dirty(&mut self) {
638        self.invalidations.invalidate_fully();
639        self.collection
640            .set_data_validity_at_least(DataValidity::FullyInvalid);
641    }
642
643    pub fn flush<E>(
648        &mut self,
649        host: Option<E>,
650        snapshots: Option<&SnapshotMap>,
651    ) -> AuthorStylesheetFlusher<'_, S>
652    where
653        E: TElement,
654    {
655        let had_invalidations = self.invalidations.flush(host, snapshots);
656        AuthorStylesheetFlusher {
657            sheets: self.collection.flush(),
658            had_invalidations,
659        }
660    }
661}