tor-browser

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

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);