focus-autofocus.html (9407B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta name="author" title="Sean Feng" href="mailto:sefeng@mozilla.com"> 5 <meta name="assert" content="Elements with autofocus should have high precedence over other elements for delegates focus"> 6 <link rel="help" href="https://github.com/whatwg/html/pull/6990"> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-vendor.js"></script> 11 <script src="resources/shadow-utils.js"></script> 12 </head> 13 14 <body> 15 <script> 16 function createShadowDOMTree() { 17 // <div #host> (delegatesFocus = true) 18 // #shadowRoot 19 // <div #firstOuterDiv> 20 // <div #innertHost> 21 // #shadowRoot 22 // <div #firstInnerDiv> 23 // <div #secondInnerDiv> 24 // <div #secondOuterDiv> 25 const host = document.createElement("div"); 26 host.setAttribute("id", "host"); 27 const outerRoot = host.attachShadow({mode: "open", delegatesFocus: true}); 28 29 const firstOuterDiv = document.createElement("div"); 30 31 const innerHost = document.createElement("div"); 32 const innerRoot = innerHost.attachShadow({mode: "open"}); 33 const firstInnerDiv = document.createElement("div"); 34 const secondInnerDiv = document.createElement("div"); 35 innerRoot.appendChild(firstInnerDiv); 36 innerRoot.appendChild(secondInnerDiv); 37 38 const secondOuterDiv = document.createElement("div"); 39 40 outerRoot.appendChild(firstOuterDiv); 41 outerRoot.appendChild(innerHost); 42 outerRoot.appendChild(secondOuterDiv); 43 document.body.appendChild(host); 44 return [ 45 host, 46 outerRoot, 47 firstOuterDiv, 48 secondOuterDiv, 49 innerHost, 50 innerRoot, 51 firstInnerDiv, 52 secondInnerDiv 53 ] 54 } 55 56 function resetShadowDOMTree() { 57 const host = document.getElementById("host"); 58 if (host) { 59 host.remove(); 60 } 61 return createShadowDOMTree(); 62 } 63 64 function resetTabIndexAndFocus( 65 firstOuterDiv, 66 secondOuterDiv, 67 firstInnerDiv, 68 secondInnerDiv, 69 outerRoot, 70 innerRoot 71 ) { 72 firstOuterDiv.removeAttribute("tabindex"); 73 firstOuterDiv.removeAttribute("autofocus"); 74 75 secondOuterDiv.removeAttribute("tabindex"); 76 secondOuterDiv.removeAttribute("autofocus"); 77 78 firstInnerDiv.removeAttribute("tabindex"); 79 firstInnerDiv.removeAttribute("autofocus"); 80 81 secondInnerDiv.removeAttribute("tabindex"); 82 secondInnerDiv.removeAttribute("autofocus"); 83 84 resetFocus(document); 85 resetFocus(outerRoot); 86 resetFocus(innerRoot); 87 } 88 89 function setAllTabIndexTo( 90 firstOuterDiv, 91 secondOuterDiv, 92 firstInnerDiv, 93 secondInnerDiv, 94 tabIndex 95 ) { 96 firstOuterDiv.tabIndex = tabIndex; 97 secondOuterDiv.tabIndex = tabIndex; 98 firstInnerDiv.tabIndex = tabIndex; 99 secondInnerDiv.tabIndex = tabIndex; 100 } 101 102 test(function() { 103 const [ 104 host, 105 outerRoot, 106 firstOuterDiv, 107 secondOuterDiv, 108 innerHost, 109 innerRoot, 110 firstInnerDiv, 111 secondInnerDiv 112 ] = resetShadowDOMTree(); 113 114 resetTabIndexAndFocus( 115 firstOuterDiv, 116 secondOuterDiv, 117 firstInnerDiv, 118 secondInnerDiv, 119 outerRoot, 120 innerRoot 121 ); 122 123 setAllTabIndexTo( 124 firstOuterDiv, 125 secondOuterDiv, 126 firstInnerDiv, 127 secondInnerDiv, 128 0 129 ); 130 131 // <div #host> (delegatesFocus = true) 132 // #shadowRoot 133 // <div tabIndex=0 #firstOuterDiv> 134 // <div #innertHost> 135 // #shadowRoot 136 // <div tabIndex=0 #firstInnerDiv> 137 // <div tabIndex=0 #secondInnerDiv> 138 // <div tabIndex=0 autofocus #secondOuterDiv> 139 secondOuterDiv.autofocus = true; 140 secondOuterDiv.setAttribute("autofocus", true); 141 142 host.focus(); 143 144 assert_equals(document.activeElement, host); 145 assert_equals(outerRoot.activeElement, secondOuterDiv); 146 }, "The second input should be focused since it has autofocus"); 147 148 test(function() { 149 const [ 150 host, 151 outerRoot, 152 firstOuterDiv, 153 secondOuterDiv, 154 innerHost, 155 innerRoot, 156 firstInnerDiv, 157 secondInnerDiv 158 ] = resetShadowDOMTree(); 159 160 resetTabIndexAndFocus( 161 firstOuterDiv, 162 secondOuterDiv, 163 firstInnerDiv, 164 secondInnerDiv, 165 outerRoot, 166 innerRoot 167 ); 168 169 // <div #host> (delegatesFocus = true) 170 // #shadowRoot 171 // <div #firstOuterDiv> 172 // <div #innertHost> 173 // #shadowRoot 174 // <div tabIndex=0 #firstInnerDiv> 175 // <div tabIndex=0 autofocus #secondInnerDiv> 176 // <div #secondOuterDiv> 177 firstInnerDiv.tabIndex = 0; 178 secondInnerDiv.tabIndex = 0; 179 secondInnerDiv.setAttribute("autofocus", true); 180 181 host.focus(); 182 assert_equals(document.activeElement, document.body); 183 assert_equals(outerRoot.activeElement, null); 184 }, "Focus should not be delegated to the autofocus element because the inner host doesn't have delegates focus"); 185 186 test(function() { 187 const [ 188 host, 189 outerRoot, 190 firstOuterDiv, 191 secondOuterDiv, 192 innerHost, 193 innerRoot, 194 firstInnerDiv, 195 secondInnerDiv 196 ] = resetShadowDOMTree(); 197 198 resetTabIndexAndFocus( 199 firstOuterDiv, 200 secondOuterDiv, 201 firstInnerDiv, 202 secondInnerDiv, 203 outerRoot, 204 innerRoot 205 ); 206 207 const newInnerHost = document.createElement("div"); 208 const newInnerRoot = newInnerHost.attachShadow({mode: "open", delegatesFocus: true}); 209 const newFirstInnerDiv = document.createElement("div"); 210 const newSecondInnerDiv = document.createElement("div"); 211 newFirstInnerDiv.setAttribute("tabIndex", 0); 212 newSecondInnerDiv.setAttribute("tabIndex", 0); 213 214 newSecondInnerDiv.setAttribute("autofocus", true); 215 newInnerRoot.appendChild(newFirstInnerDiv); 216 newInnerRoot.appendChild(newSecondInnerDiv); 217 218 // <div #host> (delegatesFocus = true) 219 // #shadowRoot 220 // <div #firstOuterDiv> 221 // <div #innertHost> (delegatesFocus = true) 222 // #shadowRoot 223 // <div tabIndex=0 #newFirstInnerDiv> 224 // <div tabIndex=0 autofocus #newSecondInnerDiv> 225 // <div #secondOuterDiv> 226 outerRoot.replaceChild(newInnerHost, innerHost); 227 228 host.focus(); 229 230 assert_equals(document.activeElement, host); 231 assert_equals(outerRoot.activeElement, newInnerHost); 232 assert_equals(newInnerRoot.activeElement, newSecondInnerDiv); 233 }, "Focus should be delegated to the autofocus element when the inner host has delegates focus"); 234 235 test(function() { 236 const [ 237 host, 238 outerRoot, 239 firstOuterDiv, 240 secondOuterDiv, 241 innerHost, 242 innerRoot, 243 firstInnerDiv, 244 secondInnerDiv 245 ] = resetShadowDOMTree(); 246 247 resetTabIndexAndFocus( 248 firstOuterDiv, 249 secondOuterDiv, 250 firstInnerDiv, 251 secondInnerDiv, 252 outerRoot, 253 innerRoot 254 ); 255 256 // <div #host> (delegatesFocus = true) 257 // #shadowRoot 258 // <slot> 259 // (slotted) <div autofocus tabIndex=0 #slottedAutofocus></div> 260 // <div tabIndex=0 #firstOuterDiv> 261 // <div #innertHost> 262 // #shadowRoot 263 // <div tabIndex=0 #firstInnerDiv> 264 // <div tabIndex=0 autofocus #secondInnerDiv> 265 // <div #secondOuterDiv> 266 267 const slottedAutofocus = document.createElement("div"); 268 slottedAutofocus.tabIndex = 0; 269 slottedAutofocus.setAttribute("autofocus", true); 270 host.appendChild(slottedAutofocus); 271 272 const slot = document.createElement("slot"); 273 outerRoot.insertBefore(slot, firstOuterDiv); 274 275 firstOuterDiv.tabIndex = 0; 276 277 host.focus(); 278 assert_equals(document.activeElement, host); 279 assert_equals(outerRoot.activeElement, firstOuterDiv); 280 }, "Focus should not be delegated to the slotted elements"); 281 282 test(function() { 283 const [ 284 host, 285 outerRoot, 286 firstOuterDiv, 287 secondOuterDiv, 288 innerHost, 289 innerRoot, 290 firstInnerDiv, 291 secondInnerDiv 292 ] = resetShadowDOMTree(); 293 294 resetTabIndexAndFocus( 295 firstOuterDiv, 296 secondOuterDiv, 297 firstInnerDiv, 298 secondInnerDiv, 299 outerRoot, 300 innerRoot 301 ); 302 303 // <div #host> (delegatesFocus = true) 304 // #shadowRoot 305 // <div #firstOuterDiv> 306 // <div tabIndex=0 #firstNestedDiv> 307 // <div tabIndex=0 #secondNestedDiv> 308 // <div tabIndex=0 autofocus #thirdNestedDiv> 309 // <div #innertHost> 310 // #shadowRoot 311 // <div #firstInnerDiv> 312 // <div #secondInnerDiv> 313 // <div autofocus tabIndex=0 #secondOuterDiv> 314 315 secondInnerDiv.tabIndex = 0; 316 secondInnerDiv.setAttribute("autofocus", true); 317 318 const firstNestedDiv = document.createElement("div"); 319 const secondNestedDiv = document.createElement("div"); 320 const thirdNestedDiv = document.createElement("div"); 321 322 firstNestedDiv.tabIndex = 0; 323 secondNestedDiv.tabIndex = 0; 324 thirdNestedDiv.tabIndex = 0; 325 thirdNestedDiv.setAttribute("autofocus", true); 326 327 firstOuterDiv.appendChild(firstNestedDiv); 328 firstNestedDiv.appendChild(secondNestedDiv); 329 secondNestedDiv.appendChild(thirdNestedDiv); 330 331 host.focus(); 332 333 assert_equals(document.activeElement, host); 334 assert_equals(outerRoot.activeElement, thirdNestedDiv); 335 }, "Focus should be delegated to the nested div which has autofocus based on the tree order"); 336 </script> 337 </body> 338 </html>