1use crate::derives::*;
8use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
9use crate::media_queries::Device;
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, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
43pub enum DataValidity {
44 Valid = 0,
47
48 CascadeInvalid = 1,
51
52 FullyInvalid = 2,
54}
55
56impl Default for DataValidity {
57 fn default() -> Self {
58 DataValidity::Valid
59 }
60}
61
62pub struct DocumentStylesheetFlusher<'a, S>
64where
65 S: StylesheetInDocument + PartialEq + 'static,
66{
67 collections: &'a mut PerOrigin<SheetCollection<S>>,
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
103pub struct SheetCollectionFlusher<'a, S>
106where
107 S: StylesheetInDocument + PartialEq + 'static,
108{
109 entries: &'a mut [StylesheetSetEntry<S>],
112 validity: DataValidity,
113 dirty: bool,
114}
115
116impl<'a, S> SheetCollectionFlusher<'a, S>
117where
118 S: StylesheetInDocument + PartialEq + 'static,
119{
120 #[inline]
122 pub fn dirty(&self) -> bool {
123 self.dirty
124 }
125
126 #[inline]
128 pub fn data_validity(&self) -> DataValidity {
129 self.validity
130 }
131
132 pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
134 self.entries.iter().map(|entry| &entry.sheet)
135 }
136}
137
138impl<'a, S> SheetCollectionFlusher<'a, S>
139where
140 S: StylesheetInDocument + PartialEq + 'static,
141{
142 pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
150 for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
151 let committed = mem::replace(&mut potential_sheet.committed, true);
152 let rebuild_kind = if !committed {
153 SheetRebuildKind::Full
156 } else {
157 match self.validity {
158 DataValidity::Valid => continue,
159 DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
160 DataValidity::FullyInvalid => SheetRebuildKind::Full,
161 }
162 };
163
164 if !callback(index, &potential_sheet.sheet, rebuild_kind) {
165 return;
166 }
167 }
168 }
169}
170
171#[derive(MallocSizeOf)]
172struct SheetCollection<S>
173where
174 S: StylesheetInDocument + PartialEq + 'static,
175{
176 entries: Vec<StylesheetSetEntry<S>>,
181
182 data_validity: DataValidity,
189
190 dirty: bool,
194}
195
196impl<S> Default for SheetCollection<S>
197where
198 S: StylesheetInDocument + PartialEq + 'static,
199{
200 fn default() -> Self {
201 Self {
202 entries: vec![],
203 data_validity: DataValidity::Valid,
204 dirty: false,
205 }
206 }
207}
208
209impl<S> SheetCollection<S>
210where
211 S: StylesheetInDocument + PartialEq + 'static,
212{
213 fn len(&self) -> usize {
215 self.entries.len()
216 }
217
218 fn get(&self, index: usize) -> Option<&S> {
220 self.entries.get(index).map(|e| &e.sheet)
221 }
222
223 fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
224 let rev_pos = self
225 .entries
226 .iter()
227 .rev()
228 .position(|entry| entry.sheet == *sheet);
229 rev_pos.map(|i| self.entries.len() - i - 1)
230 }
231
232 fn remove(&mut self, sheet: &S) {
233 let index = self.find_sheet_index(sheet);
234 if cfg!(feature = "gecko") && index.is_none() {
235 return;
237 }
238 let sheet = self.entries.remove(index.unwrap());
239 if sheet.committed {
247 self.set_data_validity_at_least(DataValidity::FullyInvalid);
248 } else {
249 self.dirty = true;
250 }
251 }
252
253 fn contains(&self, sheet: &S) -> bool {
254 self.entries.iter().any(|e| e.sheet == *sheet)
255 }
256
257 fn append(&mut self, sheet: S) {
259 debug_assert!(!self.contains(&sheet));
260 self.entries.push(StylesheetSetEntry::new(sheet));
261 self.dirty = true;
267 }
268
269 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
270 debug_assert!(!self.contains(&sheet));
271
272 let index = self
273 .find_sheet_index(before_sheet)
274 .expect("`before_sheet` stylesheet not found");
275
276 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
279 self.entries.insert(index, StylesheetSetEntry::new(sheet));
280 }
281
282 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
283 use std::cmp;
284
285 debug_assert_ne!(validity, DataValidity::Valid);
286
287 self.dirty = true;
288 self.data_validity = cmp::max(validity, self.data_validity);
289 }
290
291 fn iter(&self) -> impl Iterator<Item = &S> {
293 self.entries.iter().map(|e| &e.sheet)
294 }
295
296 fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
298 self.entries.iter_mut().map(|e| &mut e.sheet)
299 }
300
301 fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
302 let dirty = mem::replace(&mut self.dirty, false);
303 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
304
305 SheetCollectionFlusher {
306 entries: &mut self.entries,
307 dirty,
308 validity,
309 }
310 }
311}
312
313#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
315pub struct DocumentStylesheetSet<S>
316where
317 S: StylesheetInDocument + PartialEq + 'static,
318{
319 collections: PerOrigin<SheetCollection<S>>,
321
322 invalidations: StylesheetInvalidationSet,
324}
325
326macro_rules! sheet_set_methods {
333 ($set_name:expr) => {
334 fn collect_invalidations_for(
335 &mut self,
336 device: Option<&Device>,
337 custom_media: &CustomMediaMap,
338 sheet: &S,
339 guard: &SharedRwLockReadGuard,
340 ) {
341 if let Some(device) = device {
342 self.invalidations
343 .collect_invalidations_for(device, custom_media, sheet, guard);
344 }
345 }
346
347 pub fn append_stylesheet(
351 &mut self,
352 device: Option<&Device>,
353 custom_media: &CustomMediaMap,
354 sheet: S,
355 guard: &SharedRwLockReadGuard,
356 ) {
357 debug!(concat!($set_name, "::append_stylesheet"));
358 self.collect_invalidations_for(device, custom_media, &sheet, guard);
359 let collection = self.collection_for(&sheet, guard);
360 collection.append(sheet);
361 }
362
363 pub fn insert_stylesheet_before(
365 &mut self,
366 device: Option<&Device>,
367 custom_media: &CustomMediaMap,
368 sheet: S,
369 before_sheet: S,
370 guard: &SharedRwLockReadGuard,
371 ) {
372 debug!(concat!($set_name, "::insert_stylesheet_before"));
373 self.collect_invalidations_for(device, custom_media, &sheet, guard);
374
375 let collection = self.collection_for(&sheet, guard);
376 collection.insert_before(sheet, &before_sheet);
377 }
378
379 pub fn remove_stylesheet(
381 &mut self,
382 device: Option<&Device>,
383 custom_media: &CustomMediaMap,
384 sheet: S,
385 guard: &SharedRwLockReadGuard,
386 ) {
387 debug!(concat!($set_name, "::remove_stylesheet"));
388 self.collect_invalidations_for(device, custom_media, &sheet, guard);
389
390 let collection = self.collection_for(&sheet, guard);
391 collection.remove(&sheet)
392 }
393
394 pub fn rule_changed(
397 &mut self,
398 device: Option<&Device>,
399 custom_media: &CustomMediaMap,
400 sheet: &S,
401 rule: &CssRule,
402 guard: &SharedRwLockReadGuard,
403 change_kind: RuleChangeKind,
404 ancestors: &[CssRuleRef],
405 ) {
406 if let Some(device) = device {
407 let quirks_mode = device.quirks_mode();
408 self.invalidations.rule_changed(
409 sheet,
410 rule,
411 guard,
412 device,
413 quirks_mode,
414 custom_media,
415 change_kind,
416 ancestors,
417 );
418 }
419
420 let validity = match change_kind {
421 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
425 DataValidity::FullyInvalid
426 },
427 RuleChangeKind::PositionTryDeclarations | RuleChangeKind::StyleRuleDeclarations => {
442 DataValidity::FullyInvalid
443 },
444 };
445
446 let collection = self.collection_for(&sheet, guard);
447 collection.set_data_validity_at_least(validity);
448 }
449 };
450}
451
452impl<S> DocumentStylesheetSet<S>
453where
454 S: StylesheetInDocument + PartialEq + 'static,
455{
456 pub fn new() -> Self {
458 Self {
459 collections: Default::default(),
460 invalidations: StylesheetInvalidationSet::new(),
461 }
462 }
463
464 fn collection_for(
465 &mut self,
466 sheet: &S,
467 guard: &SharedRwLockReadGuard,
468 ) -> &mut SheetCollection<S> {
469 let origin = sheet.contents(guard).origin;
470 self.collections.borrow_mut_for_origin(&origin)
471 }
472
473 sheet_set_methods!("DocumentStylesheetSet");
474
475 pub fn len(&self) -> usize {
477 self.collections
478 .iter_origins()
479 .fold(0, |s, (item, _)| s + item.len())
480 }
481
482 #[inline]
484 pub fn sheet_count(&self, origin: Origin) -> usize {
485 self.collections.borrow_for_origin(&origin).len()
486 }
487
488 #[inline]
490 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
491 self.collections.borrow_for_origin(&origin).get(index)
492 }
493
494 pub fn has_changed(&self) -> bool {
496 !self.invalidations.is_empty()
497 || self
498 .collections
499 .iter_origins()
500 .any(|(collection, _)| collection.dirty)
501 }
502
503 pub fn flush(&mut self) -> (DocumentStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
506 debug!("DocumentStylesheetSet::flush");
507 (
508 DocumentStylesheetFlusher {
509 collections: &mut self.collections,
510 },
511 std::mem::take(&mut self.invalidations),
512 )
513 }
514
515 #[cfg(feature = "servo")]
517 pub fn flush_without_invalidation(&mut self) -> OriginSet {
518 debug!("DocumentStylesheetSet::flush_without_invalidation");
519
520 let mut origins = OriginSet::empty();
521 std::mem::take(&mut self.invalidations);
522
523 for (collection, origin) in self.collections.iter_mut_origins() {
524 if collection.flush().dirty() {
525 origins |= origin;
526 }
527 }
528
529 origins
530 }
531
532 pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
534 self.collections
535 .iter_origins()
536 .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
537 }
538
539 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
541 self.collections
542 .iter_mut_origins()
543 .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
544 }
545
546 pub fn force_dirty(&mut self, origins: OriginSet) {
549 self.invalidations.invalidate_fully();
550 for origin in origins.iter_origins() {
551 self.collections
553 .borrow_mut_for_origin(&origin)
554 .set_data_validity_at_least(DataValidity::FullyInvalid);
555 }
556 }
557}
558
559#[derive(MallocSizeOf)]
561pub struct AuthorStylesheetSet<S>
562where
563 S: StylesheetInDocument + PartialEq + 'static,
564{
565 collection: SheetCollection<S>,
567 invalidations: StylesheetInvalidationSet,
569}
570
571pub struct AuthorStylesheetFlusher<'a, S>
573where
574 S: StylesheetInDocument + PartialEq + 'static,
575{
576 pub sheets: SheetCollectionFlusher<'a, S>,
578}
579
580impl<S> AuthorStylesheetSet<S>
581where
582 S: StylesheetInDocument + PartialEq + 'static,
583{
584 #[inline]
586 pub fn new() -> Self {
587 Self {
588 collection: Default::default(),
589 invalidations: StylesheetInvalidationSet::new(),
590 }
591 }
592
593 pub fn dirty(&self) -> bool {
595 self.collection.dirty
596 }
597
598 pub fn is_empty(&self) -> bool {
600 self.collection.len() == 0
601 }
602
603 pub fn get(&self, index: usize) -> Option<&S> {
605 self.collection.get(index)
606 }
607
608 pub fn len(&self) -> usize {
610 self.collection.len()
611 }
612
613 fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
614 &mut self.collection
615 }
616
617 sheet_set_methods!("AuthorStylesheetSet");
618
619 pub fn iter(&self) -> impl Iterator<Item = &S> {
621 self.collection.iter()
622 }
623
624 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
626 self.collection.iter_mut()
627 }
628
629 pub fn force_dirty(&mut self) {
631 self.invalidations.invalidate_fully();
632 self.collection
633 .set_data_validity_at_least(DataValidity::FullyInvalid);
634 }
635
636 pub fn flush(&mut self) -> (AuthorStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {
638 (
639 AuthorStylesheetFlusher {
640 sheets: self.collection.flush(),
641 },
642 std::mem::take(&mut self.invalidations),
643 )
644 }
645}