tor-browser

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

offsetParent-across-shadow-boundaries.html (13115B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
      5 <meta name="assert" content="offsetParent should only return nodes that are shadow including ancestor">
      6 <link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent">
      7 <link rel="help" href="https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor">
      8 <script src="/resources/testharness.js"></script>
      9 <script src="/resources/testharnessreport.js"></script>
     10 <script src="resources/event-path-test-helpers.js"></script>
     11 </head>
     12 <body>
     13 <div id="log"></div>
     14 <div id="container" style="position: relative"></div>
     15 <script>
     16 
     17 const container = document.getElementById('container');
     18 
     19 function testOffsetParentInShadowTree(mode) {
     20    test(function () {
     21        const host = document.createElement('div');
     22        container.appendChild(host);
     23        this.add_cleanup(() => host.remove());
     24        const shadowRoot = host.attachShadow({mode});
     25        shadowRoot.innerHTML = '<div id="relativeParent" style="position: relative; padding-left: 100px; padding-top: 70px;"><div id="target"></div></div>';
     26        const relativeParent = shadowRoot.getElementById('relativeParent');
     27 
     28        assert_true(relativeParent instanceof HTMLDivElement);
     29        const target = shadowRoot.getElementById('target');
     30        assert_equals(target.offsetParent, relativeParent);
     31        assert_equals(target.offsetLeft, 100);
     32        assert_equals(target.offsetTop, 70);
     33    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode`);
     34 }
     35 
     36 testOffsetParentInShadowTree('open');
     37 testOffsetParentInShadowTree('closed');
     38 
     39 function testOffsetParentInNestedShadowTrees(mode) {
     40    test(function () {
     41        const outerHost = document.createElement('section');
     42        container.appendChild(outerHost);
     43        this.add_cleanup(() => outerHost.remove());
     44        const outerShadow = outerHost.attachShadow({mode});
     45        outerShadow.innerHTML = '<section id="outerParent" style="position: absolute; top: 50px; left: 50px;"></section>';
     46 
     47        const innerHost = document.createElement('div');
     48        outerShadow.firstChild.appendChild(innerHost);
     49        const innerShadow = innerHost.attachShadow({mode});
     50        innerShadow.innerHTML = '<div id="innerParent" style="position: relative; padding-left: 60px; padding-top: 40px;"><div id="target"></div></div>';
     51        const innerParent = innerShadow.getElementById('innerParent');
     52 
     53        const target = innerShadow.getElementById('target');
     54        assert_true(innerParent instanceof HTMLDivElement);
     55        assert_equals(target.offsetParent, innerParent);
     56        assert_equals(target.offsetLeft, 60);
     57        assert_equals(target.offsetTop, 40);
     58 
     59        outerHost.remove();
     60    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode even when nested`);
     61 }
     62 
     63 testOffsetParentInNestedShadowTrees('open');
     64 testOffsetParentInNestedShadowTrees('closed');
     65 
     66 function testOffsetParentOnElementAssignedToSlotInsideOffsetParent(mode) {
     67    test(function () {
     68        const host = document.createElement('div');
     69        host.innerHTML = '<div id="target"></div>'
     70        container.appendChild(host);
     71        this.add_cleanup(() => host.remove());
     72        const shadowRoot = host.attachShadow({mode});
     73        shadowRoot.innerHTML = '<div style="position: relative; padding-left: 85px; padding-top: 45px;"><slot></slot></div>';
     74        const target = host.querySelector('#target');
     75        assert_equals(target.offsetParent, container);
     76        assert_equals(target.offsetLeft, 85);
     77        assert_equals(target.offsetTop, 45);
     78    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
     79 }
     80 
     81 testOffsetParentOnElementAssignedToSlotInsideOffsetParent('open');
     82 testOffsetParentOnElementAssignedToSlotInsideOffsetParent('closed');
     83 
     84 function testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock(mode) {
     85    test(function () {
     86        const host = document.createElement('div');
     87        host.innerHTML = '<div id="target"></div>';
     88        container.appendChild(host);
     89        this.add_cleanup(() => host.remove());
     90        const shadowRoot = host.attachShadow({mode});
     91        shadowRoot.innerHTML = [
     92            '<div style="transform: translate(10px, 10px);" id="wrapper">',
     93            '<div style="position: fixed; padding-left: 85px; padding-top: 45px;">',
     94                '<slot></slot>',
     95            '</div></div>'].join('');
     96        const target = host.querySelector('#target');
     97        assert_equals(target.offsetParent, container);
     98        assert_equals(target.offsetLeft, 85);
     99        assert_equals(target.offsetTop, 45);
    100    }, `offsetParent must return the fixed position containing block of an element when the context object is assigned to a slot within a fixed containing block in shadow tree of ${mode} mode`);
    101 }
    102 
    103 testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock('open');
    104 testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock('closed');
    105 
    106 function testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock(mode) {
    107    test(function () {
    108        const host = document.createElement('div');
    109        host.innerHTML = '<div id="target" style="position: fixed;"></div>';
    110        container.appendChild(host);
    111        this.add_cleanup(() => host.remove());
    112        const shadowRoot = host.attachShadow({mode});
    113        shadowRoot.innerHTML = [
    114            '<div style="transform: translate(10px, 10px);" id="wrapper">',
    115            '<div style="position: fixed; padding-left: 85px; padding-top: 45px;">',
    116                '<slot></slot>',
    117            '</div></div>'].join('');
    118        const target = host.querySelector('#target');
    119        assert_equals(target.offsetParent, container);
    120        assert_equals(target.offsetLeft, 85);
    121        assert_equals(target.offsetTop, 45);
    122    }, `offsetParent must return the fixed position containing block of a fixed element when the context object is assigned to a slot within a fixed containing block in shadow tree of ${mode} mode`);
    123 }
    124 
    125 testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock('open');
    126 testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock('closed');
    127 
    128 function testOffsetParentOnElementAssignedToSlotInsideFixedPosition(mode) {
    129    test(function () {
    130        const host = document.createElement('div');
    131        host.innerHTML = '<div id="target"></div>';
    132        container.appendChild(host);
    133        this.add_cleanup(() => host.remove());
    134        const shadowRoot = host.attachShadow({mode});
    135        shadowRoot.innerHTML = [
    136            '<div id="fixed" style="position: fixed; padding-left: 85px; padding-top: 45px;">',
    137                '<slot></slot>',
    138            '</div>'].join('');
    139        const target = host.querySelector('#target');
    140        const fixed = shadowRoot.querySelector('#fixed');
    141        assert_equals(target.offsetParent, null);
    142        assert_equals(target.offsetLeft, 85 + fixed.offsetLeft);
    143        assert_equals(target.offsetTop, 45 + fixed.offsetTop);
    144    }, `offsetParent must return null when the context object is assigned to a slot without a fixed containing block in shadow tree of ${mode} mode`);
    145 }
    146 
    147 testOffsetParentOnElementAssignedToSlotInsideFixedPosition('open');
    148 testOffsetParentOnElementAssignedToSlotInsideFixedPosition('closed');
    149 
    150 function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode) {
    151    test(function () {
    152        const host = document.createElement('div');
    153        host.innerHTML = '<div id="target" style="border:solid 1px blue;">hi</div>';
    154        const previousBlock = document.createElement('div');
    155        previousBlock.style.height = '12px';
    156        container.append(previousBlock, host);
    157        this.add_cleanup(() => { container.innerHTML = ''; });
    158        const shadowRoot = host.attachShadow({mode});
    159        shadowRoot.innerHTML = '<section style="position: relative; margin-left: 20px; margin-top: 100px; background: #ccc"><div style="position: absolute; top: 10px; left: 10px;"><slot></slot></div></section>';
    160        const target = host.querySelector('#target');
    161        assert_equals(target.offsetParent, container);
    162        assert_equals(target.offsetLeft, 30);
    163        assert_equals(target.offsetTop, 122);
    164    }, `offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
    165 }
    166 
    167 testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('open');
    168 testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('closed');
    169 
    170 function testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees(mode) {
    171    test(function () {
    172        const outerHost = document.createElement('section');
    173        outerHost.innerHTML = '<div id="target"></div>';
    174        container.appendChild(outerHost);
    175        this.add_cleanup(() => outerHost.remove());
    176        const outerShadow = outerHost.attachShadow({mode});
    177        outerShadow.innerHTML = '<section style="position: absolute; top: 40px; left: 50px;"><div id="innerHost"><slot></slot></div></section>';
    178 
    179        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
    180        innerShadow.innerHTML = '<div style="position: absolute; top: 200px; margin-left: 100px;"><slot></slot></div>';
    181 
    182        const target = outerHost.querySelector('#target');
    183        assert_equals(target.offsetParent, container);
    184        assert_equals(target.offsetLeft, 150);
    185        assert_equals(target.offsetTop, 240);
    186        outerHost.remove();
    187    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of ${mode} mode`);
    188 }
    189 
    190 testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('open');
    191 testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('closed');
    192 
    193 function testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent(mode) {
    194    test(function () {
    195        const outerHost = document.createElement('section');
    196        container.appendChild(outerHost);
    197        this.add_cleanup(() => outerHost.remove());
    198        const outerShadow = outerHost.attachShadow({mode});
    199        outerShadow.innerHTML = '<div id="innerHost"><div id="target"></div></div>';
    200 
    201        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
    202        innerShadow.innerHTML = '<div style="position: absolute; top: 23px; left: 24px;"><slot></slot></div>';
    203 
    204        const target = outerShadow.querySelector('#target');
    205        assert_equals(target.offsetParent, container);
    206        assert_equals(target.offsetLeft, 24);
    207        assert_equals(target.offsetTop, 23);
    208    }, `offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of ${mode} mode did not have any offset parent`);
    209 }
    210 
    211 testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('open');
    212 testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('closed');
    213 
    214 function testOffsetParentOnUnassignedChild(mode) {
    215    test(function () {
    216        const host = document.createElement('section');
    217        host.innerHTML = '<div id="target"></div>';
    218        this.add_cleanup(() => host.remove());
    219        container.appendChild(host);
    220        const shadowRoot = host.attachShadow({mode});
    221        shadowRoot.innerHTML = '<section style="position: absolute; top: 50px; left: 50px;">content</section>';
    222        const target = host.querySelector('#target');
    223        assert_equals(target.offsetParent, null);
    224        assert_equals(target.offsetLeft, 0);
    225        assert_equals(target.offsetTop, 0);
    226    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not assigned to any slot`);
    227 }
    228 
    229 testOffsetParentOnUnassignedChild('open');
    230 testOffsetParentOnUnassignedChild('closed');
    231 
    232 function testOffsetParentOnAssignedChildNotInFlatTree(mode) {
    233    test(function () {
    234        const outerHost = document.createElement('section');
    235        outerHost.innerHTML = '<div id="target"></div>';
    236        container.appendChild(outerHost);
    237        this.add_cleanup(() => outerHost.remove());
    238        const outerShadow = outerHost.attachShadow({mode});
    239        outerShadow.innerHTML = '<div id="innerHost"><div style="position: absolute; top: 50px; left: 50px;"><slot></slot></div></div>';
    240 
    241        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
    242        innerShadow.innerHTML = '<div>content</div>';
    243 
    244        const target = outerHost.querySelector('#target');
    245        assert_equals(target.offsetParent, null);
    246        assert_equals(target.offsetLeft, 0);
    247        assert_equals(target.offsetTop, 0);
    248    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not in the flat tree`);
    249 }
    250 
    251 testOffsetParentOnAssignedChildNotInFlatTree('open');
    252 testOffsetParentOnAssignedChildNotInFlatTree('closed');
    253 
    254 </script>
    255 </body>
    256 </html>