script/event_loop/
document_loader.rs1use std::collections::HashMap;
10
11use js::context::JSContext;
12use net_traits::ResourceThreads;
13use script_bindings::cell::DomRefCell;
14use script_bindings::script_runtime::{during_gc_collection, runtime_is_alive};
15use servo_url::ServoUrl;
16
17use crate::dom::bindings::root::Dom;
18use crate::dom::document::Document;
19
20#[derive(Clone, Debug, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)]
21pub(crate) enum LoadType {
22 Image(#[no_trace] ServoUrl),
23 Script(#[no_trace] ServoUrl),
24 Subframe(#[no_trace] ServoUrl),
25 Stylesheet(#[no_trace] ServoUrl),
26 PageSource(#[no_trace] ServoUrl),
27 Media,
28}
29
30#[derive(JSTraceable, MallocSizeOf)]
33#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
34pub(crate) struct LoadBlocker {
35 doc: Dom<Document>,
37 load: Option<LoadType>,
39}
40
41impl LoadBlocker {
42 pub(crate) fn new(doc: &Document, load: LoadType) -> LoadBlocker {
44 doc.loader_mut().add_blocking_load(load.clone());
45 LoadBlocker {
46 doc: Dom::from_ref(doc),
47 load: Some(load),
48 }
49 }
50
51 pub(crate) fn terminate(cx: &mut JSContext, blocker: &DomRefCell<Option<LoadBlocker>>) {
53 let Some(load) = blocker
54 .safe_borrow_mut(cx.no_gc())
55 .as_mut()
56 .and_then(|blocker| blocker.load.take())
57 else {
58 return;
59 };
60
61 if let Some(blocker) = blocker.borrow().as_ref() {
62 blocker.doc.finish_load(load, cx);
63 }
64
65 *blocker.safe_borrow_mut(cx.no_gc()) = None;
66 }
67}
68
69impl Drop for LoadBlocker {
70 fn drop(&mut self) {
71 if runtime_is_alive() &&
81 !during_gc_collection() &&
82 let Some(load) = self.load.take()
83 {
84 self.doc.finish_load_for_dropped_blocker(load);
85 }
86 }
87}
88
89#[derive(JSTraceable, MallocSizeOf)]
90pub(crate) struct DocumentLoader {
91 #[no_trace]
92 resource_threads: ResourceThreads,
93 blocking_loads: HashMap<LoadType, u32>,
96 events_inhibited: bool,
97}
98
99impl DocumentLoader {
100 pub(crate) fn new(existing: &DocumentLoader) -> DocumentLoader {
101 DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
102 }
103
104 pub(crate) fn new_with_threads(
105 resource_threads: ResourceThreads,
106 initial_load: Option<ServoUrl>,
107 ) -> DocumentLoader {
108 debug!("Initial blocking load {:?}.", initial_load);
109
110 let initial_loads = initial_load
111 .into_iter()
112 .map(|url| (LoadType::PageSource(url), 1))
113 .collect();
114
115 DocumentLoader {
116 resource_threads,
117 blocking_loads: initial_loads,
118 events_inhibited: false,
119 }
120 }
121
122 pub(crate) fn add_blocking_load(&mut self, load: LoadType) {
124 debug!(
125 "Adding blocking load {:?} ({}).",
126 load,
127 self.blocking_loads.len()
128 );
129 self.blocking_loads
130 .entry(load)
131 .and_modify(|load_number| *load_number += 1)
132 .or_insert(1);
133 }
134
135 pub(crate) fn finish_load(&mut self, load: &LoadType) {
137 debug!(
138 "Removing blocking load {:?} ({}).",
139 load,
140 self.blocking_loads.len()
141 );
142
143 let Some(entry) = self.blocking_loads.get_mut(load) else {
144 warn!("unknown completed load {load:?}");
145 return;
146 };
147
148 *entry = entry.saturating_sub(1);
149 if *entry == 0 {
150 self.blocking_loads.remove(load);
151 }
152 }
153
154 pub(crate) fn is_blocked(&self) -> bool {
155 !self.blocking_loads.is_empty()
157 }
158
159 pub(crate) fn is_only_blocked_by_iframes(&self) -> bool {
160 self.blocking_loads
161 .keys()
162 .all(|load| matches!(*load, LoadType::Subframe(_)))
163 }
164
165 pub(crate) fn inhibit_events(&mut self) {
166 self.events_inhibited = true;
167 }
168
169 pub(crate) fn events_inhibited(&self) -> bool {
170 self.events_inhibited
171 }
172
173 pub(crate) fn resource_threads(&self) -> &ResourceThreads {
174 &self.resource_threads
175 }
176}