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