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::{
13 CssRule, Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument,
14};
15use std::{mem, slice};
16
17#[derive(MallocSizeOf)]
19struct StylesheetSetEntry<S>
20where
21 S: StylesheetInDocument + PartialEq + 'static,
22{
23 sheet: S,
25
26 committed: bool,
28}
29
30impl<S> StylesheetSetEntry<S>
31where
32 S: StylesheetInDocument + PartialEq + 'static,
33{
34 fn new(sheet: S) -> Self {
35 Self {
36 sheet,
37 committed: false,
38 }
39 }
40}
41
42pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
44where
45 S: StylesheetInDocument + PartialEq + 'static;
46
47impl<'a, S> Clone for StylesheetCollectionIterator<'a, S>
48where
49 S: StylesheetInDocument + PartialEq + 'static,
50{
51 fn clone(&self) -> Self {
52 StylesheetCollectionIterator(self.0.clone())
53 }
54}
55
56impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S>
57where
58 S: StylesheetInDocument + PartialEq + 'static,
59{
60 type Item = &'a S;
61
62 fn next(&mut self) -> Option<Self::Item> {
63 self.0.next().map(|entry| &entry.sheet)
64 }
65
66 fn size_hint(&self) -> (usize, Option<usize>) {
67 self.0.size_hint()
68 }
69}
70
71#[derive(Clone)]
73pub struct StylesheetIterator<'a, S>
74where
75 S: StylesheetInDocument + PartialEq + 'static,
76{
77 origins: OriginSetIterator,
78 collections: &'a PerOrigin<SheetCollection<S>>,
79 current: Option<(Origin, StylesheetCollectionIterator<'a, S>)>,
80}
81
82impl<'a, S> Iterator for StylesheetIterator<'a, S>
83where
84 S: StylesheetInDocument + PartialEq + 'static,
85{
86 type Item = (&'a S, Origin);
87
88 fn next(&mut self) -> Option<Self::Item> {
89 loop {
90 if self.current.is_none() {
91 let next_origin = self.origins.next()?;
92
93 self.current = Some((
94 next_origin,
95 self.collections.borrow_for_origin(&next_origin).iter(),
96 ));
97 }
98
99 {
100 let (origin, ref mut iter) = *self.current.as_mut().unwrap();
101 if let Some(s) = iter.next() {
102 return Some((s, origin));
103 }
104 }
105
106 self.current = None;
107 }
108 }
109}
110
111#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
113pub enum DataValidity {
114 Valid = 0,
117
118 CascadeInvalid = 1,
121
122 FullyInvalid = 2,
124}
125
126impl Default for DataValidity {
127 fn default() -> Self {
128 DataValidity::Valid
129 }
130}
131
132pub struct DocumentStylesheetFlusher<'a, S>
134where
135 S: StylesheetInDocument + PartialEq + 'static,
136{
137 collections: &'a mut PerOrigin<SheetCollection<S>>,
138 had_invalidations: bool,
139}
140
141#[derive(Clone, Copy, Debug)]
143pub enum SheetRebuildKind {
144 Full,
146 CascadeOnly,
148}
149
150impl SheetRebuildKind {
151 pub fn should_rebuild_invalidation(&self) -> bool {
153 matches!(*self, SheetRebuildKind::Full)
154 }
155}
156
157impl<'a, S> DocumentStylesheetFlusher<'a, S>
158where
159 S: StylesheetInDocument + PartialEq + 'static,
160{
161 pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S> {
163 self.collections.borrow_mut_for_origin(&origin).flush()
164 }
165
166 pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S> {
170 self.collections.borrow_mut_for_origin(&origin).iter()
171 }
172
173 #[inline]
176 pub fn had_invalidations(&self) -> bool {
177 self.had_invalidations
178 }
179}
180
181pub struct SheetCollectionFlusher<'a, S>
184where
185 S: StylesheetInDocument + PartialEq + 'static,
186{
187 entries: &'a mut [StylesheetSetEntry<S>],
190 validity: DataValidity,
191 dirty: bool,
192}
193
194impl<'a, S> SheetCollectionFlusher<'a, S>
195where
196 S: StylesheetInDocument + PartialEq + 'static,
197{
198 #[inline]
200 pub fn dirty(&self) -> bool {
201 self.dirty
202 }
203
204 #[inline]
206 pub fn data_validity(&self) -> DataValidity {
207 self.validity
208 }
209
210 pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
212 self.entries.iter().map(|entry| &entry.sheet)
213 }
214}
215
216impl<'a, S> SheetCollectionFlusher<'a, S>
217where
218 S: StylesheetInDocument + PartialEq + 'static,
219{
220 pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
228 for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
229 let committed = mem::replace(&mut potential_sheet.committed, true);
230 let rebuild_kind = if !committed {
231 SheetRebuildKind::Full
234 } else {
235 match self.validity {
236 DataValidity::Valid => continue,
237 DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
238 DataValidity::FullyInvalid => SheetRebuildKind::Full,
239 }
240 };
241
242 if !callback(index, &potential_sheet.sheet, rebuild_kind) {
243 return;
244 }
245 }
246 }
247}
248
249#[derive(MallocSizeOf)]
250struct SheetCollection<S>
251where
252 S: StylesheetInDocument + PartialEq + 'static,
253{
254 entries: Vec<StylesheetSetEntry<S>>,
259
260 data_validity: DataValidity,
267
268 dirty: bool,
272}
273
274impl<S> Default for SheetCollection<S>
275where
276 S: StylesheetInDocument + PartialEq + 'static,
277{
278 fn default() -> Self {
279 Self {
280 entries: vec![],
281 data_validity: DataValidity::Valid,
282 dirty: false,
283 }
284 }
285}
286
287impl<S> SheetCollection<S>
288where
289 S: StylesheetInDocument + PartialEq + 'static,
290{
291 fn len(&self) -> usize {
293 self.entries.len()
294 }
295
296 fn get(&self, index: usize) -> Option<&S> {
298 self.entries.get(index).map(|e| &e.sheet)
299 }
300
301 fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
302 let rev_pos = self
303 .entries
304 .iter()
305 .rev()
306 .position(|entry| entry.sheet == *sheet);
307 rev_pos.map(|i| self.entries.len() - i - 1)
308 }
309
310 fn remove(&mut self, sheet: &S) {
311 let index = self.find_sheet_index(sheet);
312 if cfg!(feature = "gecko") && index.is_none() {
313 return;
315 }
316 let sheet = self.entries.remove(index.unwrap());
317 if sheet.committed {
325 self.set_data_validity_at_least(DataValidity::FullyInvalid);
326 } else {
327 self.dirty = true;
328 }
329 }
330
331 fn contains(&self, sheet: &S) -> bool {
332 self.entries.iter().any(|e| e.sheet == *sheet)
333 }
334
335 fn append(&mut self, sheet: S) {
337 debug_assert!(!self.contains(&sheet));
338 self.entries.push(StylesheetSetEntry::new(sheet));
339 self.dirty = true;
345 }
346
347 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
348 debug_assert!(!self.contains(&sheet));
349
350 let index = self
351 .find_sheet_index(before_sheet)
352 .expect("`before_sheet` stylesheet not found");
353
354 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
357 self.entries.insert(index, StylesheetSetEntry::new(sheet));
358 }
359
360 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
361 use std::cmp;
362
363 debug_assert_ne!(validity, DataValidity::Valid);
364
365 self.dirty = true;
366 self.data_validity = cmp::max(validity, self.data_validity);
367 }
368
369 fn iter(&self) -> StylesheetCollectionIterator<S> {
371 StylesheetCollectionIterator(self.entries.iter())
372 }
373
374 fn flush(&mut self) -> SheetCollectionFlusher<S> {
375 let dirty = mem::replace(&mut self.dirty, false);
376 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
377
378 SheetCollectionFlusher {
379 entries: &mut self.entries,
380 dirty,
381 validity,
382 }
383 }
384}
385
386#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
388pub struct DocumentStylesheetSet<S>
389where
390 S: StylesheetInDocument + PartialEq + 'static,
391{
392 collections: PerOrigin<SheetCollection<S>>,
394
395 invalidations: StylesheetInvalidationSet,
397}
398
399macro_rules! sheet_set_methods {
406 ($set_name:expr) => {
407 fn collect_invalidations_for(
408 &mut self,
409 device: Option<&Device>,
410 sheet: &S,
411 guard: &SharedRwLockReadGuard,
412 ) {
413 if let Some(device) = device {
414 self.invalidations
415 .collect_invalidations_for(device, sheet, guard);
416 }
417 }
418
419 pub fn append_stylesheet(
423 &mut self,
424 device: Option<&Device>,
425 sheet: S,
426 guard: &SharedRwLockReadGuard,
427 ) {
428 debug!(concat!($set_name, "::append_stylesheet"));
429 self.collect_invalidations_for(device, &sheet, guard);
430 let collection = self.collection_for(&sheet);
431 collection.append(sheet);
432 }
433
434 pub fn insert_stylesheet_before(
436 &mut self,
437 device: Option<&Device>,
438 sheet: S,
439 before_sheet: S,
440 guard: &SharedRwLockReadGuard,
441 ) {
442 debug!(concat!($set_name, "::insert_stylesheet_before"));
443 self.collect_invalidations_for(device, &sheet, guard);
444
445 let collection = self.collection_for(&sheet);
446 collection.insert_before(sheet, &before_sheet);
447 }
448
449 pub fn remove_stylesheet(
451 &mut self,
452 device: Option<&Device>,
453 sheet: S,
454 guard: &SharedRwLockReadGuard,
455 ) {
456 debug!(concat!($set_name, "::remove_stylesheet"));
457 self.collect_invalidations_for(device, &sheet, guard);
458
459 let collection = self.collection_for(&sheet);
460 collection.remove(&sheet)
461 }
462
463 pub fn rule_changed(
466 &mut self,
467 device: Option<&Device>,
468 sheet: &S,
469 rule: &CssRule,
470 guard: &SharedRwLockReadGuard,
471 change_kind: RuleChangeKind,
472 ) {
473 if let Some(device) = device {
474 let quirks_mode = device.quirks_mode();
475 self.invalidations.rule_changed(
476 sheet,
477 rule,
478 guard,
479 device,
480 quirks_mode,
481 change_kind,
482 );
483 }
484
485 let validity = match change_kind {
486 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
490 DataValidity::FullyInvalid
491 },
492 RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
507 };
508
509 let collection = self.collection_for(&sheet);
510 collection.set_data_validity_at_least(validity);
511 }
512 };
513}
514
515impl<S> DocumentStylesheetSet<S>
516where
517 S: StylesheetInDocument + PartialEq + 'static,
518{
519 pub fn new() -> Self {
521 Self {
522 collections: Default::default(),
523 invalidations: StylesheetInvalidationSet::new(),
524 }
525 }
526
527 fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
528 let origin = sheet.contents().origin;
529 self.collections.borrow_mut_for_origin(&origin)
530 }
531
532 sheet_set_methods!("DocumentStylesheetSet");
533
534 pub fn len(&self) -> usize {
536 self.collections
537 .iter_origins()
538 .fold(0, |s, (item, _)| s + item.len())
539 }
540
541 #[inline]
543 pub fn sheet_count(&self, origin: Origin) -> usize {
544 self.collections.borrow_for_origin(&origin).len()
545 }
546
547 #[inline]
549 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
550 self.collections.borrow_for_origin(&origin).get(index)
551 }
552
553 pub fn has_changed(&self) -> bool {
555 !self.invalidations.is_empty()
556 || self
557 .collections
558 .iter_origins()
559 .any(|(collection, _)| collection.dirty)
560 }
561
562 pub fn flush<E>(
565 &mut self,
566 document_element: Option<E>,
567 snapshots: Option<&SnapshotMap>,
568 ) -> DocumentStylesheetFlusher<S>
569 where
570 E: TElement,
571 {
572 debug!("DocumentStylesheetSet::flush");
573
574 let had_invalidations = self.invalidations.flush(document_element, snapshots);
575
576 DocumentStylesheetFlusher {
577 collections: &mut self.collections,
578 had_invalidations,
579 }
580 }
581
582 #[cfg(feature = "servo")]
584 pub fn flush_without_invalidation(&mut self) -> OriginSet {
585 debug!("DocumentStylesheetSet::flush_without_invalidation");
586
587 let mut origins = OriginSet::empty();
588 self.invalidations.clear();
589
590 for (collection, origin) in self.collections.iter_mut_origins() {
591 if collection.flush().dirty() {
592 origins |= origin;
593 }
594 }
595
596 origins
597 }
598
599 pub fn iter(&self) -> StylesheetIterator<S> {
601 StylesheetIterator {
602 origins: OriginSet::all().iter_origins(),
603 collections: &self.collections,
604 current: None,
605 }
606 }
607
608 pub fn force_dirty(&mut self, origins: OriginSet) {
611 self.invalidations.invalidate_fully();
612 for origin in origins.iter_origins() {
613 self.collections
615 .borrow_mut_for_origin(&origin)
616 .set_data_validity_at_least(DataValidity::FullyInvalid);
617 }
618 }
619}
620
621#[derive(MallocSizeOf)]
623pub struct AuthorStylesheetSet<S>
624where
625 S: StylesheetInDocument + PartialEq + 'static,
626{
627 collection: SheetCollection<S>,
629 invalidations: StylesheetInvalidationSet,
631}
632
633pub struct AuthorStylesheetFlusher<'a, S>
635where
636 S: StylesheetInDocument + PartialEq + 'static,
637{
638 pub sheets: SheetCollectionFlusher<'a, S>,
640 pub had_invalidations: bool,
642}
643
644impl<S> AuthorStylesheetSet<S>
645where
646 S: StylesheetInDocument + PartialEq + 'static,
647{
648 #[inline]
650 pub fn new() -> Self {
651 Self {
652 collection: Default::default(),
653 invalidations: StylesheetInvalidationSet::new(),
654 }
655 }
656
657 pub fn dirty(&self) -> bool {
659 self.collection.dirty
660 }
661
662 pub fn is_empty(&self) -> bool {
664 self.collection.len() == 0
665 }
666
667 pub fn get(&self, index: usize) -> Option<&S> {
669 self.collection.get(index)
670 }
671
672 pub fn len(&self) -> usize {
674 self.collection.len()
675 }
676
677 fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
678 &mut self.collection
679 }
680
681 sheet_set_methods!("AuthorStylesheetSet");
682
683 pub fn iter(&self) -> StylesheetCollectionIterator<S> {
685 self.collection.iter()
686 }
687
688 pub fn force_dirty(&mut self) {
690 self.invalidations.invalidate_fully();
691 self.collection
692 .set_data_validity_at_least(DataValidity::FullyInvalid);
693 }
694
695 pub fn flush<E>(
700 &mut self,
701 host: Option<E>,
702 snapshots: Option<&SnapshotMap>,
703 ) -> AuthorStylesheetFlusher<S>
704 where
705 E: TElement,
706 {
707 let had_invalidations = self.invalidations.flush(host, snapshots);
708 AuthorStylesheetFlusher {
709 sheets: self.collection.flush(),
710 had_invalidations,
711 }
712 }
713}