1use bliss_traits::dom_control::{
6 DomControlError, DomControlResult, DomController, NodeId, NodeInfo,
7};
8use markup5ever::local_name;
9
10use crate::{BaseDocument, NodeData};
11
12impl DomController for BaseDocument {
13 fn query_selector(&self, selector: &str) -> DomControlResult<Option<NodeId>> {
14 self.query_selector(selector)
15 .map_err(|e| DomControlError::InvalidSelector(format!("{:?}", e)))
16 }
17
18 fn query_selector_all(&self, selector: &str) -> DomControlResult<Vec<NodeId>> {
19 self.query_selector_all(selector)
20 .map_err(|e| DomControlError::InvalidSelector(format!("{:?}", e)))
21 .map(|smallvec| smallvec.into_vec())
22 }
23
24 fn get_element_by_id(&self, id: &str) -> Option<NodeId> {
25 self.get_element_by_id(id)
26 }
27
28 fn get_node_info(&self, node_id: NodeId) -> DomControlResult<NodeInfo> {
29 let node = self
30 .get_node(node_id)
31 .ok_or(DomControlError::NodeNotFound(node_id))?;
32
33 let tag_name = node.element_data().map(|el| el.name.local.to_string());
34
35 let text_content = match &node.data {
36 NodeData::Text(text_data) => Some(text_data.content.clone()),
37 _ => {
38 let mut text = String::new();
40 for child_id in &node.children {
41 if let Some(child) = self.get_node(*child_id) {
42 if let NodeData::Text(text_data) = &child.data {
43 text.push_str(&text_data.content);
44 }
45 }
46 }
47 if text.is_empty() { None } else { Some(text) }
48 }
49 };
50
51 let attributes = node
52 .element_data()
53 .map(|el| {
54 el.attrs()
55 .iter()
56 .map(|attr| (attr.name.local.to_string(), attr.value.clone()))
57 .collect()
58 })
59 .unwrap_or_default();
60
61 Ok(NodeInfo {
62 id: node_id,
63 tag_name,
64 text_content,
65 attributes,
66 })
67 }
68
69 fn set_attribute(&mut self, node_id: NodeId, name: &str, value: &str) -> DomControlResult<()> {
70 let node = self
71 .get_node_mut(node_id)
72 .ok_or(DomControlError::NodeNotFound(node_id))?;
73
74 let element = node.element_data_mut().ok_or_else(|| {
75 DomControlError::InvalidMutation(format!("Node {} is not an element", node_id))
76 })?;
77
78 if name == "id" {
80 element.id = Some(style::Atom::from(value));
84 }
85
86 let local_name = local_name!(name);
88 if let Some(attr) = element
89 .attrs
90 .iter_mut()
91 .find(|a| a.name.local == local_name)
92 {
93 attr.value = value.to_string();
94 } else {
95 use crate::Attribute;
96 use crate::qual_name;
97 element.attrs.push(Attribute {
98 name: qual_name!(name),
99 value: value.to_string(),
100 });
101 }
102
103 node.set_restyle_hint(
105 style::invalidation::element::restyle_hints::RestyleHint::restyle_subtree(),
106 );
107
108 if name == "id" {
110 self.nodes_to_id.insert(value.to_string(), node_id);
111 }
112
113 Ok(())
114 }
115
116 fn remove_attribute(&mut self, node_id: NodeId, name: &str) -> DomControlResult<()> {
117 let node = self
118 .get_node_mut(node_id)
119 .ok_or(DomControlError::NodeNotFound(node_id))?;
120
121 let element = node.element_data_mut().ok_or_else(|| {
122 DomControlError::InvalidMutation(format!("Node {} is not an element", node_id))
123 })?;
124
125 let is_id = name == "id";
127 let old_id = if is_id { element.id.clone() } else { None };
128
129 let local_name = local_name!(name);
130 element.attrs.retain(|attr| attr.name.local != local_name);
131
132 if is_id {
133 element.id = None;
134 }
135
136 node.set_restyle_hint(
138 style::invalidation::element::restyle_hints::RestyleHint::restyle_subtree(),
139 );
140
141 if is_id {
143 if let Some(id) = old_id {
144 self.nodes_to_id.remove(&id.to_string());
145 }
146 }
147
148 Ok(())
149 }
150
151 fn set_text_content(&mut self, node_id: NodeId, text: &str) -> DomControlResult<()> {
152 let node = self
153 .get_node_mut(node_id)
154 .ok_or(DomControlError::NodeNotFound(node_id))?;
155
156 match &mut node.data {
157 NodeData::Text(text_data) => {
158 text_data.content = text.to_string();
159 }
160 NodeData::Element(_) | NodeData::AnonymousBlock(_) => {
161 return Err(DomControlError::InvalidMutation(
165 "set_text_content on elements requires child node manipulation".to_string(),
166 ));
167 }
168 _ => {
169 return Err(DomControlError::InvalidMutation(
170 "Cannot set text content on this node type".to_string(),
171 ));
172 }
173 }
174
175 node.set_restyle_hint(
177 style::invalidation::element::restyle_hints::RestyleHint::restyle_subtree(),
178 );
179
180 Ok(())
181 }
182
183 fn set_style_property(
184 &mut self,
185 node_id: NodeId,
186 property: &str,
187 value: &str,
188 ) -> DomControlResult<()> {
189 let guard = self.guard.clone();
191 let url_extra_data = self.url.url_extra_data().clone();
192
193 let node = self
194 .get_node_mut(node_id)
195 .ok_or(DomControlError::NodeNotFound(node_id))?;
196
197 let element = node.element_data_mut().ok_or_else(|| {
198 DomControlError::InvalidMutation(format!("Node {} is not an element", node_id))
199 })?;
200
201 element.set_style_property(property, value, &guard, url_extra_data);
202
203 node.set_restyle_hint(
205 style::invalidation::element::restyle_hints::RestyleHint::restyle_subtree(),
206 );
207
208 Ok(())
209 }
210
211 fn remove_style_property(&mut self, node_id: NodeId, property: &str) -> DomControlResult<()> {
212 let guard = self.guard.clone();
214 let url_extra_data = self.url.url_extra_data().clone();
215
216 let node = self
217 .get_node_mut(node_id)
218 .ok_or(DomControlError::NodeNotFound(node_id))?;
219
220 let element = node.element_data_mut().ok_or_else(|| {
221 DomControlError::InvalidMutation(format!("Node {} is not an element", node_id))
222 })?;
223
224 element.remove_style_property(property, &guard, url_extra_data);
225
226 node.set_restyle_hint(
228 style::invalidation::element::restyle_hints::RestyleHint::restyle_subtree(),
229 );
230
231 Ok(())
232 }
233
234 fn set_inner_html(&mut self, node_id: NodeId, html: &str) -> DomControlResult<()> {
235 let _ = self
237 .get_node(node_id)
238 .and_then(|node| node.element_data())
239 .ok_or(DomControlError::NodeNotFound(node_id))?;
240
241 let parser = self.html_parser_provider.clone();
242 let mut mutr = self.mutate();
243 parser.parse_inner_html(&mut mutr, node_id, html);
244 Ok(())
245 }
246
247 fn add_event_listener(
248 &mut self,
249 node_id: NodeId,
250 event: &str,
251 handler_id: u64,
252 ) -> DomControlResult<()> {
253 if self.get_node(node_id).is_none() {
255 return Err(DomControlError::NodeNotFound(node_id));
256 }
257
258 self.event_listeners
259 .entry((node_id, event.to_string()))
260 .or_default()
261 .push(handler_id);
262
263 Ok(())
264 }
265
266 fn remove_event_listener(
267 &mut self,
268 node_id: NodeId,
269 event: &str,
270 handler_id: u64,
271 ) -> DomControlResult<()> {
272 if self.get_node(node_id).is_none() {
274 return Err(DomControlError::NodeNotFound(node_id));
275 }
276
277 if let Some(handlers) = self.event_listeners.get_mut(&(node_id, event.to_string())) {
278 handlers.retain(|&id| id != handler_id);
279 if handlers.is_empty() {
280 self.event_listeners.remove(&(node_id, event.to_string()));
281 }
282 }
283
284 Ok(())
285 }
286}
287
288pub struct PolicyDomController<'a> {
290 doc: &'a mut BaseDocument,
291 policy: std::sync::Arc<dyn bliss_traits::dom_control::DomCapabilityPolicy>,
292 doc_id: usize,
293}
294
295impl<'a> PolicyDomController<'a> {
296 pub fn new(
297 doc: &'a mut BaseDocument,
298 policy: std::sync::Arc<dyn bliss_traits::dom_control::DomCapabilityPolicy>,
299 doc_id: usize,
300 ) -> Self {
301 Self {
302 doc,
303 policy,
304 doc_id,
305 }
306 }
307}
308
309impl<'a> DomController for PolicyDomController<'a> {
310 fn query_selector(&self, selector: &str) -> DomControlResult<Option<NodeId>> {
311 if !self.policy.allow_query(self.doc_id, selector) {
312 return Err(DomControlError::PermissionDenied(format!(
313 "Query not allowed: {}",
314 selector
315 )));
316 }
317 self.doc
318 .query_selector(selector)
319 .map_err(|e| DomControlError::InvalidSelector(format!("{:?}", e)))
320 }
321
322 fn query_selector_all(&self, selector: &str) -> DomControlResult<Vec<NodeId>> {
323 if !self.policy.allow_query(self.doc_id, selector) {
324 return Err(DomControlError::PermissionDenied(format!(
325 "Query not allowed: {}",
326 selector
327 )));
328 }
329 self.doc
330 .query_selector_all(selector)
331 .map_err(|e| DomControlError::InvalidSelector(format!("{:?}", e)))
332 .map(|smallvec| smallvec.into_vec())
333 }
334
335 fn get_element_by_id(&self, id: &str) -> Option<NodeId> {
336 self.doc.get_element_by_id(id)
338 }
339
340 fn get_node_info(&self, node_id: NodeId) -> DomControlResult<NodeInfo> {
341 self.doc.get_node_info(node_id)
343 }
344
345 fn set_attribute(&mut self, node_id: NodeId, name: &str, value: &str) -> DomControlResult<()> {
346 if !self
347 .policy
348 .allow_mutation(self.doc_id, node_id, "set_attribute")
349 {
350 return Err(DomControlError::PermissionDenied(
351 "set_attribute not allowed".to_string(),
352 ));
353 }
354 self.doc.set_attribute(node_id, name, value)
355 }
356
357 fn remove_attribute(&mut self, node_id: NodeId, name: &str) -> DomControlResult<()> {
358 if !self
359 .policy
360 .allow_mutation(self.doc_id, node_id, "remove_attribute")
361 {
362 return Err(DomControlError::PermissionDenied(
363 "remove_attribute not allowed".to_string(),
364 ));
365 }
366 self.doc.remove_attribute(node_id, name)
367 }
368
369 fn set_text_content(&mut self, node_id: NodeId, text: &str) -> DomControlResult<()> {
370 if !self
371 .policy
372 .allow_mutation(self.doc_id, node_id, "set_text_content")
373 {
374 return Err(DomControlError::PermissionDenied(
375 "set_text_content not allowed".to_string(),
376 ));
377 }
378 self.doc.set_text_content(node_id, text)
379 }
380
381 fn set_style_property(
382 &mut self,
383 node_id: NodeId,
384 property: &str,
385 value: &str,
386 ) -> DomControlResult<()> {
387 if !self
388 .policy
389 .allow_mutation(self.doc_id, node_id, "set_style_property")
390 {
391 return Err(DomControlError::PermissionDenied(
392 "set_style_property not allowed".to_string(),
393 ));
394 }
395 self.doc.set_style_property(node_id, property, value);
396 Ok(())
397 }
398
399 fn remove_style_property(&mut self, node_id: NodeId, property: &str) -> DomControlResult<()> {
400 if !self
401 .policy
402 .allow_mutation(self.doc_id, node_id, "remove_style_property")
403 {
404 return Err(DomControlError::PermissionDenied(
405 "remove_style_property not allowed".to_string(),
406 ));
407 }
408 self.doc.remove_style_property(node_id, property);
409 Ok(())
410 }
411
412 fn set_inner_html(&mut self, node_id: NodeId, html: &str) -> DomControlResult<()> {
413 if !self
414 .policy
415 .allow_mutation(self.doc_id, node_id, "set_inner_html")
416 {
417 return Err(DomControlError::PermissionDenied(
418 "set_inner_html not allowed".to_string(),
419 ));
420 }
421 self.doc.set_inner_html(node_id, html)
422 }
423
424 fn add_event_listener(
425 &mut self,
426 node_id: NodeId,
427 event: &str,
428 handler_id: u64,
429 ) -> DomControlResult<()> {
430 if !self
431 .policy
432 .allow_event_listener(self.doc_id, node_id, event)
433 {
434 return Err(DomControlError::PermissionDenied(format!(
435 "add_event_listener not allowed for {}",
436 event
437 )));
438 }
439 self.doc.add_event_listener(node_id, event, handler_id)
440 }
441
442 fn remove_event_listener(
443 &mut self,
444 node_id: NodeId,
445 event: &str,
446 handler_id: u64,
447 ) -> DomControlResult<()> {
448 if !self
449 .policy
450 .allow_event_listener(self.doc_id, node_id, event)
451 {
452 return Err(DomControlError::PermissionDenied(format!(
453 "remove_event_listener not allowed for {}",
454 event
455 )));
456 }
457 self.doc.remove_event_listener(node_id, event, handler_id)
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::{DocumentConfig, ElementData, NodeData, qual_name};
465
466 #[test]
467 fn test_dom_controller_query_selector() {
468 let mut doc = BaseDocument::new(DocumentConfig::default());
469
470 let div = doc.create_node(NodeData::Element(ElementData::new(
472 qual_name!("div"),
473 vec![],
474 )));
475 let span = doc.create_node(NodeData::Element(ElementData::new(
476 qual_name!("span"),
477 vec![],
478 )));
479 let text = doc.create_text_node("Hello");
480
481 doc.root_node_mut().children.push(div);
483 doc.get_node_mut(div).unwrap().parent = Some(0); doc.get_node_mut(div).unwrap().children.push(span);
487 doc.get_node_mut(span).unwrap().parent = Some(div);
488
489 doc.get_node_mut(span).unwrap().children.push(text);
491 doc.get_node_mut(text).unwrap().parent = Some(span);
492
493 let result = doc.query_selector("span").unwrap();
495 assert_eq!(result, Some(span));
496
497 let result = doc.query_selector(".nonexistent").unwrap();
498 assert_eq!(result, None);
499 }
500
501 #[test]
502 fn test_dom_controller_get_node_info() {
503 let mut doc = BaseDocument::new(DocumentConfig::default());
504
505 let div = doc.create_node(NodeData::Element(ElementData::new(
506 qual_name!("div"),
507 vec![],
508 )));
509
510 let info = doc.get_node_info(div).unwrap();
511 assert_eq!(info.id, div);
512 assert_eq!(info.tag_name, Some("div".to_string()));
513 }
514}