summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/shadow-dom/focus/focus-pseudo-matches-on-shadow-host.html
blob: 34f8c0129456b38fc46a10c9247ed304abfd9715 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content=":focus should match a shadow host which contains the focused element">
<link rel="help" href="https://html.spec.whatwg.org/#element-has-the-focus">
<link rel="help=" href="https://bugs.webkit.org/show_bug.cgi?id=202432">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<input id="defaultFocus" autofocus>
<div id="log"></div>
<div id="container"></div>
<script>

let focusedDefault = false;
function didFocusDefault() { }
function handleFocus() {
    if (!focusedDefault) {
        // Use step_timeout here to avoid nested focusing steps.
        // For example, <input id="defaultFocus" autofocus> could run scripts
        // while it's autofocusing which may run the tests, so that the
        // focus() usage in the tests becomes nested focusing steps.
        step_timeout(function() {
          testInMode('open', false);
          testInMode('open', true);
          testInMode('closed', false);
          testInMode('closed', true);
        }, 0);
    }
    focusedDefault = true;
    didFocusDefault();
}
defaultFocus.addEventListener('focus', handleFocus);

function prepare(test)
{
    test.add_cleanup(() => {
        defaultFocus.focus();
        container.textContent = '';
    });
    return new Promise((resolve) => {
        if (focusedDefault)
            resolve();
        else
            didFocusDefault = resolve;
    });
}

function testInMode(mode, delegatesFocus) {
    const modeString = `{mode:${mode}, delegatesFocus:${delegatesFocus}}`;
    promise_test(async function () {
        await prepare(this);
        const host = document.createElement('div');
        container.appendChild(host);
        const shadowRoot = host.attachShadow({mode, delegatesFocus});
        shadowRoot.innerHTML = '<input>';
        assert_equals(document.activeElement, defaultFocus);
        assert_equals(shadowRoot.activeElement, null);
        assert_false(host.matches(':focus'));
    }, `:focus must not match a shadow host with ${modeString} shadow root that does not contain the focused element`);

    promise_test(async function () {
        await prepare(this);
        const host = document.createElement('div');
        document.body.appendChild(host);
        const shadowRoot = host.attachShadow({mode, delegatesFocus});
        shadowRoot.innerHTML = '<input>';
        shadowRoot.firstChild.focus();
        assert_equals(document.activeElement, host);
        assert_equals(shadowRoot.activeElement, shadowRoot.firstChild);
        assert_true(host.matches(':focus'));
    }, `:focus must match a shadow host with ${modeString} shadow root that contains the focused element`);

    promise_test(async function () {
        await prepare(this);
        const host = document.createElement('div');
        container.appendChild(host);
        const shadowRoot = host.attachShadow({mode, delegatesFocus});
        shadowRoot.innerHTML = '<slot>';
        host.innerHTML = '<input>';
        host.firstChild.focus();
        assert_equals(document.activeElement, host.firstChild);
        assert_equals(shadowRoot.activeElement, null);
        assert_false(host.matches(':focus'));
    }, `:focus must not match a shadow host with ${modeString} shadow root contains the focused element assigned to a slot`);

    promise_test(async function() {
        await prepare(this);
        const host1 = document.body.appendChild(document.createElement('div'));
        const shadowRoot1 = host1.attachShadow({mode, delegatesFocus});
        const host2 = shadowRoot1.appendChild(document.createElement('div'));
        const shadowRoot2 = host2.attachShadow({mode, delegatesFocus});
        shadowRoot2.innerHTML = '<input>';
        shadowRoot2.firstChild.focus();
        assert_equals(document.activeElement, host1);
        assert_equals(shadowRoot1.activeElement, host2);
        assert_equals(shadowRoot2.activeElement, shadowRoot2.firstChild);
        assert_true(host1.matches(':focus'));
        assert_true(host2.matches(':focus'));
    }, `:focus must match all shadow hosts which are ancestors of a foccused element; ${modeString}`);

    promise_test(async function() {
        await prepare(this);
        const host = document.body.appendChild(document.createElement('div'));
        const shadowRoot = host.attachShadow({mode, delegatesFocus});
        shadowRoot.innerHTML = '<input>';
        const input = shadowRoot.firstChild;
        const outer = document.body.appendChild(document.createElement('div'));

        assert_false(host.matches(':focus'));
        input.focus();
        assert_true(host.matches(':focus'));
        outer.appendChild(input);
        assert_false(host.matches(':focus'));
    }, `:focus behavior on tree structure changes; ${modeString}`);
}

</script>
</body>
</html>