tor-browser

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

focus-pseudo-matches-on-shadow-host.html (4943B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
      5 <meta name="assert" content=":focus should match a shadow host which contains the focused element">
      6 <link rel="help" href="https://html.spec.whatwg.org/#element-has-the-focus">
      7 <link rel="help=" href="https://bugs.webkit.org/show_bug.cgi?id=202432">
      8 <script src="/resources/testharness.js"></script>
      9 <script src="/resources/testharnessreport.js"></script>
     10 </head>
     11 <body>
     12 <input id="defaultFocus" autofocus>
     13 <div id="log"></div>
     14 <div id="container"></div>
     15 <script>
     16 
     17 let focusedDefault = false;
     18 function didFocusDefault() { }
     19 function handleFocus() {
     20    if (!focusedDefault) {
     21        // Use step_timeout here to avoid nested focusing steps.
     22        // For example, <input id="defaultFocus" autofocus> could run scripts
     23        // while it's autofocusing which may run the tests, so that the
     24        // focus() usage in the tests becomes nested focusing steps.
     25        step_timeout(function() {
     26          testInMode('open', false);
     27          testInMode('open', true);
     28          testInMode('closed', false);
     29          testInMode('closed', true);
     30        }, 0);
     31    }
     32    focusedDefault = true;
     33    didFocusDefault();
     34 }
     35 defaultFocus.addEventListener('focus', handleFocus);
     36 
     37 function prepare(test)
     38 {
     39    test.add_cleanup(() => {
     40        defaultFocus.focus();
     41        container.textContent = '';
     42    });
     43    return new Promise((resolve) => {
     44        if (focusedDefault)
     45            resolve();
     46        else
     47            didFocusDefault = resolve;
     48    });
     49 }
     50 
     51 function testInMode(mode, delegatesFocus) {
     52    const modeString = `{mode:${mode}, delegatesFocus:${delegatesFocus}}`;
     53    promise_test(async function () {
     54        await prepare(this);
     55        const host = document.createElement('div');
     56        container.appendChild(host);
     57        const shadowRoot = host.attachShadow({mode, delegatesFocus});
     58        shadowRoot.innerHTML = '<input>';
     59        assert_equals(document.activeElement, defaultFocus);
     60        assert_equals(shadowRoot.activeElement, null);
     61        assert_false(host.matches(':focus'));
     62    }, `:focus must not match a shadow host with ${modeString} shadow root that does not contain the focused element`);
     63 
     64    promise_test(async function () {
     65        await prepare(this);
     66        const host = document.createElement('div');
     67        document.body.appendChild(host);
     68        const shadowRoot = host.attachShadow({mode, delegatesFocus});
     69        shadowRoot.innerHTML = '<input>';
     70        shadowRoot.firstChild.focus();
     71        assert_equals(document.activeElement, host);
     72        assert_equals(shadowRoot.activeElement, shadowRoot.firstChild);
     73        assert_true(host.matches(':focus'));
     74    }, `:focus must match a shadow host with ${modeString} shadow root that contains the focused element`);
     75 
     76    promise_test(async function () {
     77        await prepare(this);
     78        const host = document.createElement('div');
     79        container.appendChild(host);
     80        const shadowRoot = host.attachShadow({mode, delegatesFocus});
     81        shadowRoot.innerHTML = '<slot>';
     82        host.innerHTML = '<input>';
     83        host.firstChild.focus();
     84        assert_equals(document.activeElement, host.firstChild);
     85        assert_equals(shadowRoot.activeElement, null);
     86        assert_false(host.matches(':focus'));
     87    }, `:focus must not match a shadow host with ${modeString} shadow root contains the focused element assigned to a slot`);
     88 
     89    promise_test(async function() {
     90        await prepare(this);
     91        const host1 = document.body.appendChild(document.createElement('div'));
     92        const shadowRoot1 = host1.attachShadow({mode, delegatesFocus});
     93        const host2 = shadowRoot1.appendChild(document.createElement('div'));
     94        const shadowRoot2 = host2.attachShadow({mode, delegatesFocus});
     95        shadowRoot2.innerHTML = '<input>';
     96        shadowRoot2.firstChild.focus();
     97        assert_equals(document.activeElement, host1);
     98        assert_equals(shadowRoot1.activeElement, host2);
     99        assert_equals(shadowRoot2.activeElement, shadowRoot2.firstChild);
    100        assert_true(host1.matches(':focus'));
    101        assert_true(host2.matches(':focus'));
    102    }, `:focus must match all shadow hosts which are ancestors of a foccused element; ${modeString}`);
    103 
    104    promise_test(async function() {
    105        await prepare(this);
    106        const host = document.body.appendChild(document.createElement('div'));
    107        const shadowRoot = host.attachShadow({mode, delegatesFocus});
    108        shadowRoot.innerHTML = '<input>';
    109        const input = shadowRoot.firstChild;
    110        const outer = document.body.appendChild(document.createElement('div'));
    111 
    112        assert_false(host.matches(':focus'));
    113        input.focus();
    114        assert_true(host.matches(':focus'));
    115        outer.appendChild(input);
    116        assert_false(host.matches(':focus'));
    117    }, `:focus behavior on tree structure changes; ${modeString}`);
    118 }
    119 
    120 </script>
    121 </body>
    122 </html>