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