| OLD | NEW |
| (Empty) | |
| 1 <!DOCTYPE html> |
| 2 <html> |
| 3 <head> |
| 4 <title>Shadow DOM: Firing an event inside a node assigned to a slot</title> |
| 5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> |
| 6 <meta name="assert" content="The event path calculation algorithm must be us
ed to determine event path"> |
| 7 <link rel="help" href="https://w3c.github.io/webcomponents/spec/shadow/#even
t-paths"> |
| 8 <script src="/resources/testharness.js"></script> |
| 9 <script src="/resources/testharnessreport.js"></script> |
| 10 </head> |
| 11 <body> |
| 12 <div id="log"></div> |
| 13 <script> |
| 14 |
| 15 function dispatchEventWithLog(shadow, event) { |
| 16 var log = []; |
| 17 |
| 18 var attachedNodes = []; |
| 19 for (var nodeKey in shadow) { |
| 20 var startingNode = shadow[nodeKey]; |
| 21 for (var node = startingNode; node; node = node.parentNode) { |
| 22 if (attachedNodes.indexOf(node) >= 0) |
| 23 continue; |
| 24 attachedNodes.push(node); |
| 25 node.addEventListener(event.type, (function (event) { |
| 26 log.push([this, event.target]); |
| 27 }).bind(node)); |
| 28 } |
| 29 } |
| 30 |
| 31 shadow.target.dispatchEvent(event); |
| 32 |
| 33 return log; |
| 34 } |
| 35 |
| 36 function element(name, children, className) { |
| 37 var element = document.createElement(name); |
| 38 if (className) |
| 39 element.className = className; |
| 40 if (children) { |
| 41 for (var child of children) |
| 42 element.appendChild(child); |
| 43 } |
| 44 return element; |
| 45 } |
| 46 |
| 47 function attachShadow(host, mode, children) { |
| 48 var shadowRoot = host.attachShadow({mode: mode}); |
| 49 if (children) { |
| 50 for (var child of children) |
| 51 shadowRoot.appendChild(child); |
| 52 } |
| 53 return shadowRoot; |
| 54 } |
| 55 |
| 56 function createShadowHostWithAssignedGrandChild(mode) { |
| 57 var host = element('div', [ |
| 58 element('b', [ |
| 59 element('i') |
| 60 ]) |
| 61 ]); |
| 62 |
| 63 var root = attachShadow(host, mode, [ |
| 64 element('span', [ |
| 65 element('slot') |
| 66 ]) |
| 67 ]); |
| 68 |
| 69 return {target: host.querySelector('i'), targetParent: host.querySel
ector('b'), host: host, |
| 70 slot: root.querySelector('slot'), slotParent: root.querySele
ctor('span'), root: root}; |
| 71 } |
| 72 |
| 73 function testEventInDetachedShadowHostDescendant(mode) { |
| 74 test(function () { |
| 75 var shadow = createShadowHostWithAssignedGrandChild(mode); |
| 76 |
| 77 log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: tr
ue, composed: true})); |
| 78 |
| 79 assert_equals(log.length, 6, 'EventPath must contain [target, ta
rget parent, slot, slot parent, shadow root, shadow host]'); |
| 80 assert_array_equals(log[0], [shadow.target, shadow.target], 'Eve
ntPath[0] must be the target'); |
| 81 assert_array_equals(log[1], [shadow.targetParent, shadow.target]
, 'EventPath[1] must be the parent of the target'); |
| 82 assert_array_equals(log[2], [shadow.slot, shadow.target], 'Event
Path[2] must be the slot'); |
| 83 assert_array_equals(log[3], [shadow.slotParent, shadow.target],
'EventPath[3] must be the parent of the slot'); |
| 84 assert_array_equals(log[4], [shadow.root, shadow.target], 'Event
Path[4] must be the shadow root'); |
| 85 assert_array_equals(log[5], [shadow.host, shadow.target], 'Event
Path[5] must be the shadow host'); |
| 86 |
| 87 }, 'Firing an event inside a grand child of a detached ' + mode + '
mode shadow host'); |
| 88 } |
| 89 |
| 90 testEventInDetachedShadowHostDescendant('open'); |
| 91 testEventInDetachedShadowHostDescendant('closed'); |
| 92 |
| 93 function testEventInShadowHostDescendantInsideDocument(mode) { |
| 94 test(function () { |
| 95 var shadow = createShadowHostWithAssignedGrandChild(mode); |
| 96 document.body.appendChild(shadow.host); |
| 97 |
| 98 log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: tr
ue, composed: true})); |
| 99 |
| 100 assert_equals(log.length, 9, 'EventPath must contain [target, ta
rget parent, slot, slot parent, shadow root, shadow host, body, html, document]'
); |
| 101 assert_array_equals(log[0], [shadow.target, shadow.target], 'Eve
ntPath[0] must be the target'); |
| 102 assert_array_equals(log[1], [shadow.targetParent, shadow.target]
, 'EventPath[1] must be the parent of the target'); |
| 103 assert_array_equals(log[2], [shadow.slot, shadow.target], 'Event
Path[2] must be the slot'); |
| 104 assert_array_equals(log[3], [shadow.slotParent, shadow.target],
'EventPath[3] must be the parent of the slot'); |
| 105 assert_array_equals(log[4], [shadow.root, shadow.target], 'Event
Path[4] must be the shadow root'); |
| 106 assert_array_equals(log[5], [shadow.host, shadow.target], 'Event
Path[5] must be the shadow host'); |
| 107 assert_array_equals(log[6], [document.body, shadow.target], 'Eve
ntPath[6] must be the body element'); |
| 108 assert_array_equals(log[7], [document.documentElement, shadow.ta
rget], 'EventPath[7] must be the html element'); |
| 109 assert_array_equals(log[8], [document, shadow.target], 'EventPat
h[8] must be the html element'); |
| 110 |
| 111 }, 'Firing an event inside a grand child of an in-document ' + mode
+ ' mode shadow host'); |
| 112 } |
| 113 |
| 114 testEventInShadowHostDescendantInsideDocument('open'); |
| 115 testEventInShadowHostDescendantInsideDocument('closed'); |
| 116 |
| 117 function createNestedShadowTreesWithSlots(innerMode, outerUpperMode, out
erLowerMode) { |
| 118 var upperHost = element('upper-host', [ |
| 119 element('p', [ |
| 120 element('lower-host', [ |
| 121 element('a') |
| 122 ]) |
| 123 ]) |
| 124 ]); |
| 125 |
| 126 var upperShadow = attachShadow(upperHost, outerUpperMode, [ |
| 127 element('b', [ |
| 128 element('slot', [], 'upper-slot') |
| 129 ]) |
| 130 ]); |
| 131 |
| 132 var lowerHost = upperHost.querySelector('lower-host'); |
| 133 var lowerShadow = attachShadow(lowerHost, outerLowerMode, [ |
| 134 element('em', [ |
| 135 element('inner-host', [ |
| 136 element('span', [ |
| 137 element('slot', [], 'lower-slot') |
| 138 ]) |
| 139 ]) |
| 140 ]) |
| 141 ]); |
| 142 |
| 143 innerShadow = attachShadow(lowerShadow.querySelector('inner-host'),
innerMode, [ |
| 144 element('i', [ |
| 145 element('slot', [], 'inner-slot') |
| 146 ]) |
| 147 ]); |
| 148 |
| 149 return { |
| 150 host: upperHost, |
| 151 target: upperHost.querySelector('a'), |
| 152 upperShadow: upperShadow, |
| 153 upperSlot: upperShadow.querySelector('slot'), |
| 154 lowerShadow: lowerShadow, |
| 155 lowerSlot: lowerShadow.querySelector('slot'), |
| 156 innerShadow: innerShadow, |
| 157 innerSlot: innerShadow.querySelector('slot'), |
| 158 }; |
| 159 } |
| 160 |
| 161 /* |
| 162 upper-host (14) -- (upperShadow; 13) |
| 163 + p (10) + b (12) |
| 164 | + slot (upperSlot; 11) |
| 165 + lower-host (9) -- (lowerShadow; 8) |
| 166 + a (target; 0) + em (7) |
| 167 + inner-host (6) -------- (innerShadow; 5) |
| 168 + span (2) + i (4) |
| 169 + slot (lowerSlot; 1) + slot (innerSlot; 3) |
| 170 */ |
| 171 |
| 172 function testEventUnderTwoShadowRoots(outerUpperMode, outerLowerMode, in
nerMode) { |
| 173 test(function () { |
| 174 var shadow = createNestedShadowTreesWithSlots(innerMode, outerUp
perMode, outerLowerMode); |
| 175 |
| 176 log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: tr
ue, composed: true})); |
| 177 |
| 178 assert_equals(log.length, 15, 'EventPath must contain 15 targets
'); |
| 179 |
| 180 assert_array_equals(log[0], [shadow.target, shadow.target], 'Eve
ntPath[0] must be the target'); |
| 181 assert_array_equals(log[1], [shadow.lowerSlot, shadow.target], '
EventPath[1] must be the slot inside the lower shadow tree'); |
| 182 assert_array_equals(log[2], [shadow.lowerSlot.parentNode, shadow
.target], 'EventPath[2] must be the parent of the slot inside the lower shadow t
ree'); |
| 183 assert_array_equals(log[3], [shadow.innerSlot, shadow.target], '
EventPath[3] must be the slot inside the shadow tree inside the lower shadow tre
e'); |
| 184 assert_array_equals(log[4], [shadow.innerSlot.parentNode, shadow
.target], 'EventPath[4] must be the child of the inner shadow root'); |
| 185 assert_array_equals(log[5], [shadow.innerShadow, shadow.target],
'EventPath[5] must be the inner shadow root'); |
| 186 assert_array_equals(log[6], [shadow.innerShadow.host, shadow.tar
get], 'EventPath[6] must be the host of the inner shadow tree'); |
| 187 assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shad
ow.target], 'EventPath[7] must be the parent of the inner shadow host'); |
| 188 assert_array_equals(log[8], [shadow.lowerShadow, shadow.target],
'EventPath[8] must be the lower shadow root'); |
| 189 assert_array_equals(log[9], [shadow.lowerShadow.host, shadow.tar
get], 'EventPath[9] must be the lower shadow host'); |
| 190 assert_array_equals(log[10], [shadow.host.firstChild, shadow.tar
get], 'EventPath[10] must be the parent of the grand parent of the target'); |
| 191 assert_array_equals(log[11], [shadow.upperSlot, shadow.target],
'EventPath[11] must be the slot inside the upper shadow tree'); |
| 192 assert_array_equals(log[12], [shadow.upperSlot.parentNode, shado
w.target], 'EventPath[12] must be the parent of the slot inside the upper shadow
tree'); |
| 193 assert_array_equals(log[13], [shadow.upperShadow, shadow.target]
, 'EventPath[13] must be the upper shadow root'); |
| 194 assert_array_equals(log[14], [shadow.host, shadow.target], 'Even
tPath[14] must be the host'); |
| 195 |
| 196 }, 'Firing an event on a node with two ancestors with a detached ' +
outerUpperMode + ' and ' + outerLowerMode |
| 197 + ' shadow trees with an inner ' + innerMode + ' shadow tree'); |
| 198 } |
| 199 |
| 200 testEventUnderTwoShadowRoots('open', 'open', 'open'); |
| 201 testEventUnderTwoShadowRoots('open', 'open', 'closed'); |
| 202 testEventUnderTwoShadowRoots('open', 'closed', 'open'); |
| 203 testEventUnderTwoShadowRoots('open', 'closed', 'closed'); |
| 204 testEventUnderTwoShadowRoots('closed', 'open', 'open'); |
| 205 testEventUnderTwoShadowRoots('closed', 'open', 'closed'); |
| 206 testEventUnderTwoShadowRoots('closed', 'closed', 'open'); |
| 207 testEventUnderTwoShadowRoots('closed', 'closed', 'closed'); |
| 208 |
| 209 /* |
| 210 upper-host (11) -- (upperShadow; 10) |
| 211 + p (7) + b (9) |
| 212 | + slot (upperSlot; 8) |
| 213 + lower-host (6) -- (lowerShadow; 5) |
| 214 + a + em (4) |
| 215 + inner-host (3) -- (innerShadow; 2) |
| 216 + span + i (1) |
| 217 + slot + slot (innerSlot, target;
0) |
| 218 */ |
| 219 |
| 220 function testEventInsideNestedShadowsUnderAnotherShadow(outerUpperMode,
outerLowerMode, innerMode) { |
| 221 test(function () { |
| 222 var shadow = createNestedShadowTreesWithSlots(innerMode, outerUp
perMode, outerLowerMode); |
| 223 shadow.deepestNodeInLightDOM = shadow.target; // Needed for disp
atchEventWithLog to attach event listeners. |
| 224 shadow.target = shadow.innerSlot; |
| 225 |
| 226 log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: tr
ue, composed: true})); |
| 227 |
| 228 assert_equals(log.length, 12, 'EventPath must contain 12 targets
'); |
| 229 |
| 230 assert_array_equals(log[0], [shadow.target, shadow.target], 'Eve
ntPath[0] must be the target'); |
| 231 assert_array_equals(log[1], [shadow.target.parentNode, shadow.ta
rget], 'EventPath[1] must be the parent of the target'); |
| 232 assert_array_equals(log[2], [shadow.innerShadow, shadow.target],
'EventPath[2] must be the inner shadow root'); |
| 233 assert_array_equals(log[3], [shadow.innerShadow.host, shadow.inn
erShadow.host], 'EventPath[3] must be the inner shadow host'); |
| 234 assert_array_equals(log[4], [shadow.lowerShadow.firstChild, shad
ow.innerShadow.host], 'EventPath[4] must be the parent of the inner shadow host'
); |
| 235 assert_array_equals(log[5], [shadow.lowerShadow, shadow.innerSha
dow.host], 'EventPath[5] must be the lower (but outer) shadow root'); |
| 236 assert_array_equals(log[6], [shadow.lowerShadow.host, shadow.low
erShadow.host], 'EventPath[6] must be the lower (but outer) shadow root'); |
| 237 assert_array_equals(log[7], [shadow.host.firstChild, shadow.lowe
rShadow.host], 'EventPath[7] must be the slot inside the upper shadow tree'); |
| 238 assert_array_equals(log[8], [shadow.upperSlot, shadow.lowerShado
w.host], 'EventPath[8] must be the slot inside the upper shadow tree'); |
| 239 assert_array_equals(log[9], [shadow.upperSlot.parentNode, shadow
.lowerShadow.host], 'EventPath[9] must be the parent of the slot inside the uppe
r shadow tree'); |
| 240 assert_array_equals(log[10], [shadow.upperShadow, shadow.lowerSh
adow.host], 'EventPath[10] must be the upper shadow root'); |
| 241 assert_array_equals(log[11], [shadow.upperShadow.host, shadow.lo
werShadow.host], 'EventPath[11] must be the host'); |
| 242 |
| 243 }, 'Firing an event on a node with two ancestors with a detached ' +
outerUpperMode + ' and ' + outerLowerMode |
| 244 + ' shadow trees with an inner ' + innerMode + ' shadow tree'); |
| 245 } |
| 246 |
| 247 testEventInsideNestedShadowsUnderAnotherShadow('open', 'open', 'open'); |
| 248 testEventInsideNestedShadowsUnderAnotherShadow('open', 'open', 'closed')
; |
| 249 testEventInsideNestedShadowsUnderAnotherShadow('open', 'closed', 'open')
; |
| 250 testEventInsideNestedShadowsUnderAnotherShadow('open', 'closed', 'closed
'); |
| 251 testEventInsideNestedShadowsUnderAnotherShadow('closed', 'open', 'open')
; |
| 252 testEventInsideNestedShadowsUnderAnotherShadow('closed', 'open', 'closed
'); |
| 253 testEventInsideNestedShadowsUnderAnotherShadow('closed', 'closed', 'open
'); |
| 254 testEventInsideNestedShadowsUnderAnotherShadow('closed', 'closed', 'clos
ed'); |
| 255 |
| 256 </script> |
| 257 </body> |
| 258 </html> |
| OLD | NEW |