/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

/* import-globals-from ../../mochitest/states.js */
/* import-globals-from ../../mochitest/role.js */
loadScripts(
  { name: "states.js", dir: MOCHITESTS_DIR },
  { name: "role.js", dir: MOCHITESTS_DIR }
);

ChromeUtils.defineESModuleGetters(this, {
  CustomizableUITestUtils:
    "resource://testing-common/CustomizableUITestUtils.sys.mjs",
  SearchbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
  SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs",
});

const QUERY = "example";

function isEventForResultItem(event) {
  // XXX: See bug 2016839
  return event.accessible.role == ROLE_OPTION;
}

function isEventForMenuPopup(event) {
  return event.accessible.role == ROLE_MENUPOPUP;
}

function isEventForMenuItem(event) {
  return event.accessible.role == ROLE_MENUITEM;
}

let searchbar;

add_setup(async function () {
  SearchbarTestUtils.init(this);
  SearchTestUtils.init(this);
  // The default engine's suggestions would require network access.
  await SearchTestUtils.updateRemoteSettingsConfig([{ identifier: "engine1" }]);

  let gCUITestUtils = new CustomizableUITestUtils(window);
  searchbar = await gCUITestUtils.addSearchBar();
  // Gives us two more results besides the heuristic search result, so that
  // arrowing through them doesn't wrap around.
  await SearchbarTestUtils.formHistory.add([QUERY, `${QUERY}2`]);

  registerCleanupFunction(async () => {
    await SearchbarTestUtils.promisePopupClose(window);
    searchbar.handleRevert();
    await SearchbarTestUtils.formHistory.clear();
    gCUITestUtils.removeSearchBar();
  });
});

// Check that the searchbar manages accessibility focus appropriately.
async function runTests() {
  let focused = waitForEvent(
    EVENT_FOCUS,
    event => event.accessible.DOMNode == searchbar.inputField
  );
  searchbar.focus();
  let event = await focused;
  let textBox = event.accessible;

  info("Ensuring no focus change when text is typed");
  await SearchbarTestUtils.promiseAutocompleteResultPopup({
    window,
    value: QUERY.slice(0, 3),
  });
  Assert.equal(
    SearchbarTestUtils.getResultCount(window),
    3,
    "Expected the heuristic search result and the form history results"
  );
  // Wait a tick for a11y events to fire.
  await TestUtils.waitForTick();
  testStates(textBox, STATE_FOCUSED);

  info("Ensuring result focus on down arrow (1)");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowDown");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  info("Ensuring focus of another result on down arrow");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowDown");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  if (AppConstants.platform == "macosx") {
    info("Ensuring focus of another result on ctrl-n");
    focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
    EventUtils.synthesizeKey("n", { ctrlKey: true });
    event = await focused;
    testStates(event.accessible, STATE_FOCUSED);

    info("Ensuring focus of another result on ctrl-p");
    focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
    EventUtils.synthesizeKey("p", { ctrlKey: true });
    event = await focused;
    testStates(event.accessible, STATE_FOCUSED);
  }

  info("Ensuring focus of another result on up arrow");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowUp");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  info("Ensuring text box focus on left arrow");
  focused = waitForEvent(EVENT_FOCUS, textBox);
  EventUtils.synthesizeKey("KEY_ArrowLeft");
  await focused;
  testStates(textBox, STATE_FOCUSED);

  await SearchbarTestUtils.promisePopupClose(window);
  // On Mac, down arrow when not at the end of the field moves to the end.
  // Move back to the end so the next press of down arrow opens the popup.
  EventUtils.synthesizeKey("KEY_ArrowRight");

  info("Ensuring result focus on down arrow (2)");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowDown");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  info("Ensuring text box focus when text is typed");
  focused = waitForEvent(EVENT_FOCUS, textBox);
  EventUtils.sendString("m");
  await focused;
  testStates(textBox, STATE_FOCUSED);
  EventUtils.synthesizeKey("KEY_Backspace");
  await SearchbarTestUtils.promiseSearchComplete(window);

  info("Ensuring result focus on down arrow (3)");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowDown");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  info("Ensuring text box focus on backspace");
  focused = waitForEvent(EVENT_FOCUS, textBox);
  EventUtils.synthesizeKey("KEY_Backspace");
  await focused;
  testStates(textBox, STATE_FOCUSED);
  await SearchbarTestUtils.promiseSearchComplete(window);

  info("Ensuring result focus on down arrow (4)");
  focused = waitForEvent(EVENT_FOCUS, isEventForResultItem);
  EventUtils.synthesizeKey("KEY_ArrowDown");
  event = await focused;
  testStates(event.accessible, STATE_FOCUSED);

  info("Ensuring text box focus on text selection");
  focused = waitForEvent(EVENT_FOCUS, textBox);
  EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
  await focused;
  testStates(textBox, STATE_FOCUSED);

  if (
    AppConstants.platform == "macosx" &&
    Services.prefs.getBoolPref("widget.macos.native-context-menus", false)
  ) {
    // With native context menus, we do not observe accessibility events and we
    // cannot send synthetic key events to the menu.
    info("Opening and closing native context menu");
    let contextMenu = searchbar.querySelector(".textbox-contextmenu");
    let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
    EventUtils.synthesizeMouseAtCenter(
      searchbar.querySelector("moz-input-box"),
      { type: "contextmenu" }
    );
    await popupshown;
    let popuphidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
    contextMenu.hidePopup();
    await popuphidden;
  } else {
    info(
      "Ensuring context menu gets menu event on launch, and item focus on down"
    );
    let menuEvent = waitForEvent(
      nsIAccessibleEvent.EVENT_MENUPOPUP_START,
      isEventForMenuPopup
    );
    EventUtils.synthesizeMouseAtCenter(
      searchbar.querySelector("moz-input-box"),
      { type: "contextmenu" }
    );
    await menuEvent;

    focused = waitForEvent(EVENT_FOCUS, isEventForMenuItem);
    EventUtils.synthesizeKey("KEY_ArrowDown");
    event = await focused;
    testStates(event.accessible, STATE_FOCUSED);

    focused = waitForEvent(EVENT_FOCUS, textBox);
    let closed = waitForEvent(
      nsIAccessibleEvent.EVENT_MENUPOPUP_END,
      isEventForMenuPopup
    );
    EventUtils.synthesizeKey("KEY_Escape");
    await closed;
    await focused;
  }
  info("Ensuring searchbar is focused after context menu is dismissed.");
  testStates(textBox, STATE_FOCUSED);
}

addAccessibleTask(``, runTests);
