shadow-dom.js (4808B)
1 function removeWhiteSpaceOnlyTextNodes(node) { 2 for (var i = 0; i < node.childNodes.length; i++) { 3 var child = node.childNodes[i]; 4 if (child.nodeType === Node.TEXT_NODE && 5 child.nodeValue.trim().length == 0) { 6 node.removeChild(child); 7 i--; 8 } else if ( 9 child.nodeType === Node.ELEMENT_NODE || 10 child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { 11 removeWhiteSpaceOnlyTextNodes(child); 12 } 13 } 14 if (node.shadowRoot) { 15 removeWhiteSpaceOnlyTextNodes(node.shadowRoot); 16 } 17 } 18 19 function convertTemplatesToShadowRootsWithin(node) { 20 var nodes = node.querySelectorAll('template'); 21 for (var i = 0; i < nodes.length; ++i) { 22 var template = nodes[i]; 23 var mode = template.getAttribute('data-mode'); 24 var delegatesFocus = template.hasAttribute('data-delegatesFocus'); 25 var parent = template.parentNode; 26 parent.removeChild(template); 27 var shadowRoot; 28 if (!mode) { 29 shadowRoot = parent.attachShadow({ mode: 'open' }); 30 } else { 31 shadowRoot = 32 parent.attachShadow({ 'mode': mode, 'delegatesFocus': delegatesFocus }); 33 } 34 var expose = template.getAttribute('data-expose-as'); 35 if (expose) 36 window[expose] = shadowRoot; 37 if (template.id) 38 shadowRoot.id = template.id; 39 var fragments = document.importNode(template.content, true); 40 shadowRoot.appendChild(fragments); 41 42 convertTemplatesToShadowRootsWithin(shadowRoot); 43 } 44 } 45 46 function isShadowHost(node) { 47 return node && node.nodeType == Node.ELEMENT_NODE && node.shadowRoot; 48 } 49 50 function isIFrameElement(element) { 51 return element && element.nodeName == 'IFRAME'; 52 } 53 54 // Returns node from shadow/iframe tree "path". 55 function getNodeInComposedTree(path) { 56 var ids = path.split('/'); 57 var node = document.getElementById(ids[0]); 58 for (var i = 1; node != null && i < ids.length; ++i) { 59 if (isIFrameElement(node)) 60 node = node.contentDocument.getElementById(ids[i]); 61 else if (isShadowHost(node)) 62 node = node.shadowRoot.getElementById(ids[i]); 63 else 64 return null; 65 } 66 return node; 67 } 68 69 function createTestTree(node) { 70 let ids = {}; 71 72 function attachShadowFromTemplate(template) { 73 let parent = template.parentNode; 74 parent.removeChild(template); 75 let shadowRoot; 76 if (template.getAttribute('data-slot-assignment') === 'manual') { 77 shadowRoot = 78 parent.attachShadow({ 79 mode: template.getAttribute('data-mode'), 80 slotAssignment: 'manual' 81 }); 82 } else { 83 shadowRoot = 84 parent.attachShadow({ mode: template.getAttribute('data-mode') }); 85 } 86 let id = template.id; 87 if (id) { 88 shadowRoot.id = id; 89 ids[id] = shadowRoot; 90 } 91 shadowRoot.appendChild(document.importNode(template.content, true)); 92 return shadowRoot; 93 } 94 95 function walk(root) { 96 if (root.id) { 97 ids[root.id] = root; 98 } 99 for (let e of Array.from(root.querySelectorAll('[id]'))) { 100 ids[e.id] = e; 101 } 102 for (let e of Array.from(root.querySelectorAll('template'))) { 103 walk(attachShadowFromTemplate(e)); 104 } 105 } 106 107 walk(node.cloneNode(true)); 108 return ids; 109 } 110 111 function dispatchEventWithLog(nodes, target, event) { 112 function labelFor(e) { 113 return e.id || e.tagName; 114 } 115 116 let log = []; 117 let attachedNodes = []; 118 for (let label in nodes) { 119 let startingNode = nodes[label]; 120 for (let node = startingNode; node; node = node.parentNode) { 121 if (attachedNodes.indexOf(node) >= 0) 122 continue; 123 let id = node.id; 124 if (!id) 125 continue; 126 attachedNodes.push(node); 127 node.addEventListener(event.type, (e) => { 128 // Record [currentTarget, target, relatedTarget, composedPath()] 129 log.push([ 130 id, labelFor(e.target), 131 e.relatedTarget ? labelFor(e.relatedTarget) : null, 132 e.composedPath().map((n) => { 133 return labelFor(n); 134 }) 135 ]); 136 }); 137 } 138 } 139 target.dispatchEvent(event); 140 return log; 141 } 142 143 // This function assumes that testharness.js is available. 144 function assert_event_path_equals(actual, expected) { 145 assert_equals(actual.length, expected.length); 146 for (let i = 0; i < actual.length; ++i) { 147 assert_equals( 148 actual[i][0], expected[i][0], 149 'currentTarget at ' + i + ' should be same'); 150 assert_equals( 151 actual[i][1], expected[i][1], 'target at ' + i + ' should be same'); 152 assert_equals( 153 actual[i][2], expected[i][2], 154 'relatedTarget at ' + i + ' should be same'); 155 assert_array_equals( 156 actual[i][3], expected[i][3], 157 'composedPath at ' + i + ' should be same'); 158 } 159 } 160 161 function assert_background_color(path, color) { 162 assert_equals( 163 window.getComputedStyle(getNodeInComposedTree(path)).backgroundColor, 164 color, 'backgroundColor for ' + path + ' should be ' + color); 165 }