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

"use strict";

gReduceMotionOverride = true;

function enterCustomizationMode(win = window) {
  let customizationReadyPromise = BrowserTestUtils.waitForEvent(
    win.gNavToolbox,
    "customizationready"
  );
  win.gCustomizeMode.enter();
  return customizationReadyPromise;
}

function leaveCustomizationMode(win = window) {
  let customizationDonePromise = BrowserTestUtils.waitForEvent(
    win.gNavToolbox,
    "aftercustomization"
  );
  win.gCustomizeMode.exit();
  return customizationDonePromise;
}

Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
registerCleanupFunction(() => {
  CustomizableUI.reset();
  Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
});

// Stolen from browser/components/customizableui/tests/browser/head.js
function simulateItemDrag(aToDrag, aTarget, aEvent = {}, aOffset = 2) {
  let ev = aEvent;
  if (ev == "end" || ev == "start") {
    let win = aTarget.ownerGlobal;
    const dwu = win.windowUtils;
    let bounds = dwu.getBoundsWithoutFlushing(aTarget);
    if (ev == "end") {
      ev = {
        clientX: bounds.right - aOffset,
        clientY: bounds.bottom - aOffset,
      };
    } else {
      ev = { clientX: bounds.left + aOffset, clientY: bounds.top + aOffset };
    }
  }
  ev._domDispatchOnly = true;
  EventUtils.synthesizeDrop(
    aToDrag.parentNode,
    aTarget,
    null,
    null,
    aToDrag.ownerGlobal,
    aTarget.ownerGlobal,
    ev
  );
  // Ensure dnd suppression is cleared.
  EventUtils.synthesizeMouseAtCenter(
    aTarget,
    { type: "mouseup" },
    aTarget.ownerGlobal
  );
}

function organizeToolbars(state = {}) {
  // Set up the defaults for the state.
  let targetState = Object.assign(
    {
      // Areas where widgets can be placed, set to an array of widget IDs.
      "toolbar-menubar": undefined,
      PersonalToolbar: undefined,
      TabsToolbar: ["tabbrowser-tabs", "alltabs-button"],
      "widget-overflow-fixed-list": undefined,
      "nav-bar": ["back-button", "forward-button", "urlbar-container"],

      // The page action's that should be in the URL bar.
      pageActionsInUrlBar: [],

      // Areas to show or hide.
      titlebarVisible: false,
      menubarVisible: false,
      personalToolbarVisible: false,
    },
    state
  );

  for (let area of CustomizableUI.areas) {
    // Clear out anything there already.
    for (let widgetId of CustomizableUI.getWidgetIdsInArea(area)) {
      CustomizableUI.removeWidgetFromArea(widgetId);
    }

    if (targetState[area]) {
      // We specify the position explicitly to support the toolbars that have
      // fixed widgets.
      let position = 0;
      for (let widgetId of targetState[area]) {
        CustomizableUI.addWidgetToArea(widgetId, area, position++);
      }
    }
  }

  CustomizableUI.setToolbarVisibility(
    "toolbar-menubar",
    targetState.menubarVisible
  );
  CustomizableUI.setToolbarVisibility(
    "PersonalToolbar",
    targetState.personalToolbarVisible
  );

  Services.prefs.setIntPref(
    "browser.tabs.inTitlebar",
    !targetState.titlebarVisible
  );

  for (let action of PageActions.actions) {
    action.pinnedToUrlbar = targetState.pageActionsInUrlBar.includes(action.id);
  }

  // Clear out the existing telemetry.
  Services.telemetry.getSnapshotForKeyedScalars("main", true);
}

function assertVisibilityScalars(expected) {
  let scalars =
    Services.telemetry.getSnapshotForKeyedScalars("main", true)?.parent?.[
      "browser.ui.toolbar_widgets"
    ] ?? {};

  // Only some platforms have the menubar items.
  if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
    expected.push("menubar-items_pinned_menu-bar");
  }

  let keys = new Set(expected.concat(Object.keys(scalars)));
  for (let key of keys) {
    Assert.ok(expected.includes(key), `Scalar key ${key} was unexpected.`);
    Assert.ok(scalars[key], `Expected to see see scalar key ${key} be true.`);
  }
}

function assertCustomizeScalars(expected) {
  let scalars =
    Services.telemetry.getSnapshotForKeyedScalars("main", true)?.parent?.[
      "browser.ui.customized_widgets"
    ] ?? {};

  let keys = new Set(Object.keys(expected).concat(Object.keys(scalars)));
  for (let key of keys) {
    Assert.equal(
      scalars[key],
      expected[key],
      `Expected to see the correct value for scalar ${key}.`
    );
  }
}

add_task(async function widgetPositions() {
  organizeToolbars();

  BrowserUsageTelemetry._recordUITelemetry();

  assertVisibilityScalars([
    "menu-toolbar_pinned_off",
    "titlebar_pinned_off",
    "bookmarks-bar_pinned_off",

    "tabbrowser-tabs_pinned_tabs-bar",
    "alltabs-button_pinned_tabs-bar",
    "unified-extensions-button_pinned_nav-bar-end",

    "forward-button_pinned_nav-bar-start",
    "back-button_pinned_nav-bar-start",
  ]);

  organizeToolbars({
    PersonalToolbar: [
      "fxa-toolbar-menu-button",
      "new-tab-button",
      "developer-button",
    ],

    TabsToolbar: [
      "stop-reload-button",
      "tabbrowser-tabs",
      "personal-bookmarks",
    ],

    "nav-bar": [
      "home-button",
      "forward-button",
      "downloads-button",
      "urlbar-container",
      "back-button",
      "library-button",
    ],

    personalToolbarVisible: true,
  });

  BrowserUsageTelemetry._recordUITelemetry();

  assertVisibilityScalars([
    "menu-toolbar_pinned_off",
    "titlebar_pinned_off",
    "bookmarks-bar_pinned_on",

    "tabbrowser-tabs_pinned_tabs-bar",
    "stop-reload-button_pinned_tabs-bar",
    "personal-bookmarks_pinned_tabs-bar",
    "alltabs-button_pinned_tabs-bar",

    "home-button_pinned_nav-bar-start",
    "forward-button_pinned_nav-bar-start",
    "downloads-button_pinned_nav-bar-start",
    "back-button_pinned_nav-bar-end",
    "library-button_pinned_nav-bar-end",
    "unified-extensions-button_pinned_nav-bar-end",

    "fxa-toolbar-menu-button_pinned_bookmarks-bar",
    "new-tab-button_pinned_bookmarks-bar",
    "developer-button_pinned_bookmarks-bar",
  ]);

  CustomizableUI.reset();
});

add_task(async function customizeMode() {
  // Create a default state.
  organizeToolbars({
    PersonalToolbar: ["personal-bookmarks"],

    TabsToolbar: ["tabbrowser-tabs", "new-tab-button"],

    "nav-bar": [
      "back-button",
      "forward-button",
      "stop-reload-button",
      "urlbar-container",
      "home-button",
      "library-button",
    ],
  });

  BrowserUsageTelemetry._recordUITelemetry();

  assertVisibilityScalars([
    "menu-toolbar_pinned_off",
    "titlebar_pinned_off",
    "bookmarks-bar_pinned_off",

    "tabbrowser-tabs_pinned_tabs-bar",
    "new-tab-button_pinned_tabs-bar",
    "alltabs-button_pinned_tabs-bar",

    "back-button_pinned_nav-bar-start",
    "forward-button_pinned_nav-bar-start",
    "stop-reload-button_pinned_nav-bar-start",
    "home-button_pinned_nav-bar-end",
    "library-button_pinned_nav-bar-end",
    "unified-extensions-button_pinned_nav-bar-end",

    "personal-bookmarks_pinned_bookmarks-bar",
  ]);

  let win = await BrowserTestUtils.openNewBrowserWindow();

  await enterCustomizationMode(win);

  let toolbarButton = win.document.getElementById(
    "customization-toolbar-visibility-button"
  );
  let toolbarPopup = win.document.getElementById("customization-toolbar-menu");
  let popupShown = BrowserTestUtils.waitForEvent(toolbarPopup, "popupshown");
  EventUtils.synthesizeMouseAtCenter(toolbarButton, {}, win);
  await popupShown;

  let barMenu = win.document.getElementById("toggle_PersonalToolbar");
  let popupHidden = BrowserTestUtils.waitForEvent(toolbarPopup, "popuphidden");
  let subMenu = barMenu.querySelector("menupopup");
  popupShown = BrowserTestUtils.waitForEvent(subMenu, "popupshown");
  EventUtils.synthesizeMouseAtCenter(barMenu, {}, win);
  await popupShown;
  let alwaysButton = barMenu.querySelector('*[data-visibility-enum="always"]');
  EventUtils.synthesizeMouseAtCenter(alwaysButton, {}, win);
  await popupHidden;

  let navbar = CustomizableUI.getCustomizationTarget(
    win.document.getElementById("nav-bar")
  );
  let bookmarksBar = CustomizableUI.getCustomizationTarget(
    win.document.getElementById("PersonalToolbar")
  );
  let tabBar = CustomizableUI.getCustomizationTarget(
    win.document.getElementById("TabsToolbar")
  );

  simulateItemDrag(win.document.getElementById("home-button"), navbar, "start");
  simulateItemDrag(win.document.getElementById("library-button"), bookmarksBar);
  simulateItemDrag(win.document.getElementById("stop-reload-button"), tabBar);
  simulateItemDrag(
    win.document.getElementById("stop-reload-button"),
    navbar,
    "start"
  );
  simulateItemDrag(win.document.getElementById("stop-reload-button"), tabBar);

  await leaveCustomizationMode(win);

  await BrowserTestUtils.closeWindow(win);

  assertCustomizeScalars({
    "home-button_move_nav-bar-end_nav-bar-start_drag": 1,
    "library-button_move_nav-bar-end_bookmarks-bar_drag": 1,
    "stop-reload-button_move_nav-bar-start_tabs-bar_drag": 2,
    "stop-reload-button_move_tabs-bar_nav-bar-start_drag": 1,
    "bookmarks-bar_move_off_always_customization-toolbar-menu": 1,
  });

  CustomizableUI.reset();
});

add_task(async function contextMenus() {
  // Create a default state.
  organizeToolbars({
    PersonalToolbar: ["personal-bookmarks"],

    TabsToolbar: ["tabbrowser-tabs", "new-tab-button"],

    "nav-bar": [
      "back-button",
      "forward-button",
      "stop-reload-button",
      "urlbar-container",
      "home-button",
      "library-button",
    ],
  });

  BrowserUsageTelemetry._recordUITelemetry();

  assertVisibilityScalars([
    "menu-toolbar_pinned_off",
    "titlebar_pinned_off",
    "bookmarks-bar_pinned_off",

    "tabbrowser-tabs_pinned_tabs-bar",
    "new-tab-button_pinned_tabs-bar",
    "alltabs-button_pinned_tabs-bar",

    "back-button_pinned_nav-bar-start",
    "forward-button_pinned_nav-bar-start",
    "stop-reload-button_pinned_nav-bar-start",
    "home-button_pinned_nav-bar-end",
    "library-button_pinned_nav-bar-end",
    "unified-extensions-button_pinned_nav-bar-end",

    "personal-bookmarks_pinned_bookmarks-bar",
  ]);

  let menu = document.getElementById("toolbar-context-menu");
  let popupShown = BrowserTestUtils.waitForEvent(menu, "popupshown");
  let button = document.getElementById("stop-reload-button");
  EventUtils.synthesizeMouseAtCenter(
    button,
    { type: "contextmenu", button: 2 },
    window
  );
  await popupShown;

  let barMenu = document.getElementById("toggle_PersonalToolbar");
  let popupHidden = BrowserTestUtils.waitForEvent(menu, "popuphidden");
  let subMenu = barMenu.querySelector("menupopup");
  popupShown = BrowserTestUtils.waitForEvent(subMenu, "popupshown");
  barMenu.openMenu(true);
  await popupShown;
  let alwaysButton = subMenu.querySelector('*[data-visibility-enum="always"]');
  subMenu.activateItem(alwaysButton);
  await popupHidden;

  popupShown = BrowserTestUtils.waitForEvent(menu, "popupshown");
  EventUtils.synthesizeMouseAtCenter(
    button,
    { type: "contextmenu", button: 2 },
    window
  );
  await popupShown;

  popupHidden = BrowserTestUtils.waitForEvent(menu, "popuphidden");
  let removeButton = document.querySelector(
    "#toolbar-context-menu .customize-context-removeFromToolbar"
  );
  menu.activateItem(removeButton);
  await popupHidden;

  assertCustomizeScalars({
    "bookmarks-bar_move_off_always_toolbar-context-menu": 1,
    "stop-reload-button_remove_nav-bar-start_na_toolbar-context-menu": 1,
  });

  CustomizableUI.reset();
});

add_task(async function extensions() {
  // The page action button is only visible when a page is loaded.
  await BrowserTestUtils.withNewTab("http://example.com", async () => {
    organizeToolbars();

    const extension = ExtensionTestUtils.loadExtension({
      useAddonManager: "temporary",
      manifest: {
        version: "1",
        browser_specific_settings: {
          gecko: { id: "random_addon@example.com" },
        },
        browser_action: {
          default_icon: "default.png",
          default_title: "Hello",
          default_area: "navbar",
        },
        page_action: {
          default_icon: "default.png",
          default_title: "Hello",
        },
      },
    });

    await extension.startup();

    assertCustomizeScalars({
      "random-addon-example-com_add_na_nav-bar-end_addon": 1,
      "random-addon-example-com_add_na_pageaction-urlbar_addon": 1,
    });

    BrowserUsageTelemetry._recordUITelemetry();

    assertVisibilityScalars([
      "menu-toolbar_pinned_off",
      "titlebar_pinned_off",
      "bookmarks-bar_pinned_off",

      "tabbrowser-tabs_pinned_tabs-bar",
      "alltabs-button_pinned_tabs-bar",

      "forward-button_pinned_nav-bar-start",
      "back-button_pinned_nav-bar-start",

      "random-addon-example-com_pinned_nav-bar-end",
      "unified-extensions-button_pinned_nav-bar-end",

      "random-addon-example-com_pinned_pageaction-urlbar",
    ]);

    let addon = await AddonManager.getAddonByID(extension.id);
    await addon.disable();

    assertCustomizeScalars({
      "random-addon-example-com_remove_nav-bar-end_na_addon": 1,
      "random-addon-example-com_remove_pageaction-urlbar_na_addon": 1,
    });

    BrowserUsageTelemetry._recordUITelemetry();

    assertVisibilityScalars([
      "menu-toolbar_pinned_off",
      "titlebar_pinned_off",
      "bookmarks-bar_pinned_off",

      "tabbrowser-tabs_pinned_tabs-bar",
      "alltabs-button_pinned_tabs-bar",

      "forward-button_pinned_nav-bar-start",
      "back-button_pinned_nav-bar-start",
      "unified-extensions-button_pinned_nav-bar-end",
    ]);

    await addon.enable();

    assertCustomizeScalars({
      "random-addon-example-com_add_na_nav-bar-end_addon": 1,
      "random-addon-example-com_add_na_pageaction-urlbar_addon": 1,
    });

    BrowserUsageTelemetry._recordUITelemetry();

    assertVisibilityScalars([
      "menu-toolbar_pinned_off",
      "titlebar_pinned_off",
      "bookmarks-bar_pinned_off",

      "tabbrowser-tabs_pinned_tabs-bar",
      "alltabs-button_pinned_tabs-bar",

      "forward-button_pinned_nav-bar-start",
      "back-button_pinned_nav-bar-start",

      "random-addon-example-com_pinned_nav-bar-end",
      "unified-extensions-button_pinned_nav-bar-end",

      "random-addon-example-com_pinned_pageaction-urlbar",
    ]);

    await addon.reload();

    assertCustomizeScalars({});

    await enterCustomizationMode();

    let navbar = CustomizableUI.getCustomizationTarget(
      document.getElementById("nav-bar")
    );

    simulateItemDrag(
      document.getElementById("random_addon_example_com-browser-action"),
      navbar,
      "start"
    );

    await leaveCustomizationMode();

    assertCustomizeScalars({
      "random-addon-example-com_move_nav-bar-end_nav-bar-start_drag": 1,
    });

    await extension.unload();

    assertCustomizeScalars({
      "random-addon-example-com_remove_nav-bar-start_na_addon": 1,
      "random-addon-example-com_remove_pageaction-urlbar_na_addon": 1,
    });

    BrowserUsageTelemetry._recordUITelemetry();

    assertVisibilityScalars([
      "menu-toolbar_pinned_off",
      "titlebar_pinned_off",
      "bookmarks-bar_pinned_off",

      "tabbrowser-tabs_pinned_tabs-bar",
      "alltabs-button_pinned_tabs-bar",

      "forward-button_pinned_nav-bar-start",
      "back-button_pinned_nav-bar-start",
      "unified-extensions-button_pinned_nav-bar-end",
    ]);
  });
});
