1use crate::derives::*;
8use crate::device::Device;
9use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
10use crate::shared_lock::SharedRwLockReadGuard;
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) {
228 let index = self.find_sheet_index(sheet);
229 if cfg!(feature = "gecko") && index.is_none() {
230 return;
232 }
233 let sheet = self.entries.remove(index.unwrap());
234 if sheet.committed {
242 self.set_data_validity_at_least(DataValidity::FullyInvalid);
243 } else {
244 self.dirty = true;
245 }
246 }
247
248 fn contains(&self, sheet: &S) -> bool {
249 self.entries.iter().any(|e| e.sheet == *sheet)
250 }
251
252 fn append(&mut self, sheet: S) {
254 debug_assert!(!self.contains(&sheet));
255 self.entries.push(StylesheetSetEntry::new(sheet));
256 self.dirty = true;
262 }
263
264 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
265 debug_assert!(!self.contains(&sheet));
266
267 let index = self
268 .find_sheet_index(before_sheet)
269 .expect("`before_sheet` stylesheet not found");
270
271 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
274 self.entries.insert(index, StylesheetSetEntry::new(sheet));
275 }
276
277 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
278 use std::cmp;
279
280 debug_assert_ne!(validity, DataValidity::Valid);
281
282 self.dirty = true;
283 self.data_validity = cmp::max(validity, self.data_validity);
284 }
285
286 fn iter(&self) -> impl Iterator<Item = &S> {
288 self.entries.iter().map(|e| &e.sheet)
289 }
290
291 fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
293 self.entries.iter_mut().map(|e| &mut e.sheet)
294 }
295
296 fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
297 let dirty = mem::replace(&mut self.dirty, false);
298 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
299
300 SheetCollectionFlusher {
301 entries: &mut self.entries,
302 dirty,
303 validity,
304 }
305 }
306}
307
308#[derive(MallocSizeOf)]
310pub struct DocumentStylesheetSet<S>
311where
312 S: StylesheetInDocument + PartialEq + 'static,
313{
314 collections: PerOrigin<SheetCollection<S>>,
316
317 invalidations: StylesheetInvalidationSet,
319}
320
321macro_rules! sheet_set_methods {
328 ($set_name:expr) => {
329 fn collect_invalidations_for(
330 &mut self,
331 device: Option<&Device>,
332 custom_media: &CustomMediaMap,
333 sheet: &S,
334 guard: &SharedRwLockReadGuard,
335 ) {
336 if let Some(device) = device {
337 self.invalidations
338 .collect_invalidations_for(device, custom_media, sheet, guard);
339 }
340 }
341
342 pub fn append_stylesheet(
346 &mut self,
347 device: Option<&Device>,
348 custom_media: &CustomMediaMap,
349 sheet: S,
350 guard: &SharedRwLockReadGuard,
351 ) {
352 debug!(concat!($set_name, "::append_stylesheet"));
353 self.collect_invalidations_for(device, custom_media, &sheet, guard);
354 let collection = self.collection_for(&sheet, guard);
355 collection.append(sheet);
356 }
357
358 pub fn insert_stylesheet_before(
360 &mut self,
361 device: Option<&Device>,
362 custom_media: &CustomMediaMap,
363 sheet: S,
364 before_sheet: S,
365 guard: &SharedRwLockReadGuard,
366 ) {
367 debug!(concat!($set_name, "::insert_stylesheet_before"));
368 self.collect_invalidations_for(device, custom_media, &sheet, guard);
369
370 let collection = self.collection_for(&sheet, guard);
371 collection.insert_before(sheet, &before_sheet);
372 }
373
374 pub fn remove_stylesheet(
376 &mut self,
377 device: Option<&Device>,
378 custom_media: &CustomMediaMap,
379 sheet: S,
380 guard: &SharedRwLockReadGuard,
381 ) {
382 debug!(concat!($set_name, "::remove_stylesheet"));
383 self.collect_invalidations_for(device, custom_media, &sheet, guard);
384
385 let collection = self.collection_for(&sheet, guard);
386 collection.remove(&sheet)
387 }
388
389 pub fn rule_changed(
392 &mut self,
393 device: Option<&Device>,
394 custom_media: &CustomMediaMap,
395 sheet: &S,
396 rule: &CssRule,
397 guard: &SharedRwLockReadGuard,
398 change_kind: RuleChangeKind,
399 ancestors: &[CssRuleRef],
400 ) {
401 if let Some(device) = device {
402 let quirks_mode = device.quirks_mode();
403 self.invalidations.rule_changed(
404 sheet,
405 rule,
406 guard,
407 device,
408 quirks_mode,
409 custom_media,
410 change_kind,
411 ancestors,
412 );
413 }
414
415 let validity = match change_kind {
416 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
420 DataValidity::FullyInvalid
421 },
422 RuleChangeKind::PositionTryDeclarations | RuleChangeKind::StyleRuleDeclarations => {
437 DataValidity::FullyInvalid
438 },
439 };
440
441 let collection = self.collection_for(&sheet, guard);
442 collection.set_data_validity_at_least(validity);
443 }
444 };
445}
446
447impl<S> Default for DocumentStylesheetSet<S>
448where
449 S: StylesheetInDocument + PartialEq + 'static,
450{
451 fn default() -> Self {
452 Self::new()
453 }
454}
455
456impl<S> DocumentStylesheetSet<S>
457where
458 S: StylesheetInDocument + PartialEq + 'static,
459{
460 pub fn new() -> Self {
462 Self {
463 collections: Default::default(),
464 invalidations: StylesheetInvalidationSet::new(),
465 }
466 }
467
468 fn collection_for(
469 &mut self,
470 sheet: &S,
471 guard: &SharedRwLockReadGuard,
472 ) -> &mut SheetCollection<S> {
473 let origin = sheet.contents(guard).origin;
474 self.collections.borrow_mut_for_origin(&origin)
475 }
476
477 sheet_set_methods!("DocumentStylesheetSet");
478
479 pub fn len(&self) -> usize {
481 self.collections
482 .iter_origins()
483 .fold(0, |s, (item, _)| s + item.len())
484 }
485
486 #[inline]
488 pub fn sheet_count(&self, origin: Origin) -> usize {
489 self.collections.borrow_for_origin(&origin).len()
490 }
491
492 #[inline]
494 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
495 self.collections.borrow_for_origin(&origin).get(index)
496 }
497
498 pub fn has_changed(&self) -> bool {
500 !self.invalidations.is_empty()
501 || self
502 .collections
503 .iter_origins()
504 .any(|(collection, _)| collection.dirty)
505 }
506
507 pub fn flush(&mut self) -> (DocumentStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
510 debug!("DocumentStylesheetSet::flush");
511 (
512 DocumentStylesheetFlusher {
513 collections: &mut self.collections,
514 },
515 std::mem::take(&mut self.invalidations),
516 )
517 }
518
519 #[cfg(feature = "servo")]
521 pub fn flush_without_invalidation(&mut self) -> OriginSet {
522 debug!("DocumentStylesheetSet::flush_without_invalidation");
523
524 let mut origins = OriginSet::empty();
525 std::mem::take(&mut self.invalidations);
526
527 for (collection, origin) in self.collections.iter_mut_origins() {
528 if collection.flush().dirty() {
529 origins |= origin;
530 }
531 }
532
533 origins
534 }
535
536 pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
538 self.collections
539 .iter_origins()
540 .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
541 }
542
543 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
545 self.collections
546 .iter_mut_origins()
547 .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
548 }
549
550 pub fn force_dirty(&mut self, origins: OriginSet) {
553 self.invalidations.invalidate_fully();
554 for origin in origins.iter_origins() {
555 self.collections
557 .borrow_mut_for_origin(&origin)
558 .set_data_validity_at_least(DataValidity::FullyInvalid);
559 }
560 }
561}
562
563#[derive(MallocSizeOf)]
565pub struct AuthorStylesheetSet<S>
566where
567 S: StylesheetInDocument + PartialEq + 'static,
568{
569 collection: SheetCollection<S>,
571 invalidations: StylesheetInvalidationSet,
573}
574
575pub struct AuthorStylesheetFlusher<'a, S>
577where
578 S: StylesheetInDocument + PartialEq + 'static,
579{
580 pub sheets: SheetCollectionFlusher<'a, S>,
582}
583
584impl<S> Default for AuthorStylesheetSet<S>
585where
586 S: StylesheetInDocument + PartialEq + 'static,
587{
588 fn default() -> Self {
589 Self::new()
590 }
591}
592
593impl<S> AuthorStylesheetSet<S>
594where
595 S: StylesheetInDocument + PartialEq + 'static,
596{
597 #[inline]
599 pub fn new() -> Self {
600 Self {
601 collection: Default::default(),
602 invalidations: StylesheetInvalidationSet::new(),
603 }
604 }
605
606 pub fn dirty(&self) -> bool {
608 self.collection.dirty
609 }
610
611 pub fn is_empty(&self) -> bool {
613 self.collection.len() == 0
614 }
615
616 pub fn get(&self, index: usize) -> Option<&S> {
618 self.collection.get(index)
619 }
620
621 pub fn len(&self) -> usize {
623 self.collection.len()
624 }
625
626 fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
627 &mut self.collection
628 }
629
630 sheet_set_methods!("AuthorStylesheetSet");
631
632 pub fn iter(&self) -> impl Iterator<Item = &S> {
634 self.collection.iter()
635 }
636
637 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
639 self.collection.iter_mut()
640 }
641
642 pub fn force_dirty(&mut self) {
644 self.invalidations.invalidate_fully();
645 self.collection
646 .set_data_validity_at_least(DataValidity::FullyInvalid);
647 }
648
649 pub fn flush(&mut self) -> (AuthorStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
651 (
652 AuthorStylesheetFlusher {
653 sheets: self.collection.flush(),
654 },
655 std::mem::take(&mut self.invalidations),
656 )
657 }
658}