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