tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

event-inside-slotted-node.html (14764B)


      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 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(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.querySelector('b'), host: host,
     70                    slot: root.querySelector('slot'), slotParent: root.querySelector('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: true, composed: true}));
     78 
     79                assert_equals(log.length, 6, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host]');
     80                assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[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], 'EventPath[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], 'EventPath[4] must be the shadow root');
     85                assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[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: true, composed: true}));
     99 
    100                assert_equals(log.length, 9, 'EventPath must contain [target, target parent, slot, slot parent, shadow root, shadow host, body, html, document]');
    101                assert_array_equals(log[0], [shadow.target, shadow.target], 'EventPath[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], 'EventPath[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], 'EventPath[4] must be the shadow root');
    106                assert_array_equals(log[5], [shadow.host, shadow.target], 'EventPath[5] must be the shadow host');
    107                assert_array_equals(log[6], [document.body, shadow.target], 'EventPath[6] must be the body element');
    108                assert_array_equals(log[7], [document.documentElement, shadow.target], 'EventPath[7] must be the html element');
    109                assert_array_equals(log[8], [document, shadow.target], 'EventPath[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, outerLowerMode) {
    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, innerMode) {
    173            test(function () {
    174                var shadow = createNestedShadowTreesWithSlots(innerMode, outerUpperMode, outerLowerMode);
    175 
    176                log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: true, 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], 'EventPath[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 tree');
    183                assert_array_equals(log[3], [shadow.innerSlot, shadow.target], 'EventPath[3] must be the slot inside the shadow tree inside the lower shadow tree');
    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.target], 'EventPath[6] must be the host of the inner shadow tree');
    187                assert_array_equals(log[7], [shadow.lowerShadow.firstChild, shadow.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.target], 'EventPath[9] must be the lower shadow host');
    190                assert_array_equals(log[10], [shadow.host.firstChild, shadow.target], '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, shadow.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], 'EventPath[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, outerUpperMode, outerLowerMode);
    223                shadow.deepestNodeInLightDOM = shadow.target; // Needed for dispatchEventWithLog to attach event listeners.
    224                shadow.target = shadow.innerSlot;
    225 
    226                log = dispatchEventWithLog(shadow, new Event('foo', {bubbles: true, 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], 'EventPath[0] must be the target');
    231                assert_array_equals(log[1], [shadow.target.parentNode, shadow.target], '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.innerShadow.host], 'EventPath[3] must be the inner shadow host');
    234                assert_array_equals(log[4], [shadow.lowerShadow.firstChild, shadow.innerShadow.host], 'EventPath[4] must be the parent of the inner shadow host');
    235                assert_array_equals(log[5], [shadow.lowerShadow, shadow.innerShadow.host], 'EventPath[5] must be the lower (but outer) shadow root');
    236                assert_array_equals(log[6], [shadow.lowerShadow.host, shadow.lowerShadow.host], 'EventPath[6] must be the lower (but outer) shadow root');
    237                assert_array_equals(log[7], [shadow.host.firstChild, shadow.lowerShadow.host], 'EventPath[7] must be the slot inside the upper shadow tree');
    238                assert_array_equals(log[8], [shadow.upperSlot, shadow.lowerShadow.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 upper shadow tree');
    240                assert_array_equals(log[10], [shadow.upperShadow, shadow.lowerShadow.host], 'EventPath[10] must be the upper shadow root');
    241                assert_array_equals(log[11], [shadow.upperShadow.host, shadow.lowerShadow.host], 'EventPath[11] must be the host');
    242 
    243            }, 'Firing an event on a node within a ' + innerMode + ' shadow tree that is itself a ' + outerLowerMode
    244                + ' shadow tree (the latter being the descendent of a host for a separate ' + outerUpperMode + ' 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', 'closed');
    255 
    256    </script>
    257    </body>
    258 </html>