event-inside-shadow-tree.html (7601B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Shadow DOM: Firing an event inside a shadow tree</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 used to determine event path"> 7 <link rel="help" href="https://w3c.github.io/webcomponents/spec/shadow/#event-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(target, event) { 16 var log = []; 17 18 for (var node = target; node; node = node.parentNode || node.host) { 19 node.addEventListener(event.type, (function (event) { 20 log.push([this, event.target]); 21 }).bind(node)); 22 } 23 24 target.dispatchEvent(event); 25 26 return log; 27 } 28 29 function createShadowRootWithGrandChild(mode) { 30 var host = document.createElement('div'); 31 var root = host.attachShadow({mode: mode}); 32 33 var parent = document.createElement('span'); 34 root.appendChild(parent); 35 36 var target = document.createElement('b'); 37 parent.appendChild(target); 38 return {target: target, parent: parent, root: root, host: host}; 39 } 40 41 function testEventInDetachedShadowTree(mode) { 42 test(function () { 43 var shadow = createShadowRootWithGrandChild(mode); 44 45 log = dispatchEventWithLog(shadow.target, new Event('foo', {composed: true, bubbles: true})); 46 47 assert_equals(log.length, 4, 'EventPath must contain [target, parent, shadow root, shadow host]'); 48 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target'); 49 assert_array_equals(log[1], [shadow.parent, shadow.target], 'EventPath[1] must be the parent of the target'); 50 assert_array_equals(log[2], [shadow.root, shadow.target], 'EventPath[2] must be the shadow root'); 51 assert_array_equals(log[3], [shadow.host, shadow.host], 'EventPath[3] must be the shadow host'); 52 53 }, 'Firing an event inside a grand child of a detached ' + mode + ' mode shadow tree'); 54 } 55 56 testEventInDetachedShadowTree('open'); 57 testEventInDetachedShadowTree('closed'); 58 59 function testEventInShadowTreeInsideDocument(mode) { 60 test(function () { 61 var shadow = createShadowRootWithGrandChild(mode); 62 document.body.appendChild(shadow.host); 63 64 log = dispatchEventWithLog(shadow.target, new Event('foo', {composed: true, bubbles: true})); 65 66 assert_equals(log.length, 7, 'EventPath must contain [target, parent, shadow root, shadow host, body, html, document]'); 67 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target'); 68 assert_array_equals(log[1], [shadow.parent, shadow.target], 'EventPath[1] must be the parent of the target'); 69 assert_array_equals(log[2], [shadow.root, shadow.target], 'EventPath[2] must be the shadow root'); 70 assert_array_equals(log[3], [shadow.host, shadow.host], 'EventPath[3] must be the shadow host'); 71 assert_array_equals(log[4], [document.body, shadow.host], 'EventPath[4] must be the body element (parent of shadow host)'); 72 assert_array_equals(log[5], [document.documentElement, shadow.host], 'EventPath[5] must be the html element'); 73 assert_array_equals(log[6], [document, shadow.host], 'EventPath[6] must be the document node'); 74 75 }, 'Firing an event inside a grand child of an in-document ' + mode + ' mode shadow tree'); 76 } 77 78 testEventInShadowTreeInsideDocument('open'); 79 testEventInShadowTreeInsideDocument('closed'); 80 81 function createNestedShadowRoot(innerMode, outerMode) { 82 var outerHost = document.createElement('div'); 83 var outerRoot = outerHost.attachShadow({mode: outerMode}); 84 85 var outerChild = document.createElement('p'); 86 outerRoot.appendChild(outerChild); 87 88 var innerHost = document.createElement('span'); 89 outerChild.appendChild(innerHost); 90 91 var innerRoot = innerHost.attachShadow({mode: innerMode}); 92 var innerChild = document.createElement('span'); 93 innerRoot.appendChild(innerChild); 94 95 return {target: innerChild, innerRoot: innerRoot, innerHost: innerHost, outerChild: outerChild, outerRoot: outerRoot, outerHost: outerHost}; 96 } 97 98 function testEventInDetachedNestedShadowTree(innerMode, outerMode) { 99 test(function () { 100 var shadow = createNestedShadowRoot(innerMode, outerMode); 101 102 log = dispatchEventWithLog(shadow.target, new Event('bar', {composed: true, bubbles: true})); 103 104 assert_equals(log.length, 6, 'EventPath must contain [target, inner root, inner host, parent, outer root, outer host]'); 105 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target'); 106 assert_array_equals(log[1], [shadow.innerRoot, shadow.target], 'EventPath[1] must be the inner shadow root'); 107 assert_array_equals(log[2], [shadow.innerHost, shadow.innerHost], 'EventPath[2] must be the inner shadow host'); 108 assert_array_equals(log[3], [shadow.outerChild, shadow.innerHost], 'EventPath[3] must be the parent of the inner shadow host'); 109 assert_array_equals(log[4], [shadow.outerRoot, shadow.innerHost], 'EventPath[4] must be the outer shadow root'); 110 assert_array_equals(log[5], [shadow.outerHost, shadow.outerHost], 'EventPath[5] must be the outer shadow host'); 111 112 }, 'Firing an event inside a detached ' + innerMode + ' mode shadow tree inside ' + outerMode + ' mode shadow tree'); 113 } 114 115 testEventInDetachedNestedShadowTree('open', 'open'); 116 testEventInDetachedNestedShadowTree('open', 'closed'); 117 testEventInDetachedNestedShadowTree('closed', 'open'); 118 testEventInDetachedNestedShadowTree('closed', 'closed'); 119 120 function testEventInNestedShadowTreeInsideDocument(innerMode, outerMode) { 121 test(function () { 122 var shadow = createNestedShadowRoot(innerMode, outerMode); 123 document.body.appendChild(shadow.outerHost); 124 125 log = dispatchEventWithLog(shadow.target, new Event('bar', {composed: true, bubbles: true})); 126 127 assert_equals(log.length, 9, 'EventPath must contain [target, inner root, inner host, parent, outer root, outer host]'); 128 assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[0] must be the target'); 129 assert_array_equals(log[1], [shadow.innerRoot, shadow.target], 'EventPath[1] must be the inner shadow root'); 130 assert_array_equals(log[2], [shadow.innerHost, shadow.innerHost], 'EventPath[2] must be the inner shadow host'); 131 assert_array_equals(log[3], [shadow.outerChild, shadow.innerHost], 'EventPath[3] must be the parent of the inner shadow host'); 132 assert_array_equals(log[4], [shadow.outerRoot, shadow.innerHost], 'EventPath[4] must be the outer shadow root'); 133 assert_array_equals(log[5], [shadow.outerHost, shadow.outerHost], 'EventPath[5] must be the outer shadow host'); 134 assert_array_equals(log[6], [document.body, shadow.outerHost], 'EventPath[6] must be the body element'); 135 assert_array_equals(log[7], [document.documentElement, shadow.outerHost], 'EventPath[7] must be the html element'); 136 assert_array_equals(log[8], [document, shadow.outerHost], 'EventPath[8] must be the document node'); 137 138 }, 'Firing an event inside an in-document ' + innerMode + ' mode shadow tree inside ' + outerMode + ' mode shadow tree'); 139 } 140 141 testEventInNestedShadowTreeInsideDocument('open', 'open'); 142 testEventInNestedShadowTreeInsideDocument('open', 'closed'); 143 testEventInNestedShadowTreeInsideDocument('closed', 'open'); 144 testEventInNestedShadowTreeInsideDocument('closed', 'closed'); 145 146 </script> 147 </body> 148 </html>