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, CssRuleRef, CustomMediaMap, Origin, OriginSet, PerOrigin, StylesheetInDocument,
14};
15use std::mem;
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
42#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
44pub enum DataValidity {
45 Valid = 0,
48
49 CascadeInvalid = 1,
52
53 FullyInvalid = 2,
55}
56
57impl Default for DataValidity {
58 fn default() -> Self {
59 DataValidity::Valid
60 }
61}
62
63pub struct DocumentStylesheetFlusher<'a, S>
65where
66 S: StylesheetInDocument + PartialEq + 'static,
67{
68 collections: &'a mut PerOrigin<SheetCollection<S>>,
69 had_invalidations: bool,
70}
71
72#[derive(Clone, Copy, Debug)]
74pub enum SheetRebuildKind {
75 Full,
77 CascadeOnly,
79}
80
81impl SheetRebuildKind {
82 pub fn should_rebuild_invalidation(&self) -> bool {
84 matches!(*self, SheetRebuildKind::Full)
85 }
86}
87
88impl<'a, S> DocumentStylesheetFlusher<'a, S>
89where
90 S: StylesheetInDocument + PartialEq + 'static,
91{
92 pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<'_, S> {
94 self.collections.borrow_mut_for_origin(&origin).flush()
95 }
96
97 pub fn origin_sheets(&self, origin: Origin) -> impl Iterator<Item = &S> {
101 self.collections.borrow_for_origin(&origin).iter()
102 }
103
104 #[inline]
107 pub fn had_invalidations(&self) -> bool {
108 self.had_invalidations
109 }
110}
111
112pub struct SheetCollectionFlusher<'a, S>
115where
116 S: StylesheetInDocument + PartialEq + 'static,
117{
118 entries: &'a mut [StylesheetSetEntry<S>],
121 validity: DataValidity,
122 dirty: bool,
123}
124
125impl<'a, S> SheetCollectionFlusher<'a, S>
126where
127 S: StylesheetInDocument + PartialEq + 'static,
128{
129 #[inline]
131 pub fn dirty(&self) -> bool {
132 self.dirty
133 }
134
135 #[inline]
137 pub fn data_validity(&self) -> DataValidity {
138 self.validity
139 }
140
141 pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
143 self.entries.iter().map(|entry| &entry.sheet)
144 }
145}
146
147impl<'a, S> SheetCollectionFlusher<'a, S>
148where
149 S: StylesheetInDocument + PartialEq + 'static,
150{
151 pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
159 for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
160 let committed = mem::replace(&mut potential_sheet.committed, true);
161 let rebuild_kind = if !committed {
162 SheetRebuildKind::Full
165 } else {
166 match self.validity {
167 DataValidity::Valid => continue,
168 DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
169 DataValidity::FullyInvalid => SheetRebuildKind::Full,
170 }
171 };
172
173 if !callback(index, &potential_sheet.sheet, rebuild_kind) {
174 return;
175 }
176 }
177 }
178}
179
180#[derive(MallocSizeOf)]
181struct SheetCollection<S>
182where
183 S: StylesheetInDocument + PartialEq + 'static,
184{
185 entries: Vec<StylesheetSetEntry<S>>,
190
191 data_validity: DataValidity,
198
199 dirty: bool,
203}
204
205impl<S> Default for SheetCollection<S>
206where
207 S: StylesheetInDocument + PartialEq + 'static,
208{
209 fn default() -> Self {
210 Self {
211 entries: vec![],
212 data_validity: DataValidity::Valid,
213 dirty: false,
214 }
215 }
216}
217
218impl<S> SheetCollection<S>
219where
220 S: StylesheetInDocument + PartialEq + 'static,
221{
222 fn len(&self) -> usize {
224 self.entries.len()
225 }
226
227 fn get(&self, index: usize) -> Option<&S> {
229 self.entries.get(index).map(|e| &e.sheet)
230 }
231
232 fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
233 let rev_pos = self
234 .entries
235 .iter()
236 .rev()
237 .position(|entry| entry.sheet == *sheet);
238 rev_pos.map(|i| self.entries.len() - i - 1)
239 }
240
241 fn remove(&mut self, sheet: &S) {
242 let index = self.find_sheet_index(sheet);
243 if cfg!(feature = "gecko") && index.is_none() {
244 return;
246 }
247 let sheet = self.entries.remove(index.unwrap());
248 if sheet.committed {
256 self.set_data_validity_at_least(DataValidity::FullyInvalid);
257 } else {
258 self.dirty = true;
259 }
260 }
261
262 fn contains(&self, sheet: &S) -> bool {
263 self.entries.iter().any(|e| e.sheet == *sheet)
264 }
265
266 fn append(&mut self, sheet: S) {
268 debug_assert!(!self.contains(&sheet));
269 self.entries.push(StylesheetSetEntry::new(sheet));
270 self.dirty = true;
276 }
277
278 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
279 debug_assert!(!self.contains(&sheet));
280
281 let index = self
282 .find_sheet_index(before_sheet)
283 .expect("`before_sheet` stylesheet not found");
284
285 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
288 self.entries.insert(index, StylesheetSetEntry::new(sheet));
289 }
290
291 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
292 use std::cmp;
293
294 debug_assert_ne!(validity, DataValidity::Valid);
295
296 self.dirty = true;
297 self.data_validity = cmp::max(validity, self.data_validity);
298 }
299
300 fn iter(&self) -> impl Iterator<Item = &S> {
302 self.entries.iter().map(|e| &e.sheet)
303 }
304
305 fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
307 self.entries.iter_mut().map(|e| &mut e.sheet)
308 }
309
310 fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
311 let dirty = mem::replace(&mut self.dirty, false);
312 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
313
314 SheetCollectionFlusher {
315 entries: &mut self.entries,
316 dirty,
317 validity,
318 }
319 }
320}
321
322#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
324pub struct DocumentStylesheetSet<S>
325where
326 S: StylesheetInDocument + PartialEq + 'static,
327{
328 collections: PerOrigin<SheetCollection<S>>,
330
331 invalidations: StylesheetInvalidationSet,
333}
334
335macro_rules! sheet_set_methods {
342 ($set_name:expr) => {
343 fn collect_invalidations_for(
344 &mut self,
345 device: Option<&Device>,
346 custom_media: &CustomMediaMap,
347 sheet: &S,
348 guard: &SharedRwLockReadGuard,
349 ) {
350 if let Some(device) = device {
351 self.invalidations
352 .collect_invalidations_for(device, custom_media, sheet, guard);
353 }
354 }
355
356 pub fn append_stylesheet(
360 &mut self,
361 device: Option<&Device>,
362 custom_media: &CustomMediaMap,
363 sheet: S,
364 guard: &SharedRwLockReadGuard,
365 ) {
366 debug!(concat!($set_name, "::append_stylesheet"));
367 self.collect_invalidations_for(device, custom_media, &sheet, guard);
368 let collection = self.collection_for(&sheet, guard);
369 collection.append(sheet);
370 }
371
372 pub fn insert_stylesheet_before(
374 &mut self,
375 device: Option<&Device>,
376 custom_media: &CustomMediaMap,
377 sheet: S,
378 before_sheet: S,
379 guard: &SharedRwLockReadGuard,
380 ) {
381 debug!(concat!($set_name, "::insert_stylesheet_before"));
382 self.collect_invalidations_for(device, custom_media, &sheet, guard);
383
384 let collection = self.collection_for(&sheet, guard);
385 collection.insert_before(sheet, &before_sheet);
386 }
387
388 pub fn remove_stylesheet(
390 &mut self,
391 device: Option<&Device>,
392 custom_media: &CustomMediaMap,
393 sheet: S,
394 guard: &SharedRwLockReadGuard,
395 ) {
396 debug!(concat!($set_name, "::remove_stylesheet"));
397 self.collect_invalidations_for(device, custom_media, &sheet, guard);
398
399 let collection = self.collection_for(&sheet, guard);
400 collection.remove(&sheet)
401 }
402
403 pub fn rule_changed(
406 &mut self,
407 device: Option<&Device>,
408 custom_media: &CustomMediaMap,
409 sheet: &S,
410 rule: &CssRule,
411 guard: &SharedRwLockReadGuard,
412 change_kind: RuleChangeKind,
413 ancestors: &[CssRuleRef],
414 ) {
415 if let Some(device) = device {
416 let quirks_mode = device.quirks_mode();
417 self.invalidations.rule_changed(
418 sheet,
419 rule,
420 guard,
421 device,
422 quirks_mode,
423 custom_media,
424 change_kind,
425 ancestors,
426 );
427 }
428
429 let validity = match change_kind {
430 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
434 DataValidity::FullyInvalid
435 },
436 RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
451 };
452
453 let collection = self.collection_for(&sheet, guard);
454 collection.set_data_validity_at_least(validity);
455 }
456 };
457}
458
459impl<S> DocumentStylesheetSet<S>
460where
461 S: StylesheetInDocument + PartialEq + 'static,
462{
463 pub fn new() -> Self {
465 Self {
466 collections: Default::default(),
467 invalidations: StylesheetInvalidationSet::new(),
468 }
469 }
470
471 fn collection_for(
472 &mut self,
473 sheet: &S,
474 guard: &SharedRwLockReadGuard,
475 ) -> &mut SheetCollection<S> {
476 let origin = sheet.contents(guard).origin;
477 self.collections.borrow_mut_for_origin(&origin)
478 }
479
480 sheet_set_methods!("DocumentStylesheetSet");
481
482 pub fn len(&self) -> usize {
484 self.collections
485 .iter_origins()
486 .fold(0, |s, (item, _)| s + item.len())
487 }
488
489 #[inline]
491 pub fn sheet_count(&self, origin: Origin) -> usize {
492 self.collections.borrow_for_origin(&origin).len()
493 }
494
495 #[inline]
497 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
498 self.collections.borrow_for_origin(&origin).get(index)
499 }
500
501 pub fn has_changed(&self) -> bool {
503 !self.invalidations.is_empty()
504 || self
505 .collections
506 .iter_origins()
507 .any(|(collection, _)| collection.dirty)
508 }
509
510 pub fn flush<E>(
513 &mut self,
514 document_element: Option<E>,
515 snapshots: Option<&SnapshotMap>,
516 ) -> DocumentStylesheetFlusher<'_, S>
517 where
518 E: TElement,
519 {
520 debug!("DocumentStylesheetSet::flush");
521
522 let had_invalidations = self.invalidations.flush(document_element, snapshots);
523
524 DocumentStylesheetFlusher {
525 collections: &mut self.collections,
526 had_invalidations,
527 }
528 }
529
530 #[cfg(feature = "servo")]
532 pub fn flush_without_invalidation(&mut self) -> OriginSet {
533 debug!("DocumentStylesheetSet::flush_without_invalidation");
534
535 let mut origins = OriginSet::empty();
536 self.invalidations.clear();
537
538 for (collection, origin) in self.collections.iter_mut_origins() {
539 if collection.flush().dirty() {
540 origins |= origin;
541 }
542 }
543
544 origins
545 }
546
547 pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
549 self.collections
550 .iter_origins()
551 .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
552 }
553
554 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
556 self.collections
557 .iter_mut_origins()
558 .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
559 }
560
561 pub fn force_dirty(&mut self, origins: OriginSet) {
564 self.invalidations.invalidate_fully();
565 for origin in origins.iter_origins() {
566 self.collections
568 .borrow_mut_for_origin(&origin)
569 .set_data_validity_at_least(DataValidity::FullyInvalid);
570 }
571 }
572}
573
574#[derive(MallocSizeOf)]
576pub struct AuthorStylesheetSet<S>
577where
578 S: StylesheetInDocument + PartialEq + 'static,
579{
580 collection: SheetCollection<S>,
582 invalidations: StylesheetInvalidationSet,
584}
585
586pub struct AuthorStylesheetFlusher<'a, S>
588where
589 S: StylesheetInDocument + PartialEq + 'static,
590{
591 pub sheets: SheetCollectionFlusher<'a, S>,
593 pub had_invalidations: bool,
595}
596
597impl<S> AuthorStylesheetSet<S>
598where
599 S: StylesheetInDocument + PartialEq + 'static,
600{
601 #[inline]
603 pub fn new() -> Self {
604 Self {
605 collection: Default::default(),
606 invalidations: StylesheetInvalidationSet::new(),
607 }
608 }
609
610 pub fn dirty(&self) -> bool {
612 self.collection.dirty
613 }
614
615 pub fn is_empty(&self) -> bool {
617 self.collection.len() == 0
618 }
619
620 pub fn get(&self, index: usize) -> Option<&S> {
622 self.collection.get(index)
623 }
624
625 pub fn len(&self) -> usize {
627 self.collection.len()
628 }
629
630 fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
631 &mut self.collection
632 }
633
634 sheet_set_methods!("AuthorStylesheetSet");
635
636 pub fn iter(&self) -> impl Iterator<Item = &S> {
638 self.collection.iter()
639 }
640
641 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
643 self.collection.iter_mut()
644 }
645
646 pub fn force_dirty(&mut self) {
648 self.invalidations.invalidate_fully();
649 self.collection
650 .set_data_validity_at_least(DataValidity::FullyInvalid);
651 }
652
653 pub fn flush<E>(
658 &mut self,
659 host: Option<E>,
660 snapshots: Option<&SnapshotMap>,
661 ) -> AuthorStylesheetFlusher<'_, S>
662 where
663 E: TElement,
664 {
665 let had_invalidations = self.invalidations.flush(host, snapshots);
666 AuthorStylesheetFlusher {
667 sheets: self.collection.flush(),
668 had_invalidations,
669 }
670 }
671}