browser_test_focus_urlbar.js (15400B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* import-globals-from ../../mochitest/states.js */ 7 /* import-globals-from ../../mochitest/role.js */ 8 loadScripts( 9 { name: "states.js", dir: MOCHITESTS_DIR }, 10 { name: "role.js", dir: MOCHITESTS_DIR } 11 ); 12 13 ChromeUtils.defineESModuleGetters(this, { 14 PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", 15 PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", 16 UrlbarProvider: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs", 17 UrlbarProvidersManager: 18 "moz-src:///browser/components/urlbar/UrlbarProvidersManager.sys.mjs", 19 UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs", 20 UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", 21 UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs", 22 }); 23 24 function isEventForAutocompleteItem(event) { 25 return event.accessible.role == ROLE_COMBOBOX_OPTION; 26 } 27 28 function isEventForButton(event) { 29 return event.accessible.role == ROLE_PUSHBUTTON; 30 } 31 32 function isEventForOneOffEngine(event) { 33 let parent = event.accessible.parent; 34 return ( 35 event.accessible.role == ROLE_PUSHBUTTON && 36 parent && 37 parent.role == ROLE_GROUPING && 38 parent.name 39 ); 40 } 41 42 function isEventForMenuPopup(event) { 43 return event.accessible.role == ROLE_MENUPOPUP; 44 } 45 46 function isEventForMenuItem(event) { 47 return event.accessible.role == ROLE_MENUITEM; 48 } 49 50 function isEventForResultButton(event) { 51 let parent = event.accessible.parent; 52 return ( 53 event.accessible.role == ROLE_PUSHBUTTON && 54 parent?.role == ROLE_COMBOBOX_LIST 55 ); 56 } 57 58 /** 59 * A test provider. 60 */ 61 class TipTestProvider extends UrlbarProvider { 62 constructor(matches) { 63 super(); 64 this._matches = matches; 65 } 66 get name() { 67 return "TipTestProvider"; 68 } 69 get type() { 70 return UrlbarUtils.PROVIDER_TYPE.PROFILE; 71 } 72 async isActive() { 73 return true; 74 } 75 isRestricting() { 76 return true; 77 } 78 async startQuery(context, addCallback) { 79 this._context = context; 80 for (const match of this._matches) { 81 addCallback(this, match); 82 } 83 } 84 } 85 86 // Check that the URL bar manages accessibility focus appropriately. 87 async function runTests() { 88 // TODO: Remove in https://bugzilla.mozilla.org/show_bug.cgi?id=1923383 89 await SpecialPowers.pushPrefEnv({ 90 set: [["browser.urlbar.scotchBonnet.enableOverride", false]], 91 }); 92 registerCleanupFunction(async function () { 93 await UrlbarTestUtils.promisePopupClose(window); 94 await PlacesUtils.history.clear(); 95 }); 96 97 await PlacesTestUtils.addVisits([ 98 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 99 "http://example1.com/blah", 100 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 101 "http://example2.com/blah", 102 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 103 "http://example1.com/", 104 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 105 "http://example2.com/", 106 ]); 107 108 // Ensure initial state. 109 await UrlbarTestUtils.promisePopupClose(window); 110 111 let focused = waitForEvent( 112 EVENT_FOCUS, 113 event => event.accessible.role == ROLE_ENTRY 114 ); 115 gURLBar.focus(); 116 let event = await focused; 117 let textBox = event.accessible; 118 // Ensure the URL bar is ready for a new URL to be typed. 119 // Sometimes, when this test runs, the existing text isn't selected when the 120 // URL bar is focused. Pressing escape twice ensures that the popup is 121 // closed and that the existing text is selected. 122 EventUtils.synthesizeKey("KEY_Escape"); 123 EventUtils.synthesizeKey("KEY_Escape"); 124 125 info("Ensuring no focus change when first text is typed"); 126 await UrlbarTestUtils.promiseAutocompleteResultPopup({ 127 window, 128 waitForFocus, 129 value: "example", 130 fireInputEvent: true, 131 }); 132 // Wait a tick for a11y events to fire. 133 await TestUtils.waitForTick(); 134 testStates(textBox, STATE_FOCUSED); 135 136 info("Ensuring no focus change on backspace"); 137 EventUtils.synthesizeKey("KEY_Backspace"); 138 await UrlbarTestUtils.promiseSearchComplete(window); 139 // Wait a tick for a11y events to fire. 140 await TestUtils.waitForTick(); 141 testStates(textBox, STATE_FOCUSED); 142 143 info("Ensuring no focus change on text selection and delete"); 144 EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true }); 145 EventUtils.synthesizeKey("KEY_Delete"); 146 await UrlbarTestUtils.promiseSearchComplete(window); 147 // Wait a tick for a11y events to fire. 148 await TestUtils.waitForTick(); 149 testStates(textBox, STATE_FOCUSED); 150 151 info("Ensuring autocomplete focus on down arrow (1)"); 152 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 153 EventUtils.synthesizeKey("KEY_ArrowDown"); 154 event = await focused; 155 testStates(event.accessible, STATE_FOCUSED); 156 157 info("Ensuring focus of another autocomplete item on down arrow"); 158 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 159 EventUtils.synthesizeKey("KEY_ArrowDown"); 160 event = await focused; 161 testStates(event.accessible, STATE_FOCUSED); 162 163 info("Ensuring previous arrow selection state doesn't get stale on input"); 164 focused = waitForEvent(EVENT_FOCUS, textBox); 165 EventUtils.sendString("z"); 166 await focused; 167 EventUtils.synthesizeKey("KEY_Backspace"); 168 await UrlbarTestUtils.promiseSearchComplete(window); 169 testStates(textBox, STATE_FOCUSED); 170 171 info("Ensuring focus of another autocomplete item on down arrow"); 172 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 173 EventUtils.synthesizeKey("KEY_ArrowDown"); 174 event = await focused; 175 testStates(event.accessible, STATE_FOCUSED); 176 177 if (AppConstants.platform == "macosx") { 178 info("Ensuring focus of another autocomplete item on ctrl-n"); 179 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 180 EventUtils.synthesizeKey("n", { ctrlKey: true }); 181 event = await focused; 182 testStates(event.accessible, STATE_FOCUSED); 183 184 info("Ensuring focus of another autocomplete item on ctrl-p"); 185 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 186 EventUtils.synthesizeKey("p", { ctrlKey: true }); 187 event = await focused; 188 testStates(event.accessible, STATE_FOCUSED); 189 } 190 191 info("Ensuring focus of another autocomplete item on up arrow"); 192 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 193 EventUtils.synthesizeKey("KEY_ArrowUp"); 194 event = await focused; 195 testStates(event.accessible, STATE_FOCUSED); 196 197 info("Ensuring text box focus on left arrow"); 198 focused = waitForEvent(EVENT_FOCUS, textBox); 199 EventUtils.synthesizeKey("KEY_ArrowLeft"); 200 await focused; 201 testStates(textBox, STATE_FOCUSED); 202 203 gURLBar.view.close(); 204 // On Mac, down arrow when not at the end of the field moves to the end. 205 // Move back to the end so the next press of down arrow opens the popup. 206 EventUtils.synthesizeKey("KEY_ArrowRight"); 207 208 info("Ensuring autocomplete focus on down arrow (2)"); 209 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 210 EventUtils.synthesizeKey("KEY_ArrowDown"); 211 event = await focused; 212 testStates(event.accessible, STATE_FOCUSED); 213 214 info("Ensuring autocomplete focus on arrow up for search settings button"); 215 focused = waitForEvent(EVENT_FOCUS, isEventForButton); 216 EventUtils.synthesizeKey("KEY_ArrowUp"); 217 event = await focused; 218 testStates(event.accessible, STATE_FOCUSED); 219 220 info("Ensuring text box focus when text is typed"); 221 focused = waitForEvent(EVENT_FOCUS, textBox); 222 EventUtils.sendString("z"); 223 await focused; 224 testStates(textBox, STATE_FOCUSED); 225 EventUtils.synthesizeKey("KEY_Backspace"); 226 await UrlbarTestUtils.promiseSearchComplete(window); 227 228 info("Ensuring autocomplete focus on down arrow (3)"); 229 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 230 EventUtils.synthesizeKey("KEY_ArrowDown"); 231 event = await focused; 232 testStates(event.accessible, STATE_FOCUSED); 233 234 info("Ensuring text box focus on backspace"); 235 focused = waitForEvent(EVENT_FOCUS, textBox); 236 EventUtils.synthesizeKey("KEY_Backspace"); 237 await focused; 238 testStates(textBox, STATE_FOCUSED); 239 await UrlbarTestUtils.promiseSearchComplete(window); 240 241 info("Ensuring autocomplete focus on arrow down (4)"); 242 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 243 EventUtils.synthesizeKey("KEY_ArrowDown"); 244 event = await focused; 245 testStates(event.accessible, STATE_FOCUSED); 246 247 // Arrow down to the last result. 248 const resultCount = UrlbarTestUtils.getResultCount(window); 249 while (UrlbarTestUtils.getSelectedRowIndex(window) != resultCount - 1) { 250 EventUtils.synthesizeKey("KEY_ArrowDown"); 251 } 252 253 info("Ensuring one-off search button focus on arrow down"); 254 focused = waitForEvent(EVENT_FOCUS, isEventForOneOffEngine); 255 EventUtils.synthesizeKey("KEY_ArrowDown"); 256 event = await focused; 257 testStates(event.accessible, STATE_FOCUSED); 258 259 info("Ensuring autocomplete focus on arrow up"); 260 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 261 EventUtils.synthesizeKey("KEY_ArrowUp"); 262 event = await focused; 263 testStates(event.accessible, STATE_FOCUSED); 264 265 info("Ensuring text box focus on text selection"); 266 focused = waitForEvent(EVENT_FOCUS, textBox); 267 EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true }); 268 await focused; 269 testStates(textBox, STATE_FOCUSED); 270 271 if (AppConstants.platform == "macosx") { 272 // On Mac, ctrl-n after arrow left/right does not re-open the popup. 273 // Type some text so the next press of ctrl-n opens the popup. 274 EventUtils.sendString("ple"); 275 276 info("Ensuring autocomplete focus on ctrl-n"); 277 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 278 EventUtils.synthesizeKey("n", { ctrlKey: true }); 279 event = await focused; 280 testStates(event.accessible, STATE_FOCUSED); 281 } 282 283 if ( 284 AppConstants.platform == "macosx" && 285 Services.prefs.getBoolPref("widget.macos.native-context-menus", false) 286 ) { 287 // With native context menus, we do not observe accessibility events and we 288 // cannot send synthetic key events to the menu. 289 info("Opening and closing context native context menu"); 290 let contextMenu = gURLBar.querySelector(".textbox-contextmenu"); 291 let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); 292 EventUtils.synthesizeMouseAtCenter(gURLBar.querySelector("moz-input-box"), { 293 type: "contextmenu", 294 }); 295 await popupshown; 296 let popuphidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden"); 297 contextMenu.hidePopup(); 298 await popuphidden; 299 } else { 300 info( 301 "Ensuring context menu gets menu event on launch, and item focus on down" 302 ); 303 let menuEvent = waitForEvent( 304 nsIAccessibleEvent.EVENT_MENUPOPUP_START, 305 isEventForMenuPopup 306 ); 307 EventUtils.synthesizeMouseAtCenter(gURLBar.querySelector("moz-input-box"), { 308 type: "contextmenu", 309 }); 310 await menuEvent; 311 312 focused = waitForEvent(EVENT_FOCUS, isEventForMenuItem); 313 EventUtils.synthesizeKey("KEY_ArrowDown"); 314 event = await focused; 315 testStates(event.accessible, STATE_FOCUSED); 316 317 focused = waitForEvent(EVENT_FOCUS, textBox); 318 let closed = waitForEvent( 319 nsIAccessibleEvent.EVENT_MENUPOPUP_END, 320 isEventForMenuPopup 321 ); 322 EventUtils.synthesizeKey("KEY_Escape"); 323 await closed; 324 await focused; 325 } 326 info("Ensuring address bar is focused after context menu is dismissed."); 327 testStates(textBox, STATE_FOCUSED); 328 } 329 330 // We test TIP results in their own test so the spoofed results don't interfere 331 // with the main test. 332 async function runTipTests() { 333 let matches = [ 334 new UrlbarResult({ 335 type: UrlbarUtils.RESULT_TYPE.URL, 336 source: UrlbarUtils.RESULT_SOURCE.HISTORY, 337 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 338 payload: { url: "http://mozilla.org/a" }, 339 }), 340 new UrlbarResult({ 341 type: UrlbarUtils.RESULT_TYPE.TIP, 342 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 343 payload: { 344 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 345 helpUrl: "http://example.com/", 346 type: "test", 347 titleL10n: { id: "urlbar-search-tips-confirm" }, 348 buttons: [ 349 { 350 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 351 url: "http://example.com/", 352 l10n: { id: "urlbar-search-tips-confirm" }, 353 }, 354 ], 355 }, 356 }), 357 new UrlbarResult({ 358 type: UrlbarUtils.RESULT_TYPE.URL, 359 source: UrlbarUtils.RESULT_SOURCE.HISTORY, 360 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 361 payload: { url: "http://mozilla.org/b" }, 362 }), 363 new UrlbarResult({ 364 type: UrlbarUtils.RESULT_TYPE.URL, 365 source: UrlbarUtils.RESULT_SOURCE.HISTORY, 366 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 367 payload: { url: "http://mozilla.org/c" }, 368 }), 369 ]; 370 371 // Ensure the tip appears in the expected position. 372 matches[1].suggestedIndex = 2; 373 374 let provider = new TipTestProvider(matches); 375 UrlbarProvidersManager.registerProvider(provider); 376 377 registerCleanupFunction(async function () { 378 UrlbarProvidersManager.unregisterProvider(provider); 379 }); 380 381 let focused = waitForEvent( 382 EVENT_FOCUS, 383 event => event.accessible.role == ROLE_ENTRY 384 ); 385 gURLBar.focus(); 386 let event = await focused; 387 let textBox = event.accessible; 388 389 EventUtils.synthesizeKey("KEY_Escape"); 390 EventUtils.synthesizeKey("KEY_Escape"); 391 392 info("Ensuring no focus change when first text is typed"); 393 await UrlbarTestUtils.promiseAutocompleteResultPopup({ 394 window, 395 waitForFocus, 396 value: "example", 397 fireInputEvent: true, 398 }); 399 // Wait a tick for a11y events to fire. 400 await TestUtils.waitForTick(); 401 testStates(textBox, STATE_FOCUSED); 402 403 info("Ensuring autocomplete focus on down arrow (1)"); 404 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 405 EventUtils.synthesizeKey("KEY_ArrowDown"); 406 event = await focused; 407 testStates(event.accessible, STATE_FOCUSED); 408 409 info("Ensuring the tip button is focused on down arrow"); 410 info("Also ensuring that the tip button is a part of a labelled group"); 411 focused = waitForEvent(EVENT_FOCUS, isEventForResultButton); 412 EventUtils.synthesizeKey("KEY_ArrowDown"); 413 event = await focused; 414 testStates(event.accessible, STATE_FOCUSED); 415 416 info("Ensuring the help button is focused on tab"); 417 info("Also ensuring that the help button is a part of a labelled group"); 418 focused = waitForEvent(EVENT_FOCUS, isEventForResultButton); 419 EventUtils.synthesizeKey("KEY_Tab"); 420 event = await focused; 421 testStates(event.accessible, STATE_FOCUSED); 422 423 info("Ensuring autocomplete focus on down arrow (2)"); 424 focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); 425 EventUtils.synthesizeKey("KEY_ArrowDown"); 426 event = await focused; 427 testStates(event.accessible, STATE_FOCUSED); 428 429 info("Ensuring the help button is focused on shift+tab"); 430 focused = waitForEvent(EVENT_FOCUS, isEventForResultButton); 431 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 432 event = await focused; 433 testStates(event.accessible, STATE_FOCUSED); 434 435 info("Ensuring text box focus on left arrow, and not back to the tip button"); 436 focused = waitForEvent(EVENT_FOCUS, textBox); 437 EventUtils.synthesizeKey("KEY_ArrowLeft"); 438 await focused; 439 testStates(textBox, STATE_FOCUSED); 440 } 441 442 addAccessibleTask(``, runTests); 443 addAccessibleTask(``, runTipTests);