/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
var overflowList = document.getElementById(
  navbar.getAttribute("default-overflowtarget")
);

const kTestBtn1 = "test-addWidgetToArea-overflow";
const kTestBtn2 = "test-removeWidgetFromArea-overflow";
const kTestBtn3 = "test-createWidget-overflow";
const kTestBtn4 = "test-createWidget-overflow-first-item";
const kTestBtn5 = "test-addWidgetToArea-overflow-first-item";
const kSidebarBtn = "sidebar-button";
const kLibraryButton = "library-button";
const kDownloadsBtn = "downloads-button";
const kSearchBox = "search-container";

var originalWindowWidth;

// Adding a widget should add it next to the widget it's being inserted next to.
add_task(async function subsequent_widget() {
  let sidebarRevampEnabled = Services.prefs.getBoolPref(
    "sidebar.revamp",
    false
  );
  createDummyXULButton(kTestBtn1, "Test");
  if (sidebarRevampEnabled) {
    createDummyXULButton(kTestBtn2, "Test2");
  }
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should start subsequent_widget with a non-overflowing toolbar."
  );
  ok(
    CustomizableUI.inDefaultState,
    "Should start subsequent_widget in default state."
  );
  CustomizableUI.addWidgetToArea(
    !sidebarRevampEnabled ? kSidebarBtn : kTestBtn2,
    "nav-bar"
  );
  await waitForElementShown(
    document.getElementById(!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2)
  );

  originalWindowWidth = ensureToolbarOverflow(window, false);
  await TestUtils.waitForCondition(() => {
    return (
      navbar.hasAttribute("overflowing") &&
      document
        .getElementById(!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2)
        .getAttribute("overflowedItem") == "true"
    );
  });
  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
  ok(
    !navbar.querySelector(
      `#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`
    ),
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should no longer be in the navbar`
  );

  let overflowBtnNode = overflowList.querySelector(
    `#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`
  );
  ok(
    overflowBtnNode,
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should be overflowing`
  );
  ok(
    overflowBtnNode && overflowBtnNode.getAttribute("overflowedItem") == "true",
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should have overflowedItem attribute`
  );

  let placementOfOverflowButton = CustomizableUI.getWidgetIdsInArea(
    navbar.id
  ).indexOf(!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2);
  CustomizableUI.addWidgetToArea(
    kTestBtn1,
    navbar.id,
    placementOfOverflowButton
  );
  ok(
    !navbar.querySelector("#" + kTestBtn1),
    "New button should not be in the navbar"
  );
  let newButtonNode = overflowList.querySelector("#" + kTestBtn1);
  ok(newButtonNode, "New button should be overflowing");
  ok(
    newButtonNode && newButtonNode.getAttribute("overflowedItem") == "true",
    "New button should have overflowedItem attribute"
  );
  let nextEl = newButtonNode && newButtonNode.nextElementSibling;
  is(
    nextEl && nextEl.id,
    !sidebarRevampEnabled ? kSidebarBtn : kTestBtn2,
    `Test button should be next to ${
      !sidebarRevampEnabled ? "sidebar" : "test 2"
    } button.`
  );

  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should not have an overflowing toolbar."
  );
  ok(
    navbar.querySelector(`#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`),
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should be in the navbar`
  );
  ok(
    overflowBtnNode && overflowBtnNode.getAttribute("overflowedItem") != "true",
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should no longer have overflowedItem attribute`
  );
  ok(
    !overflowList.querySelector(
      `#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`
    ),
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should no longer be overflowing`
  );
  ok(
    navbar.querySelector("#" + kTestBtn1),
    "Test button should be in the navbar"
  );
  ok(
    !overflowList.querySelector("#" + kTestBtn1),
    "Test button should no longer be overflowing"
  );
  ok(
    newButtonNode && newButtonNode.getAttribute("overflowedItem") != "true",
    "New button should no longer have overflowedItem attribute"
  );
  let el = document.getElementById(kTestBtn1);
  if (el) {
    CustomizableUI.removeWidgetFromArea(kTestBtn1);
    el.remove();
  }
  CustomizableUI.removeWidgetFromArea(
    !sidebarRevampEnabled ? kSidebarBtn : kTestBtn2
  );
  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
});

// Removing a widget should remove it from the overflow list if that is where it is, and update it accordingly.
add_task(async function remove_widget() {
  createDummyXULButton(kTestBtn2, "Test");
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should start remove_widget with a non-overflowing toolbar."
  );
  ok(
    CustomizableUI.inDefaultState,
    "Should start remove_widget in default state."
  );
  CustomizableUI.addWidgetToArea(kTestBtn2, navbar.id);
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should still have a non-overflowing toolbar."
  );

  originalWindowWidth = ensureToolbarOverflow(window, false);
  await TestUtils.waitForCondition(() => navbar.hasAttribute("overflowing"));
  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
  ok(
    !navbar.querySelector("#" + kTestBtn2),
    "Test button should not be in the navbar"
  );
  ok(
    overflowList.querySelector("#" + kTestBtn2),
    "Test button should be overflowing"
  );

  CustomizableUI.removeWidgetFromArea(kTestBtn2);

  ok(
    !overflowList.querySelector("#" + kTestBtn2),
    "Test button should not be overflowing."
  );
  ok(
    !navbar.querySelector("#" + kTestBtn2),
    "Test button should not be in the navbar"
  );
  ok(
    gNavToolbox.palette.querySelector("#" + kTestBtn2),
    "Test button should be in the palette"
  );

  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should not have an overflowing toolbar."
  );
  let el = document.getElementById(kTestBtn2);
  if (el) {
    CustomizableUI.removeWidgetFromArea(kTestBtn2);
    el.remove();
  }
  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
});

// Constructing a widget while overflown should set the right class on it.
add_task(async function construct_widget() {
  let sidebarRevampEnabled = Services.prefs.getBoolPref(
    "sidebar.revamp",
    false
  );
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should start construct_widget with a non-overflowing toolbar."
  );
  ok(
    CustomizableUI.inDefaultState,
    "Should start construct_widget in default state."
  );

  CustomizableUI.addWidgetToArea(
    !sidebarRevampEnabled ? kSidebarBtn : kTestBtn2,
    "nav-bar"
  );
  await waitForElementShown(
    document.getElementById(!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2)
  );

  originalWindowWidth = ensureToolbarOverflow(window, false);
  await TestUtils.waitForCondition(() => {
    return (
      navbar.hasAttribute("overflowing") &&
      document
        .getElementById(!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2)
        .getAttribute("overflowedItem") == "true"
    );
  });
  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
  ok(
    !navbar.querySelector(
      `#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`
    ),
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should no longer be in the navbar`
  );
  let overflowBtnNode = overflowList.querySelector(
    `#${!sidebarRevampEnabled ? kSidebarBtn : kTestBtn2}`
  );
  ok(
    overflowBtnNode,
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should be overflowing`
  );
  ok(
    overflowBtnNode && overflowBtnNode.getAttribute("overflowedItem") == "true",
    `${
      !sidebarRevampEnabled ? "Sidebar" : "Test 2"
    } button should have overflowedItem class`
  );

  let testBtnSpec = {
    id: kTestBtn3,
    label: "Overflowable widget test",
    defaultArea: "nav-bar",
  };
  CustomizableUI.createWidget(testBtnSpec);
  let testNode = overflowList.querySelector("#" + kTestBtn3);
  ok(testNode, "Test button should be overflowing");
  ok(
    testNode && testNode.getAttribute("overflowedItem") == "true",
    "Test button should have overflowedItem class"
  );

  CustomizableUI.destroyWidget(kTestBtn3);
  testNode = document.getElementById(kTestBtn3);
  ok(!testNode, "Test button should be gone");

  CustomizableUI.createWidget(testBtnSpec);
  testNode = overflowList.querySelector("#" + kTestBtn3);
  ok(testNode, "Test button should be overflowing");
  ok(
    testNode && testNode.getAttribute("overflowedItem") == "true",
    "Test button should have overflowedItem class"
  );

  CustomizableUI.removeWidgetFromArea(kTestBtn3);
  testNode = document.getElementById(kTestBtn3);
  ok(!testNode, "Test button should be gone");
  CustomizableUI.destroyWidget(kTestBtn3);
  CustomizableUI.removeWidgetFromArea(
    !sidebarRevampEnabled ? kSidebarBtn : kTestBtn2
  );
  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
});

add_task(async function insertBeforeFirstItemInOverflow() {
  originalWindowWidth = window.outerWidth;
  ok(
    !navbar.hasAttribute("overflowing"),
    "Should start insertBeforeFirstItemInOverflow with a non-overflowing toolbar."
  );
  ok(
    CustomizableUI.inDefaultState,
    "Should start insertBeforeFirstItemInOverflow in default state."
  );

  // Remove sidebar button if present
  CustomizableUI.removeWidgetFromArea("sidebar-button");

  CustomizableUI.addWidgetToArea(
    "fullscreen-button",
    "nav-bar",
    CustomizableUI.getWidgetIdsInArea("nav-bar").indexOf(
      "fxa-toolbar-menu-button"
    )
  );
  let fullscreenButton = document.getElementById("fullscreen-button");
  await waitForElementShown(fullscreenButton);

  CustomizableUI.addWidgetToArea(
    kLibraryButton,
    "nav-bar",
    CustomizableUI.getWidgetIdsInArea("nav-bar").indexOf("fullscreen-button")
  );
  let libraryButton = document.getElementById(kLibraryButton);
  await waitForElementShown(libraryButton);
  // Ensure nothing flexes to make the resize predictable:
  navbar
    .querySelectorAll("toolbarspring")
    .forEach(s => CustomizableUI.removeWidgetFromArea(s.id));
  let urlbar = document.getElementById("urlbar-container");
  urlbar.style.minWidth = urlbar.getBoundingClientRect().width + "px";
  // Negative number to make the window smaller by the difference between the left side of
  // the item next to the library button and left side of the hamburger one.
  // The width of the overflow button that needs to appear will then be enough to
  // also hide the library button.
  let resizeWidthToMakeLibraryLast =
    libraryButton.nextElementSibling.getBoundingClientRect().left -
    PanelUI.menuButton.parentNode.getBoundingClientRect().left +
    10; // Leave some margin for the margins between buttons etc.;
  info(
    "Resizing to " +
      resizeWidthToMakeLibraryLast +
      " , waiting for library to overflow."
  );
  window.resizeBy(resizeWidthToMakeLibraryLast, 0);
  await TestUtils.waitForCondition(() => {
    return (
      libraryButton.getAttribute("overflowedItem") == "true" &&
      !libraryButton.previousElementSibling
    );
  }, "Library button is overflowed");

  let testBtnSpec = { id: kTestBtn4, label: "Overflowable widget test" };
  let placementOfLibraryButton = CustomizableUI.getWidgetIdsInArea(
    navbar.id
  ).indexOf(kLibraryButton);
  CustomizableUI.createWidget(testBtnSpec);
  CustomizableUI.addWidgetToArea(
    kTestBtn4,
    "nav-bar",
    placementOfLibraryButton
  );
  let testNode = overflowList.querySelector("#" + kTestBtn4);
  ok(testNode, "Test button should be overflowing");
  ok(
    testNode && testNode.getAttribute("overflowedItem") == "true",
    "Test button should have overflowedItem class"
  );
  CustomizableUI.destroyWidget(kTestBtn4);
  testNode = document.getElementById(kTestBtn4);
  ok(!testNode, "Test button should be gone");

  createDummyXULButton(kTestBtn5, "Test");
  CustomizableUI.addWidgetToArea(
    kTestBtn5,
    "nav-bar",
    placementOfLibraryButton
  );
  testNode = overflowList.querySelector("#" + kTestBtn5);
  ok(testNode, "Test button should be overflowing");
  ok(
    testNode && testNode.getAttribute("overflowedItem") == "true",
    "Test button should have overflowedItem class"
  );
  CustomizableUI.removeWidgetFromArea(kTestBtn5);
  testNode && testNode.remove();

  urlbar.style.removeProperty("min-width");
  CustomizableUI.removeWidgetFromArea(kLibraryButton);
  unensureToolbarOverflow(window, originalWindowWidth);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
  await resetCustomization();
});

registerCleanupFunction(async function asyncCleanup() {
  document.getElementById("urlbar-container").style.removeProperty("min-width");
  window.resizeTo(originalWindowWidth, window.outerHeight);
  await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
  await resetCustomization();
});
