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