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

"use strict";

const kDevPanelID = "PanelUI-developer-tools";

/**
 * Test the behavior of key presses on various toolbar buttons.
 */

function waitForLocationChange() {
  let promise = new Promise(resolve => {
    let wpl = {
      onLocationChange() {
        gBrowser.removeProgressListener(wpl);
        resolve();
      },
    };
    gBrowser.addProgressListener(wpl);
  });
  return promise;
}

add_task(async function setPref() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["test.wait300msAfterTabSwitch", true],
      ["browser.toolbars.keyboard_navigation", true],
    ],
  });
});

// Test activation of the app menu button from the keyboard.
// The app menu should appear and focus should move inside it.
add_task(async function testAppMenuButtonPress() {
  let button = document.getElementById("PanelUI-menu-button");
  let focused = BrowserTestUtils.waitForEvent(
    window.PanelUI.mainView,
    "focus",
    true
  );
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  await focused;
  ok(true, "Focus inside app menu after toolbar button pressed");
  let hidden = BrowserTestUtils.waitForEvent(
    window.PanelUI.panel,
    "popuphidden"
  );
  EventUtils.synthesizeKey("KEY_Escape");
  await hidden;
});

// Test that the app menu doesn't open when a key other than space or enter is
// pressed .
add_task(async function testAppMenuButtonWrongKey() {
  let button = document.getElementById("PanelUI-menu-button");
  await focusAndActivateElement(button, () =>
    EventUtils.synthesizeKey("KEY_Tab")
  );
  await TestUtils.waitForTick();
  is(window.PanelUI.panel.state, "closed", "App menu is closed after tab");
});

// Test activation of the Library button from the keyboard.
// The Library menu should appear and focus should move inside it.
add_task(async function testLibraryButtonPress() {
  CustomizableUI.addWidgetToArea("library-button", "nav-bar");
  let button = document.getElementById("library-button");
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  let view = document.getElementById("appMenu-libraryView");
  let focused = BrowserTestUtils.waitForEvent(view, "focus", true);
  await focused;
  ok(true, "Focus inside Library menu after toolbar button pressed");
  let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
  view.closest("panel").hidePopup();
  await hidden;
  CustomizableUI.removeWidgetFromArea("library-button");
});

// Test activation of the Developer button from the keyboard.
// This is a customizable widget of type "view".
// The Developer menu should appear and focus should move inside it.
add_task(async function testDeveloperButtonPress() {
  CustomizableUI.addWidgetToArea(
    "developer-button",
    CustomizableUI.AREA_NAVBAR
  );
  let button = document.getElementById("developer-button");
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  let view = document.getElementById(kDevPanelID);
  let focused = BrowserTestUtils.waitForEvent(view, "focus", true);
  await focused;
  ok(true, "Focus inside Developer menu after toolbar button pressed");
  let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
  view.closest("panel").hidePopup();
  await hidden;
  CustomizableUI.reset();
});

// Test that the Developer menu doesn't open when a key other than space or
// enter is pressed .
add_task(async function testDeveloperButtonWrongKey() {
  CustomizableUI.addWidgetToArea(
    "developer-button",
    CustomizableUI.AREA_NAVBAR
  );
  let button = document.getElementById("developer-button");
  await focusAndActivateElement(button, () =>
    EventUtils.synthesizeKey("KEY_Tab")
  );
  await TestUtils.waitForTick();
  let panel = document.getElementById(kDevPanelID).closest("panel");
  ok(!panel || panel.state == "closed", "Developer menu not open after tab");
  CustomizableUI.reset();
});

// Test activation of the Page actions button from the keyboard.
// The Page Actions menu should appear and focus should move inside it.
add_task(async function testPageActionsButtonPress() {
  // The page actions button is not normally visible, so we must
  // unhide it.
  BrowserPageActions.mainButtonNode.style.display = "flex";
  registerCleanupFunction(() => {
    BrowserPageActions.mainButtonNode.style.removeProperty("display");
  });
  await BrowserTestUtils.withNewTab("https://example.com", async function () {
    let button = document.getElementById("pageActionButton");
    await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
    let view = document.getElementById("pageActionPanelMainView");
    let focused = BrowserTestUtils.waitForEvent(view, "focus", true);
    await focused;
    ok(true, "Focus inside Page Actions menu after toolbar button pressed");
    let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
    view.closest("panel").hidePopup();
    await hidden;
  });
});

// Test activation of the Back and Forward buttons from the keyboard.
add_task(async function testBackForwardButtonPress() {
  await BrowserTestUtils.withNewTab(
    "https://example.com/1",
    async function (aBrowser) {
      BrowserTestUtils.startLoadingURIString(aBrowser, "https://example.com/2");

      await BrowserTestUtils.browserLoaded(aBrowser);
      let backButton = document.getElementById("back-button");
      let onLocationChange = waitForLocationChange();
      await focusAndActivateElement(backButton, () =>
        EventUtils.synthesizeKey(" ")
      );
      await onLocationChange;
      ok(true, "Location changed after back button pressed");

      let forwardButton = document.getElementById("forward-button");
      onLocationChange = waitForLocationChange();
      await focusAndActivateElement(forwardButton, () =>
        EventUtils.synthesizeKey(" ")
      );
      await onLocationChange;
      ok(true, "Location changed after forward button pressed");
    }
  );
});

// Test activation of the Reload button from the keyboard.
// This is a toolbarbutton with a click handler and no command handler, but
// the toolbar keyboard navigation code should handle keyboard activation.
add_task(async function testReloadButtonPress() {
  await BrowserTestUtils.withNewTab(
    "https://example.com/1",
    async function (aBrowser) {
      info("Waiting for button to be enabled.");
      let button = document.getElementById("reload-button");
      await TestUtils.waitForCondition(() => !button.disabled);
      let loaded = BrowserTestUtils.browserLoaded(aBrowser);
      info("Focusing button");
      await focusAndActivateElement(button, () => {
        info("Pressing space on the button");
        EventUtils.synthesizeKey(" ");
      });
      info("Waiting for load.");
      await loaded;
      ok(true, "Page loaded after Reload button pressed");
    }
  );
});

// Test activation of the Sidebars button from the keyboard.
// This is a toolbarbutton with a command handler.
add_task(async function testSidebarsButtonPress() {
  const { SidebarController } = window;
  let sidebarRevampEnabled = Services.prefs.getBoolPref(
    "sidebar.revamp",
    false
  );
  let sidebar, sidebarBox;
  if (!sidebarRevampEnabled) {
    CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar");
  } else {
    // Expanded is only available with vertical tabs enabled
    await SpecialPowers.pushPrefEnv({
      set: [["sidebar.verticalTabs", true]],
    });
    await SidebarController.initializeUIState({ launcherExpanded: false });
  }
  let button = document.getElementById("sidebar-button");
  ok(!button.checked, "Sidebars button not checked at start of test");
  if (!sidebarRevampEnabled) {
    sidebarBox = document.getElementById("sidebar-box");
    ok(sidebarBox.hidden, "Sidebar hidden at start of test");
  } else {
    sidebar = document.querySelector("sidebar-main");
    ok(!sidebar.expanded, "Sidebar collapsed at start of test");
  }
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  if (!sidebarRevampEnabled) {
    await TestUtils.waitForCondition(() => button.checked);
    ok(true, "Sidebars button checked after press");
    ok(!sidebarBox.hidden, "Sidebar visible after press");
  } else {
    await TestUtils.waitForCondition(
      () => sidebar.expanded,
      "Sidebar expanded after press"
    );
    ok(sidebar.expanded, "Sidebar expanded after press");
  }
  // Make sure the sidebar is fully loaded before we hide it.
  // Otherwise, the unload event might call JS which isn't loaded yet.
  // We can't use BrowserTestUtils.browserLoaded because it fails on non-tab
  // docs. Instead, wait for something in the JS script.
  if (!sidebarRevampEnabled) {
    let sidebarWin = document.getElementById("sidebar").contentWindow;
    await TestUtils.waitForCondition(() => sidebarWin.PlacesUIUtils);
  } else {
    await sidebar.updateComplete;
  }
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  if (!sidebarRevampEnabled) {
    await TestUtils.waitForCondition(() => !button.checked);
    ok(true, "Sidebars button not checked after press");
    ok(sidebarBox.hidden, "Sidebar hidden after press");
    CustomizableUI.removeWidgetFromArea("sidebar-button");
  } else {
    await TestUtils.waitForCondition(
      () => !sidebar.expanded,
      "Sidebar not expanded after press"
    );
    ok(!sidebar.expanded, "Sidebar not expanded after press");
    await SpecialPowers.popPrefEnv();
  }
});

// Test activation of the Bookmark this page button from the keyboard.
// This is an image with a click handler on its parent and no command handler,
// but the toolbar keyboard navigation code should handle keyboard activation.
add_task(async function testBookmarkButtonPress() {
  await BrowserTestUtils.withNewTab("https://example.com", async function () {
    let button = document.getElementById("star-button-box");
    StarUI._createPanelIfNeeded();
    let panel = document.getElementById("editBookmarkPanel");
    let focused = BrowserTestUtils.waitForEvent(panel, "focus", true);
    // The button ignores activation while the bookmarked status is being
    // updated. So, wait for it to finish updating.
    await TestUtils.waitForCondition(
      () => BookmarkingUI.status != BookmarkingUI.STATUS_UPDATING
    );
    await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
    await focused;
    ok(true, "Focus inside edit bookmark panel after Bookmark button pressed");
    let hidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
    EventUtils.synthesizeKey("KEY_Escape");
    await hidden;
  });
});

// Test activation of the Bookmarks Menu button from the keyboard.
// This is a button with type="menu".
// The Bookmarks Menu should appear.
add_task(async function testBookmarksmenuButtonPress() {
  CustomizableUI.addWidgetToArea(
    "bookmarks-menu-button",
    CustomizableUI.AREA_NAVBAR
  );
  let button = document.getElementById("bookmarks-menu-button");
  let menu = document.getElementById("BMB_bookmarksPopup");
  let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  await shown;
  ok(true, "Bookmarks Menu shown after toolbar button pressed");
  let hidden = BrowserTestUtils.waitForEvent(menu, "popuphidden");
  menu.hidePopup();
  await hidden;
  CustomizableUI.reset();
});

// Test activation of the overflow button from the keyboard.
// The overflow menu should appear and focus should move inside it.
add_task(async function testOverflowButtonPress() {
  // Move something to the overflow menu to make the button appear.
  CustomizableUI.addWidgetToArea(
    "developer-button",
    CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
  );
  let button = document.getElementById("nav-bar-overflow-button");
  let view = document.getElementById("widget-overflow-mainView");
  let focused = BrowserTestUtils.waitForEvent(view, "focus", true);
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  await focused;
  ok(true, "Focus inside overflow menu after toolbar button pressed");
  let panel = document.getElementById("widget-overflow");
  let hidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
  panel.hidePopup();
  await hidden;
  CustomizableUI.reset();
});

// Test activation of the Downloads button from the keyboard.
// The Downloads panel should appear and focus should move inside it.
add_task(async function testDownloadsButtonPress() {
  DownloadsButton.unhide();
  let button = document.getElementById("downloads-button");
  let panel = document.getElementById("downloadsPanel");
  let focused = BrowserTestUtils.waitForEvent(panel, "focus", true);
  await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" "));
  await focused;
  ok(true, "Focus inside Downloads panel after toolbar button pressed");
  let hidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
  panel.hidePopup();
  await hidden;
  DownloadsButton.hide();
});
