1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! A centralized set of stylesheets for a document.
6 
7 use crate::dom::TElement;
8 use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
9 use crate::media_queries::Device;
10 use crate::selector_parser::SnapshotMap;
11 use crate::shared_lock::SharedRwLockReadGuard;
12 use crate::stylesheets::{
13     CssRule, Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument,
14 };
15 use std::{mem, slice};
16 
17 /// Entry for a StylesheetSet.
18 #[derive(MallocSizeOf)]
19 struct StylesheetSetEntry<S>
20 where
21     S: StylesheetInDocument + PartialEq + 'static,
22 {
23     /// The sheet.
24     sheet: S,
25 
26     /// Whether this sheet has been part of at least one flush.
27     committed: bool,
28 }
29 
30 impl<S> StylesheetSetEntry<S>
31 where
32     S: StylesheetInDocument + PartialEq + 'static,
33 {
new(sheet: S) -> Self34     fn new(sheet: S) -> Self {
35         Self {
36             sheet,
37             committed: false,
38         }
39     }
40 }
41 
42 /// A iterator over the stylesheets of a list of entries in the StylesheetSet.
43 pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
44 where
45     S: StylesheetInDocument + PartialEq + 'static;
46 
47 impl<'a, S> Clone for StylesheetCollectionIterator<'a, S>
48 where
49     S: StylesheetInDocument + PartialEq + 'static,
50 {
clone(&self) -> Self51     fn clone(&self) -> Self {
52         StylesheetCollectionIterator(self.0.clone())
53     }
54 }
55 
56 impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S>
57 where
58     S: StylesheetInDocument + PartialEq + 'static,
59 {
60     type Item = &'a S;
61 
next(&mut self) -> Option<Self::Item>62     fn next(&mut self) -> Option<Self::Item> {
63         self.0.next().map(|entry| &entry.sheet)
64     }
65 
size_hint(&self) -> (usize, Option<usize>)66     fn size_hint(&self) -> (usize, Option<usize>) {
67         self.0.size_hint()
68     }
69 }
70 
71 /// An iterator over the flattened view of the stylesheet collections.
72 #[derive(Clone)]
73 pub struct StylesheetIterator<'a, S>
74 where
75     S: StylesheetInDocument + PartialEq + 'static,
76 {
77     origins: OriginSetIterator,
78     collections: &'a PerOrigin<SheetCollection<S>>,
79     current: Option<(Origin, StylesheetCollectionIterator<'a, S>)>,
80 }
81 
82 impl<'a, S> Iterator for StylesheetIterator<'a, S>
83 where
84     S: StylesheetInDocument + PartialEq + 'static,
85 {
86     type Item = (&'a S, Origin);
87 
next(&mut self) -> Option<Self::Item>88     fn next(&mut self) -> Option<Self::Item> {
89         loop {
90             if self.current.is_none() {
91                 let next_origin = self.origins.next()?;
92 
93                 self.current = Some((
94                     next_origin,
95                     self.collections.borrow_for_origin(&next_origin).iter(),
96                 ));
97             }
98 
99             {
100                 let (origin, ref mut iter) = *self.current.as_mut().unwrap();
101                 if let Some(s) = iter.next() {
102                     return Some((s, origin));
103                 }
104             }
105 
106             self.current = None;
107         }
108     }
109 }
110 
111 /// The validity of the data in a given cascade origin.
112 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
113 pub enum DataValidity {
114     /// The origin is clean, all the data already there is valid, though we may
115     /// have new sheets at the end.
116     Valid = 0,
117 
118     /// The cascade data is invalid, but not the invalidation data (which is
119     /// order-independent), and thus only the cascade data should be inserted.
120     CascadeInvalid = 1,
121 
122     /// Everything needs to be rebuilt.
123     FullyInvalid = 2,
124 }
125 
126 impl Default for DataValidity {
default() -> Self127     fn default() -> Self {
128         DataValidity::Valid
129     }
130 }
131 
132 /// A struct to iterate over the different stylesheets to be flushed.
133 pub struct DocumentStylesheetFlusher<'a, S>
134 where
135     S: StylesheetInDocument + PartialEq + 'static,
136 {
137     collections: &'a mut PerOrigin<SheetCollection<S>>,
138     had_invalidations: bool,
139 }
140 
141 /// The type of rebuild that we need to do for a given stylesheet.
142 #[derive(Clone, Copy, Debug)]
143 pub enum SheetRebuildKind {
144     /// A full rebuild, of both cascade data and invalidation data.
145     Full,
146     /// A partial rebuild, of only the cascade data.
147     CascadeOnly,
148 }
149 
150 impl SheetRebuildKind {
151     /// Whether the stylesheet invalidation data should be rebuilt.
should_rebuild_invalidation(&self) -> bool152     pub fn should_rebuild_invalidation(&self) -> bool {
153         matches!(*self, SheetRebuildKind::Full)
154     }
155 }
156 
157 impl<'a, S> DocumentStylesheetFlusher<'a, S>
158 where
159     S: StylesheetInDocument + PartialEq + 'static,
160 {
161     /// Returns a flusher for `origin`.
flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S>162     pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S> {
163         self.collections.borrow_mut_for_origin(&origin).flush()
164     }
165 
166     /// Returns the list of stylesheets for `origin`.
167     ///
168     /// Only used for UA sheets.
origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S>169     pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S> {
170         self.collections.borrow_mut_for_origin(&origin).iter()
171     }
172 
173     /// Returns whether any DOM invalidations were processed as a result of the
174     /// stylesheet flush.
175     #[inline]
had_invalidations(&self) -> bool176     pub fn had_invalidations(&self) -> bool {
177         self.had_invalidations
178     }
179 }
180 
181 /// A flusher struct for a given collection, that takes care of returning the
182 /// appropriate stylesheets that need work.
183 pub struct SheetCollectionFlusher<'a, S>
184 where
185     S: StylesheetInDocument + PartialEq + 'static,
186 {
187     // TODO: This can be made an iterator again once
188     // https://github.com/rust-lang/rust/pull/82771 lands on stable.
189     entries: &'a mut [StylesheetSetEntry<S>],
190     validity: DataValidity,
191     dirty: bool,
192 }
193 
194 impl<'a, S> SheetCollectionFlusher<'a, S>
195 where
196     S: StylesheetInDocument + PartialEq + 'static,
197 {
198     /// Whether the collection was originally dirty.
199     #[inline]
dirty(&self) -> bool200     pub fn dirty(&self) -> bool {
201         self.dirty
202     }
203 
204     /// What the state of the sheet data is.
205     #[inline]
data_validity(&self) -> DataValidity206     pub fn data_validity(&self) -> DataValidity {
207         self.validity
208     }
209 
210     /// Returns an iterator over the remaining list of sheets to consume.
sheets<'b>(&'b self) -> impl Iterator<Item = &'b S>211     pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
212         self.entries.iter().map(|entry| &entry.sheet)
213     }
214 }
215 
216 impl<'a, S> SheetCollectionFlusher<'a, S>
217 where
218     S: StylesheetInDocument + PartialEq + 'static,
219 {
220     /// Iterates over all sheets and values that we have to invalidate.
221     ///
222     /// TODO(emilio): This would be nicer as an iterator but we can't do that
223     /// until https://github.com/rust-lang/rust/pull/82771 stabilizes.
224     ///
225     /// Since we don't have a good use-case for partial iteration, this does the
226     /// trick for now.
each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool)227     pub fn each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool) {
228         for potential_sheet in self.entries.iter_mut() {
229             let committed = mem::replace(&mut potential_sheet.committed, true);
230             let rebuild_kind = if !committed {
231                 // If the sheet was uncommitted, we need to do a full rebuild
232                 // anyway.
233                 SheetRebuildKind::Full
234             } else {
235                 match self.validity {
236                     DataValidity::Valid => continue,
237                     DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
238                     DataValidity::FullyInvalid => SheetRebuildKind::Full,
239                 }
240             };
241 
242             if !callback(&potential_sheet.sheet, rebuild_kind) {
243                 return;
244             }
245         }
246     }
247 }
248 
249 #[derive(MallocSizeOf)]
250 struct SheetCollection<S>
251 where
252     S: StylesheetInDocument + PartialEq + 'static,
253 {
254     /// The actual list of stylesheets.
255     ///
256     /// This is only a list of top-level stylesheets, and as such it doesn't
257     /// include recursive `@import` rules.
258     entries: Vec<StylesheetSetEntry<S>>,
259 
260     /// The validity of the data that was already there for a given origin.
261     ///
262     /// Note that an origin may appear on `origins_dirty`, but still have
263     /// `DataValidity::Valid`, if only sheets have been appended into it (in
264     /// which case the existing data is valid, but the origin needs to be
265     /// rebuilt).
266     data_validity: DataValidity,
267 
268     /// Whether anything in the collection has changed. Note that this is
269     /// different from `data_validity`, in the sense that after a sheet append,
270     /// the data validity is still `Valid`, but we need to be marked as dirty.
271     dirty: bool,
272 }
273 
274 impl<S> Default for SheetCollection<S>
275 where
276     S: StylesheetInDocument + PartialEq + 'static,
277 {
default() -> Self278     fn default() -> Self {
279         Self {
280             entries: vec![],
281             data_validity: DataValidity::Valid,
282             dirty: false,
283         }
284     }
285 }
286 
287 impl<S> SheetCollection<S>
288 where
289     S: StylesheetInDocument + PartialEq + 'static,
290 {
291     /// Returns the number of stylesheets in the set.
len(&self) -> usize292     fn len(&self) -> usize {
293         self.entries.len()
294     }
295 
296     /// Returns the `index`th stylesheet in the set if present.
get(&self, index: usize) -> Option<&S>297     fn get(&self, index: usize) -> Option<&S> {
298         self.entries.get(index).map(|e| &e.sheet)
299     }
300 
remove(&mut self, sheet: &S)301     fn remove(&mut self, sheet: &S) {
302         let index = self.entries.iter().position(|entry| entry.sheet == *sheet);
303         if cfg!(feature = "gecko") && index.is_none() {
304             // FIXME(emilio): Make Gecko's PresShell::AddUserSheet not suck.
305             return;
306         }
307         let sheet = self.entries.remove(index.unwrap());
308         // Removing sheets makes us tear down the whole cascade and invalidation
309         // data, but only if the sheet has been involved in at least one flush.
310         // Checking whether the sheet has been committed allows us to avoid
311         // rebuilding the world when sites quickly append and remove a
312         // stylesheet.
313         //
314         // See bug 1434756.
315         if sheet.committed {
316             self.set_data_validity_at_least(DataValidity::FullyInvalid);
317         } else {
318             self.dirty = true;
319         }
320     }
321 
contains(&self, sheet: &S) -> bool322     fn contains(&self, sheet: &S) -> bool {
323         self.entries.iter().any(|e| e.sheet == *sheet)
324     }
325 
326     /// Appends a given sheet into the collection.
append(&mut self, sheet: S)327     fn append(&mut self, sheet: S) {
328         debug_assert!(!self.contains(&sheet));
329         self.entries.push(StylesheetSetEntry::new(sheet));
330         // Appending sheets doesn't alter the validity of the existing data, so
331         // we don't need to change `data_validity` here.
332         //
333         // But we need to be marked as dirty, otherwise we'll never add the new
334         // sheet!
335         self.dirty = true;
336     }
337 
insert_before(&mut self, sheet: S, before_sheet: &S)338     fn insert_before(&mut self, sheet: S, before_sheet: &S) {
339         debug_assert!(!self.contains(&sheet));
340 
341         let index = self
342             .entries
343             .iter()
344             .position(|entry| entry.sheet == *before_sheet)
345             .expect("`before_sheet` stylesheet not found");
346 
347         // Inserting stylesheets somewhere but at the end changes the validity
348         // of the cascade data, but not the invalidation data.
349         self.set_data_validity_at_least(DataValidity::CascadeInvalid);
350         self.entries.insert(index, StylesheetSetEntry::new(sheet));
351     }
352 
set_data_validity_at_least(&mut self, validity: DataValidity)353     fn set_data_validity_at_least(&mut self, validity: DataValidity) {
354         use std::cmp;
355 
356         debug_assert_ne!(validity, DataValidity::Valid);
357 
358         self.dirty = true;
359         self.data_validity = cmp::max(validity, self.data_validity);
360     }
361 
362     /// Returns an iterator over the current list of stylesheets.
iter(&self) -> StylesheetCollectionIterator<S>363     fn iter(&self) -> StylesheetCollectionIterator<S> {
364         StylesheetCollectionIterator(self.entries.iter())
365     }
366 
flush(&mut self) -> SheetCollectionFlusher<S>367     fn flush(&mut self) -> SheetCollectionFlusher<S> {
368         let dirty = mem::replace(&mut self.dirty, false);
369         let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
370 
371         SheetCollectionFlusher {
372             entries: &mut self.entries,
373             dirty,
374             validity,
375         }
376     }
377 }
378 
379 /// The set of stylesheets effective for a given document.
380 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
381 pub struct DocumentStylesheetSet<S>
382 where
383     S: StylesheetInDocument + PartialEq + 'static,
384 {
385     /// The collections of sheets per each origin.
386     collections: PerOrigin<SheetCollection<S>>,
387 
388     /// The invalidations for stylesheets added or removed from this document.
389     invalidations: StylesheetInvalidationSet,
390 }
391 
392 /// This macro defines methods common to DocumentStylesheetSet and
393 /// AuthorStylesheetSet.
394 ///
395 /// We could simplify the setup moving invalidations to SheetCollection, but
396 /// that would imply not sharing invalidations across origins of the same
397 /// documents, which is slightly annoying.
398 macro_rules! sheet_set_methods {
399     ($set_name:expr) => {
400         fn collect_invalidations_for(
401             &mut self,
402             device: Option<&Device>,
403             sheet: &S,
404             guard: &SharedRwLockReadGuard,
405         ) {
406             if let Some(device) = device {
407                 self.invalidations
408                     .collect_invalidations_for(device, sheet, guard);
409             }
410         }
411 
412         /// Appends a new stylesheet to the current set.
413         ///
414         /// No device implies not computing invalidations.
415         pub fn append_stylesheet(
416             &mut self,
417             device: Option<&Device>,
418             sheet: S,
419             guard: &SharedRwLockReadGuard,
420         ) {
421             debug!(concat!($set_name, "::append_stylesheet"));
422             self.collect_invalidations_for(device, &sheet, guard);
423             let collection = self.collection_for(&sheet);
424             collection.append(sheet);
425         }
426 
427         /// Insert a given stylesheet before another stylesheet in the document.
428         pub fn insert_stylesheet_before(
429             &mut self,
430             device: Option<&Device>,
431             sheet: S,
432             before_sheet: S,
433             guard: &SharedRwLockReadGuard,
434         ) {
435             debug!(concat!($set_name, "::insert_stylesheet_before"));
436             self.collect_invalidations_for(device, &sheet, guard);
437 
438             let collection = self.collection_for(&sheet);
439             collection.insert_before(sheet, &before_sheet);
440         }
441 
442         /// Remove a given stylesheet from the set.
443         pub fn remove_stylesheet(
444             &mut self,
445             device: Option<&Device>,
446             sheet: S,
447             guard: &SharedRwLockReadGuard,
448         ) {
449             debug!(concat!($set_name, "::remove_stylesheet"));
450             self.collect_invalidations_for(device, &sheet, guard);
451 
452             let collection = self.collection_for(&sheet);
453             collection.remove(&sheet)
454         }
455 
456         /// Notify the set that a rule from a given stylesheet has changed
457         /// somehow.
458         pub fn rule_changed(
459             &mut self,
460             device: Option<&Device>,
461             sheet: &S,
462             rule: &CssRule,
463             guard: &SharedRwLockReadGuard,
464             change_kind: RuleChangeKind,
465         ) {
466             if let Some(device) = device {
467                 let quirks_mode = device.quirks_mode();
468                 self.invalidations.rule_changed(
469                     sheet,
470                     rule,
471                     guard,
472                     device,
473                     quirks_mode,
474                     change_kind,
475                 );
476             }
477 
478             let validity = match change_kind {
479                 // Insertion / Removals need to rebuild both the cascade and
480                 // invalidation data. For generic changes this is conservative,
481                 // could be optimized on a per-case basis.
482                 RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
483                     DataValidity::FullyInvalid
484                 },
485                 // TODO(emilio): This, in theory, doesn't need to invalidate
486                 // style data, if the rule we're modifying is actually in the
487                 // CascadeData already.
488                 //
489                 // But this is actually a bit tricky to prove, because when we
490                 // copy-on-write a stylesheet we don't bother doing a rebuild,
491                 // so we may still have rules from the original stylesheet
492                 // instead of the cloned one that we're modifying. So don't
493                 // bother for now and unconditionally rebuild, it's no worse
494                 // than what we were already doing anyway.
495                 //
496                 // Maybe we could record whether we saw a clone in this flush,
497                 // and if so do the conservative thing, otherwise just
498                 // early-return.
499                 RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
500             };
501 
502             let collection = self.collection_for(&sheet);
503             collection.set_data_validity_at_least(validity);
504         }
505     };
506 }
507 
508 impl<S> DocumentStylesheetSet<S>
509 where
510     S: StylesheetInDocument + PartialEq + 'static,
511 {
512     /// Create a new empty DocumentStylesheetSet.
new() -> Self513     pub fn new() -> Self {
514         Self {
515             collections: Default::default(),
516             invalidations: StylesheetInvalidationSet::new(),
517         }
518     }
519 
collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S>520     fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
521         let origin = sheet.contents().origin;
522         self.collections.borrow_mut_for_origin(&origin)
523     }
524 
525     sheet_set_methods!("DocumentStylesheetSet");
526 
527     /// Returns the number of stylesheets in the set.
len(&self) -> usize528     pub fn len(&self) -> usize {
529         self.collections
530             .iter_origins()
531             .fold(0, |s, (item, _)| s + item.len())
532     }
533 
534     /// Returns the count of stylesheets for a given origin.
535     #[inline]
sheet_count(&self, origin: Origin) -> usize536     pub fn sheet_count(&self, origin: Origin) -> usize {
537         self.collections.borrow_for_origin(&origin).len()
538     }
539 
540     /// Returns the `index`th stylesheet in the set for the given origin.
541     #[inline]
get(&self, origin: Origin, index: usize) -> Option<&S>542     pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
543         self.collections.borrow_for_origin(&origin).get(index)
544     }
545 
546     /// Returns whether the given set has changed from the last flush.
has_changed(&self) -> bool547     pub fn has_changed(&self) -> bool {
548         !self.invalidations.is_empty() ||
549             self.collections
550                 .iter_origins()
551                 .any(|(collection, _)| collection.dirty)
552     }
553 
554     /// Flush the current set, unmarking it as dirty, and returns a
555     /// `DocumentStylesheetFlusher` in order to rebuild the stylist.
flush<E>( &mut self, document_element: Option<E>, snapshots: Option<&SnapshotMap>, ) -> DocumentStylesheetFlusher<S> where E: TElement,556     pub fn flush<E>(
557         &mut self,
558         document_element: Option<E>,
559         snapshots: Option<&SnapshotMap>,
560     ) -> DocumentStylesheetFlusher<S>
561     where
562         E: TElement,
563     {
564         debug!("DocumentStylesheetSet::flush");
565 
566         let had_invalidations = self.invalidations.flush(document_element, snapshots);
567 
568         DocumentStylesheetFlusher {
569             collections: &mut self.collections,
570             had_invalidations,
571         }
572     }
573 
574     /// Flush stylesheets, but without running any of the invalidation passes.
575     #[cfg(feature = "servo")]
flush_without_invalidation(&mut self) -> OriginSet576     pub fn flush_without_invalidation(&mut self) -> OriginSet {
577         debug!("DocumentStylesheetSet::flush_without_invalidation");
578 
579         let mut origins = OriginSet::empty();
580         self.invalidations.clear();
581 
582         for (collection, origin) in self.collections.iter_mut_origins() {
583             if collection.flush().dirty() {
584                 origins |= origin;
585             }
586         }
587 
588         origins
589     }
590 
591     /// Return an iterator over the flattened view of all the stylesheets.
iter(&self) -> StylesheetIterator<S>592     pub fn iter(&self) -> StylesheetIterator<S> {
593         StylesheetIterator {
594             origins: OriginSet::all().iter(),
595             collections: &self.collections,
596             current: None,
597         }
598     }
599 
600     /// Mark the stylesheets for the specified origin as dirty, because
601     /// something external may have invalidated it.
force_dirty(&mut self, origins: OriginSet)602     pub fn force_dirty(&mut self, origins: OriginSet) {
603         self.invalidations.invalidate_fully();
604         for origin in origins.iter() {
605             // We don't know what happened, assume the worse.
606             self.collections
607                 .borrow_mut_for_origin(&origin)
608                 .set_data_validity_at_least(DataValidity::FullyInvalid);
609         }
610     }
611 }
612 
613 /// The set of stylesheets effective for a given Shadow Root.
614 #[derive(MallocSizeOf)]
615 pub struct AuthorStylesheetSet<S>
616 where
617     S: StylesheetInDocument + PartialEq + 'static,
618 {
619     /// The actual style sheets.
620     collection: SheetCollection<S>,
621     /// The set of invalidations scheduled for this collection.
622     invalidations: StylesheetInvalidationSet,
623 }
624 
625 /// A struct to flush an author style sheet collection.
626 pub struct AuthorStylesheetFlusher<'a, S>
627 where
628     S: StylesheetInDocument + PartialEq + 'static,
629 {
630     /// The actual flusher for the collection.
631     pub sheets: SheetCollectionFlusher<'a, S>,
632     /// Whether any sheet invalidation matched.
633     pub had_invalidations: bool,
634 }
635 
636 impl<S> AuthorStylesheetSet<S>
637 where
638     S: StylesheetInDocument + PartialEq + 'static,
639 {
640     /// Create a new empty AuthorStylesheetSet.
641     #[inline]
new() -> Self642     pub fn new() -> Self {
643         Self {
644             collection: Default::default(),
645             invalidations: StylesheetInvalidationSet::new(),
646         }
647     }
648 
649     /// Whether anything has changed since the last time this was flushed.
dirty(&self) -> bool650     pub fn dirty(&self) -> bool {
651         self.collection.dirty
652     }
653 
654     /// Whether the collection is empty.
is_empty(&self) -> bool655     pub fn is_empty(&self) -> bool {
656         self.collection.len() == 0
657     }
658 
659     /// Returns the `index`th stylesheet in the collection of author styles if present.
get(&self, index: usize) -> Option<&S>660     pub fn get(&self, index: usize) -> Option<&S> {
661         self.collection.get(index)
662     }
663 
664     /// Returns the number of author stylesheets.
len(&self) -> usize665     pub fn len(&self) -> usize {
666         self.collection.len()
667     }
668 
collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S>669     fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
670         &mut self.collection
671     }
672 
673     sheet_set_methods!("AuthorStylesheetSet");
674 
675     /// Iterate over the list of stylesheets.
iter(&self) -> StylesheetCollectionIterator<S>676     pub fn iter(&self) -> StylesheetCollectionIterator<S> {
677         self.collection.iter()
678     }
679 
680     /// Mark the sheet set dirty, as appropriate.
force_dirty(&mut self)681     pub fn force_dirty(&mut self) {
682         self.invalidations.invalidate_fully();
683         self.collection
684             .set_data_validity_at_least(DataValidity::FullyInvalid);
685     }
686 
687     /// Flush the stylesheets for this author set.
688     ///
689     /// `host` is the root of the affected subtree, like the shadow host, for
690     /// example.
flush<E>( &mut self, host: Option<E>, snapshots: Option<&SnapshotMap>, ) -> AuthorStylesheetFlusher<S> where E: TElement,691     pub fn flush<E>(
692         &mut self,
693         host: Option<E>,
694         snapshots: Option<&SnapshotMap>,
695     ) -> AuthorStylesheetFlusher<S>
696     where
697         E: TElement,
698     {
699         let had_invalidations = self.invalidations.flush(host, snapshots);
700         AuthorStylesheetFlusher {
701             sheets: self.collection.flush(),
702             had_invalidations,
703         }
704     }
705 }
706