1use std::borrow::Cow;
4use std::cell::{Ref, RefCell};
5use std::rc::{Rc, Weak};
6
7use html5ever::tendril::{StrTendril, TendrilSink};
8use html5ever::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeBuilderOpts, TreeSink};
9use html5ever::{Attribute, ParseOpts, QualName};
10
11#[derive(Clone, Debug)]
16pub struct ScriptTag {
17 pub src: Option<StrTendril>,
18 pub script_type: StrTendril,
19 pub nomodule: bool,
20 pub mount: Option<StrTendril>,
21}
22
23impl ScriptTag {
24 fn from_attributes(attributes: &[Attribute]) -> Self {
25 let attribute = |name: &str| {
26 attributes
27 .iter()
28 .find(|attribute| attribute.name.local.as_ref() == name)
29 .map(|attribute| attribute.value.clone())
30 };
31 Self {
32 src: attribute("src"),
33 script_type: attribute("type").unwrap_or_default(),
34 nomodule: attribute("nomodule").is_some(),
35 mount: attribute("mount"),
36 }
37 }
38
39 pub fn is_javascript(&self) -> bool {
43 match self.script_type.trim().to_ascii_lowercase().as_str() {
44 "module" => true,
45 "" | "text/javascript" | "application/javascript" => !self.nomodule,
46 _ => false,
47 }
48 }
49}
50
51pub fn scan_script_tags(html: &str) -> Vec<ScriptTag> {
60 let sink = PreloadSink {
61 root: PreloadNode::new(None, None),
62 scripts: RefCell::new(Vec::new()),
63 };
64 html5ever::parse_document(
65 sink,
66 ParseOpts {
67 tree_builder: TreeBuilderOpts {
68 scripting_enabled: true,
69 drop_doctype: true,
70 ..Default::default()
71 },
72 ..Default::default()
73 },
74 )
75 .one(html)
76}
77
78struct PreloadNode {
79 name: RefCell<Option<QualName>>,
80 script: Option<ScriptTag>,
81 parent: RefCell<Option<Rc<PreloadNode>>>,
82 children: RefCell<Vec<Weak<PreloadNode>>>,
83 template_contents: RefCell<Option<Rc<PreloadNode>>>,
84}
85
86impl PreloadNode {
87 fn new(name: Option<QualName>, script: Option<ScriptTag>) -> Rc<Self> {
88 Rc::new(Self {
89 name: RefCell::new(name),
90 script,
91 parent: RefCell::new(None),
92 children: RefCell::new(Vec::new()),
93 template_contents: RefCell::new(None),
94 })
95 }
96
97 fn detach(node: &Rc<Self>) {
98 let parent = node.parent.borrow_mut().take();
99 if let Some(parent) = parent {
100 let pointer = Rc::as_ptr(node);
101 parent
102 .children
103 .borrow_mut()
104 .retain(|child| child.as_ptr() != pointer);
105 }
106 }
107
108 fn append(parent: &Rc<Self>, node: Rc<Self>) {
109 Self::detach(&node);
110 *node.parent.borrow_mut() = Some(Rc::clone(parent));
111 parent.children.borrow_mut().push(Rc::downgrade(&node));
112 }
113}
114
115struct PreloadSink {
116 root: Rc<PreloadNode>,
117 scripts: RefCell<Vec<Rc<PreloadNode>>>,
120}
121
122impl TreeSink for PreloadSink {
123 type Output = Vec<ScriptTag>;
124 type Handle = Rc<PreloadNode>;
125 type ElemName<'a>
126 = Ref<'a, QualName>
127 where
128 Self: 'a;
129
130 fn finish(self) -> Self::Output {
131 let mut scripts = Vec::new();
132 let mut stack = vec![Rc::clone(&self.root)];
133 while let Some(node) = stack.pop() {
134 if let Some(script) = &node.script {
135 scripts.push(script.clone());
136 }
137 stack.extend(
138 node.children
139 .borrow()
140 .iter()
141 .rev()
142 .filter_map(Weak::upgrade),
143 );
144 }
145 scripts
146 }
147
148 fn parse_error(&self, _: Cow<'static, str>) {}
149
150 fn get_document(&self) -> Self::Handle {
151 Rc::clone(&self.root)
152 }
153
154 fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> Self::ElemName<'a> {
155 Ref::map(target.name.borrow(), |name| {
156 name.as_ref().expect("preload handle is not an element")
157 })
158 }
159
160 fn create_element(
161 &self,
162 name: QualName,
163 attributes: Vec<Attribute>,
164 _: ElementFlags,
165 ) -> Self::Handle {
166 let script =
167 (name.local.as_ref() == "script").then(|| ScriptTag::from_attributes(&attributes));
168 let node = PreloadNode::new(Some(name), script);
169 if node.script.is_some() {
170 self.scripts.borrow_mut().push(Rc::clone(&node));
171 }
172 node
173 }
174
175 fn create_comment(&self, _: StrTendril) -> Self::Handle {
176 PreloadNode::new(None, None)
177 }
178
179 fn create_pi(&self, _: StrTendril, _: StrTendril) -> Self::Handle {
180 PreloadNode::new(None, None)
181 }
182
183 fn append(&self, parent: &Self::Handle, child: NodeOrText<Self::Handle>) {
184 if let NodeOrText::AppendNode(node) = child {
185 PreloadNode::append(parent, node);
186 }
187 }
188
189 fn append_before_sibling(&self, sibling: &Self::Handle, child: NodeOrText<Self::Handle>) {
190 let NodeOrText::AppendNode(node) = child else {
191 return;
192 };
193 if Rc::ptr_eq(sibling, &node) {
194 return;
195 }
196 let parent = sibling.parent.borrow().clone();
197 let Some(parent) = parent else {
198 return;
199 };
200 PreloadNode::detach(&node);
201 let pointer = Rc::as_ptr(sibling);
202 let mut children = parent.children.borrow_mut();
203 let index = children
204 .iter()
205 .position(|child| child.as_ptr() == pointer)
206 .expect("preload sibling missing from parent");
207 *node.parent.borrow_mut() = Some(Rc::clone(&parent));
208 children.insert(index, Rc::downgrade(&node));
209 }
210
211 fn append_based_on_parent_node(
212 &self,
213 element: &Self::Handle,
214 previous: &Self::Handle,
215 child: NodeOrText<Self::Handle>,
216 ) {
217 let has_parent = element.parent.borrow().is_some();
218 if has_parent {
219 self.append_before_sibling(element, child);
220 } else {
221 self.append(previous, child);
222 }
223 }
224
225 fn append_doctype_to_document(&self, _: StrTendril, _: StrTendril, _: StrTendril) {}
226
227 fn allow_declarative_shadow_roots(&self, _: &Self::Handle) -> bool {
228 false
232 }
233
234 fn get_template_contents(&self, target: &Self::Handle) -> Self::Handle {
235 let mut contents = target.template_contents.borrow_mut();
236 Rc::clone(contents.get_or_insert_with(|| PreloadNode::new(None, None)))
237 }
238
239 fn same_node(&self, left: &Self::Handle, right: &Self::Handle) -> bool {
240 Rc::ptr_eq(left, right)
241 }
242
243 fn set_quirks_mode(&self, _: QuirksMode) {}
244
245 fn add_attrs_if_missing(&self, _: &Self::Handle, _: Vec<Attribute>) {
246 }
249
250 fn remove_from_parent(&self, target: &Self::Handle) {
251 PreloadNode::detach(target);
252 }
253
254 fn reparent_children(&self, previous: &Self::Handle, parent: &Self::Handle) {
255 let children: Vec<_> = previous
256 .children
257 .borrow_mut()
258 .drain(..)
259 .filter_map(|child| child.upgrade())
260 .collect();
261 for child in children {
262 PreloadNode::append(parent, child);
263 }
264 }
265}