1use crate::derives::*;
8use crate::dom::TElement;
9use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
10use crate::media_queries::Device;
11use crate::selector_parser::SnapshotMap;
12use crate::shared_lock::SharedRwLockReadGuard;
13use crate::stylesheets::{
14 CssRule, CssRuleRef, CustomMediaMap, Origin, OriginSet, PerOrigin, StylesheetInDocument,
15};
16use std::mem;
17
18#[derive(MallocSizeOf)]
20struct StylesheetSetEntry<S>
21where
22 S: StylesheetInDocument + PartialEq + 'static,
23{
24 sheet: S,
26
27 committed: bool,
29}
30
31impl<S> StylesheetSetEntry<S>
32where
33 S: StylesheetInDocument + PartialEq + 'static,
34{
35 fn new(sheet: S) -> Self {
36 Self {
37 sheet,
38 committed: false,
39 }
40 }
41}
42
43#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
45pub enum DataValidity {
46 Valid = 0,
49
50 CascadeInvalid = 1,
53
54 FullyInvalid = 2,
56}
57
58impl Default for DataValidity {
59 fn default() -> Self {
60 DataValidity::Valid
61 }
62}
63
64pub struct DocumentStylesheetFlusher<'a, S>
66where
67 S: StylesheetInDocument + PartialEq + 'static,
68{
69 collections: &'a mut PerOrigin<SheetCollection<S>>,
70 had_invalidations: bool,
71}
72
73#[derive(Clone, Copy, Debug)]
75pub enum SheetRebuildKind {
76 Full,
78 CascadeOnly,
80}
81
82impl SheetRebuildKind {
83 pub fn should_rebuild_invalidation(&self) -> bool {
85 matches!(*self, SheetRebuildKind::Full)
86 }
87}
88
89impl<'a, S> DocumentStylesheetFlusher<'a, S>
90where
91 S: StylesheetInDocument + PartialEq + 'static,
92{
93 pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<'_, S> {
95 self.collections.borrow_mut_for_origin(&origin).flush()
96 }
97
98 pub fn origin_sheets(&self, origin: Origin) -> impl Iterator<Item = &S> {
102 self.collections.borrow_for_origin(&origin).iter()
103 }
104
105 #[inline]
108 pub fn had_invalidations(&self) -> bool {
109 self.had_invalidations
110 }
111}
112
113pub struct SheetCollectionFlusher<'a, S>
116where
117 S: StylesheetInDocument + PartialEq + 'static,
118{
119 entries: &'a mut [StylesheetSetEntry<S>],
122 validity: DataValidity,
123 dirty: bool,
124}
125
126impl<'a, S> SheetCollectionFlusher<'a, S>
127where
128 S: StylesheetInDocument + PartialEq + 'static,
129{
130 #[inline]
132 pub fn dirty(&self) -> bool {
133 self.dirty
134 }
135
136 #[inline]
138 pub fn data_validity(&self) -> DataValidity {
139 self.validity
140 }
141
142 pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
144 self.entries.iter().map(|entry| &entry.sheet)
145 }
146}
147
148impl<'a, S> SheetCollectionFlusher<'a, S>
149where
150 S: StylesheetInDocument + PartialEq + 'static,
151{
152 pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {
160 for (index, potential_sheet) in self.entries.iter_mut().enumerate() {
161 let committed = mem::replace(&mut potential_sheet.committed, true);
162 let rebuild_kind = if !committed {
163 SheetRebuildKind::Full
166 } else {
167 match self.validity {
168 DataValidity::Valid => continue,
169 DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
170 DataValidity::FullyInvalid => SheetRebuildKind::Full,
171 }
172 };
173
174 if !callback(index, &potential_sheet.sheet, rebuild_kind) {
175 return;
176 }
177 }
178 }
179}
180
181#[derive(MallocSizeOf)]
182struct SheetCollection<S>
183where
184 S: StylesheetInDocument + PartialEq + 'static,
185{
186 entries: Vec<StylesheetSetEntry<S>>,
191
192 data_validity: DataValidity,
199
200 dirty: bool,
204}
205
206impl<S> Default for SheetCollection<S>
207where
208 S: StylesheetInDocument + PartialEq + 'static,
209{
210 fn default() -> Self {
211 Self {
212 entries: vec![],
213 data_validity: DataValidity::Valid,
214 dirty: false,
215 }
216 }
217}
218
219impl<S> SheetCollection<S>
220where
221 S: StylesheetInDocument + PartialEq + 'static,
222{
223 fn len(&self) -> usize {
225 self.entries.len()
226 }
227
228 fn get(&self, index: usize) -> Option<&S> {
230 self.entries.get(index).map(|e| &e.sheet)
231 }
232
233 fn find_sheet_index(&self, sheet: &S) -> Option<usize> {
234 let rev_pos = self
235 .entries
236 .iter()
237 .rev()
238 .position(|entry| entry.sheet == *sheet);
239 rev_pos.map(|i| self.entries.len() - i - 1)
240 }
241
242 fn remove(&mut self, sheet: &S) {
243 let index = self.find_sheet_index(sheet);
244 if cfg!(feature = "gecko") && index.is_none() {
245 return;
247 }
248 let sheet = self.entries.remove(index.unwrap());
249 if sheet.committed {
257 self.set_data_validity_at_least(DataValidity::FullyInvalid);
258 } else {
259 self.dirty = true;
260 }
261 }
262
263 fn contains(&self, sheet: &S) -> bool {
264 self.entries.iter().any(|e| e.sheet == *sheet)
265 }
266
267 fn append(&mut self, sheet: S) {
269 debug_assert!(!self.contains(&sheet));
270 self.entries.push(StylesheetSetEntry::new(sheet));
271 self.dirty = true;
277 }
278
279 fn insert_before(&mut self, sheet: S, before_sheet: &S) {
280 debug_assert!(!self.contains(&sheet));
281
282 let index = self
283 .find_sheet_index(before_sheet)
284 .expect("`before_sheet` stylesheet not found");
285
286 self.set_data_validity_at_least(DataValidity::CascadeInvalid);
289 self.entries.insert(index, StylesheetSetEntry::new(sheet));
290 }
291
292 fn set_data_validity_at_least(&mut self, validity: DataValidity) {
293 use std::cmp;
294
295 debug_assert_ne!(validity, DataValidity::Valid);
296
297 self.dirty = true;
298 self.data_validity = cmp::max(validity, self.data_validity);
299 }
300
301 fn iter(&self) -> impl Iterator<Item = &S> {
303 self.entries.iter().map(|e| &e.sheet)
304 }
305
306 fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
308 self.entries.iter_mut().map(|e| &mut e.sheet)
309 }
310
311 fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {
312 let dirty = mem::replace(&mut self.dirty, false);
313 let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
314
315 SheetCollectionFlusher {
316 entries: &mut self.entries,
317 dirty,
318 validity,
319 }
320 }
321}
322
323#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
325pub struct DocumentStylesheetSet<S>
326where
327 S: StylesheetInDocument + PartialEq + 'static,
328{
329 collections: PerOrigin<SheetCollection<S>>,
331
332 invalidations: StylesheetInvalidationSet,
334}
335
336macro_rules! sheet_set_methods {
343 ($set_name:expr) => {
344 fn collect_invalidations_for(
345 &mut self,
346 device: Option<&Device>,
347 custom_media: &CustomMediaMap,
348 sheet: &S,
349 guard: &SharedRwLockReadGuard,
350 ) {
351 if let Some(device) = device {
352 self.invalidations
353 .collect_invalidations_for(device, custom_media, sheet, guard);
354 }
355 }
356
357 pub fn append_stylesheet(
361 &mut self,
362 device: Option<&Device>,
363 custom_media: &CustomMediaMap,
364 sheet: S,
365 guard: &SharedRwLockReadGuard,
366 ) {
367 debug!(concat!($set_name, "::append_stylesheet"));
368 self.collect_invalidations_for(device, custom_media, &sheet, guard);
369 let collection = self.collection_for(&sheet, guard);
370 collection.append(sheet);
371 }
372
373 pub fn insert_stylesheet_before(
375 &mut self,
376 device: Option<&Device>,
377 custom_media: &CustomMediaMap,
378 sheet: S,
379 before_sheet: S,
380 guard: &SharedRwLockReadGuard,
381 ) {
382 debug!(concat!($set_name, "::insert_stylesheet_before"));
383 self.collect_invalidations_for(device, custom_media, &sheet, guard);
384
385 let collection = self.collection_for(&sheet, guard);
386 collection.insert_before(sheet, &before_sheet);
387 }
388
389 pub fn remove_stylesheet(
391 &mut self,
392 device: Option<&Device>,
393 custom_media: &CustomMediaMap,
394 sheet: S,
395 guard: &SharedRwLockReadGuard,
396 ) {
397 debug!(concat!($set_name, "::remove_stylesheet"));
398 self.collect_invalidations_for(device, custom_media, &sheet, guard);
399
400 let collection = self.collection_for(&sheet, guard);
401 collection.remove(&sheet)
402 }
403
404 pub fn rule_changed(
407 &mut self,
408 device: Option<&Device>,
409 custom_media: &CustomMediaMap,
410 sheet: &S,
411 rule: &CssRule,
412 guard: &SharedRwLockReadGuard,
413 change_kind: RuleChangeKind,
414 ancestors: &[CssRuleRef],
415 ) {
416 if let Some(device) = device {
417 let quirks_mode = device.quirks_mode();
418 self.invalidations.rule_changed(
419 sheet,
420 rule,
421 guard,
422 device,
423 quirks_mode,
424 custom_media,
425 change_kind,
426 ancestors,
427 );
428 }
429
430 let validity = match change_kind {
431 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
435 DataValidity::FullyInvalid
436 },
437 RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
452 };
453
454 let collection = self.collection_for(&sheet, guard);
455 collection.set_data_validity_at_least(validity);
456 }
457 };
458}
459
460impl<S> DocumentStylesheetSet<S>
461where
462 S: StylesheetInDocument + PartialEq + 'static,
463{
464 pub fn new() -> Self {
466 Self {
467 collections: Default::default(),
468 invalidations: StylesheetInvalidationSet::new(),
469 }
470 }
471
472 fn collection_for(
473 &mut self,
474 sheet: &S,
475 guard: &SharedRwLockReadGuard,
476 ) -> &mut SheetCollection<S> {
477 let origin = sheet.contents(guard).origin;
478 self.collections.borrow_mut_for_origin(&origin)
479 }
480
481 sheet_set_methods!("DocumentStylesheetSet");
482
483 pub fn len(&self) -> usize {
485 self.collections
486 .iter_origins()
487 .fold(0, |s, (item, _)| s + item.len())
488 }
489
490 #[inline]
492 pub fn sheet_count(&self, origin: Origin) -> usize {
493 self.collections.borrow_for_origin(&origin).len()
494 }
495
496 #[inline]
498 pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
499 self.collections.borrow_for_origin(&origin).get(index)
500 }
501
502 pub fn has_changed(&self) -> bool {
504 !self.invalidations.is_empty()
505 || self
506 .collections
507 .iter_origins()
508 .any(|(collection, _)| collection.dirty)
509 }
510
511 pub fn flush<E>(
514 &mut self,
515 document_element: Option<E>,
516 snapshots: Option<&SnapshotMap>,
517 ) -> DocumentStylesheetFlusher<'_, S>
518 where
519 E: TElement,
520 {
521 debug!("DocumentStylesheetSet::flush");
522
523 let had_invalidations = self.invalidations.flush(document_element, snapshots);
524
525 DocumentStylesheetFlusher {
526 collections: &mut self.collections,
527 had_invalidations,
528 }
529 }
530
531 #[cfg(feature = "servo")]
533 pub fn flush_without_invalidation(&mut self) -> OriginSet {
534 debug!("DocumentStylesheetSet::flush_without_invalidation");
535
536 let mut origins = OriginSet::empty();
537 self.invalidations.clear();
538
539 for (collection, origin) in self.collections.iter_mut_origins() {
540 if collection.flush().dirty() {
541 origins |= origin;
542 }
543 }
544
545 origins
546 }
547
548 pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {
550 self.collections
551 .iter_origins()
552 .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))
553 }
554
555 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {
557 self.collections
558 .iter_mut_origins()
559 .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))
560 }
561
562 pub fn force_dirty(&mut self, origins: OriginSet) {
565 self.invalidations.invalidate_fully();
566 for origin in origins.iter_origins() {
567 self.collections
569 .borrow_mut_for_origin(&origin)
570 .set_data_validity_at_least(DataValidity::FullyInvalid);
571 }
572 }
573}
574
575#[derive(MallocSizeOf)]
577pub struct AuthorStylesheetSet<S>
578where
579 S: StylesheetInDocument + PartialEq + 'static,
580{
581 collection: SheetCollection<S>,
583 invalidations: StylesheetInvalidationSet,
585}
586
587pub struct AuthorStylesheetFlusher<'a, S>
589where
590 S: StylesheetInDocument + PartialEq + 'static,
591{
592 pub sheets: SheetCollectionFlusher<'a, S>,
594 pub had_invalidations: bool,
596}
597
598impl<S> AuthorStylesheetSet<S>
599where
600 S: StylesheetInDocument + PartialEq + 'static,
601{
602 #[inline]
604 pub fn new() -> Self {
605 Self {
606 collection: Default::default(),
607 invalidations: StylesheetInvalidationSet::new(),
608 }
609 }
610
611 pub fn dirty(&self) -> bool {
613 self.collection.dirty
614 }
615
616 pub fn is_empty(&self) -> bool {
618 self.collection.len() == 0
619 }
620
621 pub fn get(&self, index: usize) -> Option<&S> {
623 self.collection.get(index)
624 }
625
626 pub fn len(&self) -> usize {
628 self.collection.len()
629 }
630
631 fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {
632 &mut self.collection
633 }
634
635 sheet_set_methods!("AuthorStylesheetSet");
636
637 pub fn iter(&self) -> impl Iterator<Item = &S> {
639 self.collection.iter()
640 }
641
642 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {
644 self.collection.iter_mut()
645 }
646
647 pub fn force_dirty(&mut self) {
649 self.invalidations.invalidate_fully();
650 self.collection
651 .set_data_validity_at_least(DataValidity::FullyInvalid);
652 }
653
654 pub fn flush<E>(
659 &mut self,
660 host: Option<E>,
661 snapshots: Option<&SnapshotMap>,
662 ) -> AuthorStylesheetFlusher<'_, S>
663 where
664 E: TElement,
665 {
666 let had_invalidations = self.invalidations.flush(host, snapshots);
667 AuthorStylesheetFlusher {
668 sheets: self.collection.flush(),
669 had_invalidations,
670 }
671 }
672}