script/
document_loader.rs1use std::collections::HashMap;
10
11use net_traits::request::RequestBuilder;
12use net_traits::{BoxedFetchCallback, ResourceThreads, fetch_async};
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;
19use crate::fetch::FetchCanceller;
20
21#[derive(Clone, Debug, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)]
22pub(crate) enum LoadType {
23 Image(#[no_trace] ServoUrl),
24 Script(#[no_trace] ServoUrl),
25 Subframe(#[no_trace] ServoUrl),
26 Stylesheet(#[no_trace] ServoUrl),
27 PageSource(#[no_trace] ServoUrl),
28 Media,
29}
30
31#[derive(JSTraceable, MallocSizeOf)]
35#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
36pub(crate) struct LoadBlocker {
37 doc: Dom<Document>,
39 load: Option<LoadType>,
41}
42
43impl LoadBlocker {
44 pub(crate) fn new(doc: &Document, load: LoadType) -> LoadBlocker {
46 doc.loader_mut().add_blocking_load(load.clone());
47 LoadBlocker {
48 doc: Dom::from_ref(doc),
49 load: Some(load),
50 }
51 }
52
53 pub(crate) fn terminate(
55 blocker: &DomRefCell<Option<LoadBlocker>>,
56 cx: &mut js::context::JSContext,
57 ) {
58 let Some(load) = blocker
59 .safe_borrow_mut(cx.no_gc())
60 .as_mut()
61 .and_then(|blocker| blocker.load.take())
62 else {
63 return;
64 };
65
66 if let Some(blocker) = blocker.borrow().as_ref() {
67 blocker.doc.finish_load(load, cx);
68 }
69
70 *blocker.safe_borrow_mut(cx.no_gc()) = None;
71 }
72}
73
74impl Drop for LoadBlocker {
75 fn drop(&mut self) {
76 if runtime_is_alive() &&
86 !during_gc_collection() &&
87 let Some(load) = self.load.take()
88 {
89 self.doc.finish_load_for_dropped_blocker(load);
90 }
91 }
92}
93
94#[derive(JSTraceable, MallocSizeOf)]
95pub(crate) struct DocumentLoader {
96 #[no_trace]
97 resource_threads: ResourceThreads,
98 blocking_loads: HashMap<LoadType, u32>,
101 events_inhibited: bool,
102 cancellers: Vec<FetchCanceller>,
103}
104
105impl DocumentLoader {
106 pub(crate) fn new(existing: &DocumentLoader) -> DocumentLoader {
107 DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
108 }
109
110 pub(crate) fn new_with_threads(
111 resource_threads: ResourceThreads,
112 initial_load: Option<ServoUrl>,
113 ) -> DocumentLoader {
114 debug!("Initial blocking load {:?}.", initial_load);
115
116 let initial_loads = initial_load
117 .into_iter()
118 .map(|url| (LoadType::PageSource(url), 1))
119 .collect();
120
121 DocumentLoader {
122 resource_threads,
123 blocking_loads: initial_loads,
124 events_inhibited: false,
125 cancellers: Vec::new(),
126 }
127 }
128
129 pub(crate) fn cancel_all_loads(&mut self) -> Vec<FetchCanceller> {
131 self.cancellers.drain(..).collect()
132 }
133
134 fn add_blocking_load(&mut self, load: LoadType) {
136 debug!(
137 "Adding blocking load {:?} ({}).",
138 load,
139 self.blocking_loads.len()
140 );
141 self.blocking_loads
142 .entry(load)
143 .and_modify(|load_number| *load_number += 1)
144 .or_insert(1);
145 }
146
147 pub(crate) fn fetch_async_with_callback(
149 &mut self,
150 load: LoadType,
151 request: RequestBuilder,
152 callback: BoxedFetchCallback,
153 ) {
154 self.add_blocking_load(load);
155 self.fetch_async_background(request, callback);
156 }
157
158 pub(crate) fn fetch_async_background(
160 &mut self,
161 request: RequestBuilder,
162 callback: BoxedFetchCallback,
163 ) {
164 self.cancellers.push(FetchCanceller::new(
165 request.id,
166 request.keep_alive,
167 self.resource_threads.core_thread.clone(),
168 ));
169 fetch_async(&self.resource_threads.core_thread, request, None, callback);
170 }
171
172 pub(crate) fn finish_load(&mut self, load: &LoadType) {
174 debug!(
175 "Removing blocking load {:?} ({}).",
176 load,
177 self.blocking_loads.len()
178 );
179
180 let Some(entry) = self.blocking_loads.get_mut(load) else {
181 warn!("unknown completed load {load:?}");
182 return;
183 };
184
185 *entry = entry.saturating_sub(1);
186 if *entry == 0 {
187 self.blocking_loads.remove(load);
188 }
189 }
190
191 pub(crate) fn is_blocked(&self) -> bool {
192 !self.blocking_loads.is_empty()
194 }
195
196 pub(crate) fn is_only_blocked_by_iframes(&self) -> bool {
197 self.blocking_loads
198 .keys()
199 .all(|load| matches!(*load, LoadType::Subframe(_)))
200 }
201
202 pub(crate) fn inhibit_events(&mut self) {
203 self.events_inhibited = true;
204 }
205
206 pub(crate) fn events_inhibited(&self) -> bool {
207 self.events_inhibited
208 }
209
210 pub(crate) fn resource_threads(&self) -> &ResourceThreads {
211 &self.resource_threads
212 }
213}