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}