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>