1 use webapi::document_fragment::DocumentFragment; 2 use webapi::element::Element; 3 use webapi::event_target::{EventTarget, IEventTarget}; 4 use webapi::node::{INode, Node}; 5 use webapi::parent_node::IParentNode; 6 use webcore::try_from::TryInto; 7 use webcore::value::Reference; 8 9 /// The mode associated to a shadow root. 10 /// Mainly used in [IElement::attach_shadow](trait.IElement.html#method.attach_shadow) and 11 /// [IShadowRoot::mode](trait.IShadowRoot.html#method.mode). 12 // https://dom.spec.whatwg.org/#shadowroot-mode 13 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 14 pub enum ShadowRootMode { 15 /// { mode: "open" } 16 Open, 17 /// { mode: "closed" } 18 Closed, 19 } 20 21 impl ShadowRootMode { as_str(&self) -> &'static str22 pub(crate) fn as_str(&self) -> &'static str { 23 match *self { 24 ShadowRootMode::Open => "open", 25 ShadowRootMode::Closed => "closed", 26 } 27 } 28 } 29 30 /// The `ShadowRoot` interface of the Shadow DOM API is the root node of a DOM 31 /// subtree that is rendered separately from a document's main DOM tree. 32 /// 33 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot) 34 // https://dom.spec.whatwg.org/#interface-shadowroot 35 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 36 #[reference(instance_of = "ShadowRoot")] 37 #[reference(subclass_of(EventTarget, Node, DocumentFragment))] 38 pub struct ShadowRoot(Reference); 39 40 impl IEventTarget for ShadowRoot {} 41 impl INode for ShadowRoot {} 42 impl IParentNode for ShadowRoot {} 43 44 impl ShadowRoot { 45 /// The mode property of the `ShadowRoot` specifies its mode. 46 /// 47 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode) 48 // https://dom.spec.whatwg.org/#ref-for-dom-shadowroot-mode mode(&self) -> ShadowRootMode49 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 /// The host read-only property of the `ShadowRoot` returns a reference to the DOM element 60 /// the ShadowRoot is attached to. 61 /// 62 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/host) 63 // https://dom.spec.whatwg.org/#ref-for-dom-shadowroot-host host(&self) -> Element64 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"))] 70 mod 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] test_shadow_root_host()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] test_shadow_dom()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 } 147