stdweb/webapi/
shadow_root.rs1use webapi::document_fragment::DocumentFragment;
2use webapi::element::Element;
3use webapi::event_target::{EventTarget, IEventTarget};
4use webapi::node::{INode, Node};
5use webapi::parent_node::IParentNode;
6use webcore::try_from::TryInto;
7use webcore::value::Reference;
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
14pub enum ShadowRootMode {
15 Open,
17 Closed,
19}
20
21impl ShadowRootMode {
22 pub(crate) fn as_str(&self) -> &'static str {
23 match *self {
24 ShadowRootMode::Open => "open",
25 ShadowRootMode::Closed => "closed",
26 }
27 }
28}
29
30#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
36#[reference(instance_of = "ShadowRoot")]
37#[reference(subclass_of(EventTarget, Node, DocumentFragment))]
38pub struct ShadowRoot(Reference);
39
40impl IEventTarget for ShadowRoot {}
41impl INode for ShadowRoot {}
42impl IParentNode for ShadowRoot {}
43
44impl ShadowRoot {
45 pub fn mode(&self) -> ShadowRootMode {
50 let mode_string: String = js!( return @{self.as_ref()}.mode; ).try_into().unwrap();
51
52 match mode_string.as_str() {
53 "open" => ShadowRootMode::Open,
54 "closed" => ShadowRootMode::Closed,
55 _ => unreachable!("mode can only be `open` or `closed`"),
56 }
57 }
58
59 pub fn host(&self) -> Element {
65 js!( return @{self.as_ref()}.host; ).try_into().unwrap()
66 }
67}
68
69#[cfg(all(test, feature = "web_test"))]
70mod tests {
71 use super::*;
72 use webapi::document::document;
73 use webapi::element::{Element, IElement};
74 use webapi::html_elements::{SlotContentKind, SlotElement, TemplateElement};
75 use webapi::node::{CloneKind, INode, Node};
76 use webapi::parent_node::IParentNode;
77
78 #[test]
79 fn test_shadow_root_host() {
80 let element = document().create_element("div").unwrap();
81 let shadow_root = element.attach_shadow(ShadowRootMode::Open).unwrap();
82 assert_eq!(shadow_root.host(), element);
83 }
84
85 #[test]
86 fn test_shadow_dom() {
87 let div: Element = Node::from_html(r#"<div>
88 <span id="span1" slot="slot1"></span>
89</div>"#)
90 .unwrap()
91 .try_into()
92 .unwrap();
93 let tpl: TemplateElement = Node::from_html(r#"<template>
94 <slot name="slot1" id="slot1"><span id="span2"></span></slot><br>
95 <slot name="slot2" id="slot2"><span id="span3"></span></slot><br>
96</template>"#)
97 .unwrap()
98 .try_into()
99 .unwrap();
100
101 let span1 = div.query_selector("#span1").unwrap().unwrap();
102
103 let shadow_root = div.attach_shadow(ShadowRootMode::Open).unwrap();
104 let n = tpl.content().clone_node(CloneKind::Deep).unwrap();
105
106 shadow_root.append_child(&n);
107
108 let slot1: SlotElement = shadow_root
109 .query_selector("#slot1")
110 .unwrap()
111 .unwrap()
112 .try_into()
113 .unwrap();
114 let slot2: SlotElement = shadow_root
115 .query_selector("#slot2")
116 .unwrap()
117 .unwrap()
118 .try_into()
119 .unwrap();
120
121 assert_eq!(
122 slot1
123 .assigned_nodes(SlotContentKind::AssignedOnly)
124 .iter()
125 .map(|m| m.clone().try_into().unwrap())
126 .collect::<Vec<Element>>(),
127 &[span1.clone()]
128 );
129 assert_eq!(slot2.assigned_nodes(SlotContentKind::AssignedOnly).len(), 0);
130
131 assert_eq!(
132 slot1
133 .assigned_nodes(SlotContentKind::WithFallback)
134 .iter()
135 .map(|m| m.clone().try_into().unwrap())
136 .collect::<Vec<Element>>(),
137 &[span1.clone()]
138 );
139
140 let slot2_nodes = slot2.assigned_nodes(SlotContentKind::WithFallback);
141 assert_eq!(slot2_nodes.len(), 1);
142 let fallback_span = slot2_nodes[0].clone();
143
144 assert_eq!(js!( return @{fallback_span}.id; ), "span3");
145 }
146}