script/dom/
stylesheetcontentscache.rs1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::collections::hash_map::Entry;
8use std::hash::{DefaultHasher, Hash, Hasher};
9use std::rc::Rc;
10
11use servo_arc::Arc as ServoArc;
12use style::context::QuirksMode;
13use style::shared_lock::SharedRwLock;
14use style::stylesheets::{CssRule, StylesheetContents, UrlExtraData};
15use stylo_atoms::Atom;
16
17const MAX_LENGTH_OF_TEXT_INSERTED_INTO_TABLE: usize = 1024;
18const UNIQUE_OWNED: usize = 2;
19
20#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq)]
28pub(crate) struct StylesheetContentsCacheKey {
29 #[conditional_malloc_size_of]
30 stylesheet_text: Rc<Atom>,
31 base_url: Atom,
32 #[ignore_malloc_size_of = "defined in style crate"]
33 quirks_mode: QuirksMode,
34}
35
36impl StylesheetContentsCacheKey {
37 fn new(stylesheet_text: &str, base_url: &str, quirks_mode: QuirksMode) -> Self {
38 let contents_atom = if stylesheet_text.len() > MAX_LENGTH_OF_TEXT_INSERTED_INTO_TABLE {
43 let mut hasher = DefaultHasher::new();
44 stylesheet_text.hash(&mut hasher);
45 Atom::from(hasher.finish().to_string().as_str())
46 } else {
47 Atom::from(stylesheet_text)
48 };
49
50 Self {
51 stylesheet_text: Rc::new(contents_atom),
52 base_url: Atom::from(base_url),
53 quirks_mode,
54 }
55 }
56
57 pub(crate) fn is_uniquely_owned(&self) -> bool {
58 Rc::strong_count(&self.stylesheet_text) <= UNIQUE_OWNED
60 }
61}
62
63thread_local! {
64 static STYLESHEETCONTENTS_CACHE: RefCell<HashMap<StylesheetContentsCacheKey, ServoArc<StylesheetContents>>> =
65 RefCell::default();
66}
67
68pub(crate) struct StylesheetContentsCache;
69
70impl StylesheetContentsCache {
71 fn contents_can_be_cached(contents: &StylesheetContents, shared_lock: &SharedRwLock) -> bool {
72 let guard = shared_lock.read();
73 let rules = contents.rules(&guard);
74 !(rules.is_empty() || rules.iter().any(|rule| matches!(rule, CssRule::Import(_))))
77 }
78
79 pub(crate) fn get_or_insert_with(
80 stylesheet_text: &str,
81 shared_lock: &SharedRwLock,
82 url_data: UrlExtraData,
83 quirks_mode: QuirksMode,
84 stylesheetcontents_create_callback: impl FnOnce() -> ServoArc<StylesheetContents>,
85 ) -> (
86 Option<StylesheetContentsCacheKey>,
87 ServoArc<StylesheetContents>,
88 ) {
89 let cache_key =
90 StylesheetContentsCacheKey::new(stylesheet_text, url_data.as_str(), quirks_mode);
91 STYLESHEETCONTENTS_CACHE.with_borrow_mut(|stylesheetcontents_cache| {
92 let entry = stylesheetcontents_cache.entry(cache_key);
93 match entry {
94 Entry::Occupied(occupied_entry) => {
95 (
98 Some(occupied_entry.key().clone()),
99 occupied_entry.get().clone(),
100 )
101 },
102 Entry::Vacant(vacant_entry) => {
103 let contents = stylesheetcontents_create_callback();
104 if Self::contents_can_be_cached(&contents, shared_lock) {
105 let occupied_entry = vacant_entry.insert_entry(contents.clone());
106 (Some(occupied_entry.key().clone()), contents)
109 } else {
110 (None, contents)
111 }
112 },
113 }
114 })
115 }
116
117 pub(crate) fn remove(cache_key: StylesheetContentsCacheKey) {
118 STYLESHEETCONTENTS_CACHE.with_borrow_mut(|stylesheetcontents_cache| {
119 stylesheetcontents_cache.remove(&cache_key)
120 });
121 }
122}