/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

async function openContextMenuInOptionsPage(optionsBrowser) {
  let contentAreaContextMenu = document.getElementById(
    "contentAreaContextMenu"
  );
  let popupShownPromise = BrowserTestUtils.waitForEvent(
    contentAreaContextMenu,
    "popupshown"
  );

  info("Trigger context menu in the extension options page");

  // Instead of BrowserTestUtils.synthesizeMouseAtCenter, we are dispatching a contextmenu
  // event directly on the target element to prevent intermittent failures on debug builds
  // (especially linux32-debug), see Bug 1519808 for a rationale.
  SpecialPowers.spawn(optionsBrowser, [], () => {
    let el = content.document.querySelector("a");
    el.dispatchEvent(
      new content.MouseEvent("contextmenu", {
        bubbles: true,
        cancelable: true,
        view: el.ownerGlobal,
      })
    );
  });

  info("Wait the context menu to be shown");
  await popupShownPromise;

  return contentAreaContextMenu;
}

async function contextMenuClosed(contextMenu) {
  info("Wait context menu popup to be closed");
  await closeContextMenu(contextMenu);
  is(contextMenu.state, "closed", "The context menu popup has been closed");
}

add_task(async function test_tab_options_popups() {
  async function backgroundScript() {
    browser.menus.onShown.addListener(info => {
      browser.test.sendMessage("extension-menus-onShown", info);
    });

    await browser.menus.create({
      id: "sidebaronly",
      title: "sidebaronly",
      viewTypes: ["sidebar"],
    });
    await browser.menus.create({
      id: "tabonly",
      title: "tabonly",
      viewTypes: ["tab"],
    });
    await browser.menus.create({ id: "anypage", title: "anypage" });

    browser.runtime.openOptionsPage();
  }

  function optionsScript() {
    browser.test.sendMessage("options-page:loaded", document.documentURI);
  }

  let extension = ExtensionTestUtils.loadExtension({
    useAddonManager: "temporary",

    manifest: {
      permissions: ["tabs", "menus"],
      options_ui: {
        page: "options.html",
      },
    },
    files: {
      "options.html": `<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <script src="options.js" type="text/javascript"></script>
          </head>
          <body style="height: 100px;">
            <h1>Extensions Options</h1>
            <a href="http://mochi.test:8888/">options page link</a>
          </body>
        </html>`,
      "options.js": optionsScript,
    },
    background: backgroundScript,
  });

  const aboutAddonsTab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "about:addons"
  );

  await extension.startup();

  const pageUrl = await extension.awaitMessage("options-page:loaded");

  const optionsBrowser = getInlineOptionsBrowser(gBrowser.selectedBrowser);

  const contentAreaContextMenu = await openContextMenuInOptionsPage(
    optionsBrowser
  );

  let contextMenuItemIds = [
    "context-openlinkintab",
    "context-openlinkprivate",
    "context-copylink",
  ];

  // Test that the "open link in container" menu is available if the containers are enabled
  // (which is the default on Nightly, but not on Beta).
  if (Services.prefs.getBoolPref("privacy.userContext.enabled")) {
    contextMenuItemIds.push("context-openlinkinusercontext-menu");
  }

  for (const itemID of contextMenuItemIds) {
    const item = contentAreaContextMenu.querySelector(`#${itemID}`);

    ok(!item.hidden, `${itemID} should not be hidden`);
    ok(!item.disabled, `${itemID} should not be disabled`);
  }

  const menuDetails = await extension.awaitMessage("extension-menus-onShown");

  isnot(
    menuDetails.targetElementId,
    undefined,
    "Got a targetElementId in the menu details"
  );
  delete menuDetails.targetElementId;

  Assert.deepEqual(
    menuDetails,
    {
      menuIds: ["anypage"],
      contexts: ["link", "all"],
      viewType: undefined,
      frameId: 0,
      editable: false,
      linkText: "options page link",
      linkUrl: "http://mochi.test:8888/",
      pageUrl,
    },
    "Got the expected menu details from menus.onShown"
  );

  await contextMenuClosed(contentAreaContextMenu);

  BrowserTestUtils.removeTab(aboutAddonsTab);

  await extension.unload();
});

add_task(async function overrideContext_in_options_page() {
  function optionsScript() {
    document.addEventListener(
      "contextmenu",
      () => {
        browser.menus.overrideContext({});
        browser.test.sendMessage("contextmenu-overridden");
      },
      { once: true }
    );
    browser.test.sendMessage("options-page:loaded");
  }

  let extension = ExtensionTestUtils.loadExtension({
    useAddonManager: "temporary",
    manifest: {
      permissions: ["tabs", "menus", "menus.overrideContext"],
      options_ui: {
        page: "options.html",
      },
    },
    files: {
      "options.html": `<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <script src="options.js" type="text/javascript"></script>
          </head>
          <body style="height: 100px;">
            <h1>Extensions Options</h1>
            <a href="http://mochi.test:8888/">options page link</a>
          </body>
        </html>`,
      "options.js": optionsScript,
    },
    async background() {
      // Expected to match and be shown.
      await new Promise(resolve => {
        browser.menus.create({ id: "bg_1_1", title: "bg_1_1" });
        browser.menus.create({ id: "bg_1_2", title: "bg_1_2" });
        // Expected to not match and be hidden.
        browser.menus.create(
          {
            id: "bg_1_3",
            title: "bg_1_3",
            targetUrlPatterns: ["*://nomatch/*"],
          },
          // menus.create returns a number and gets a callback, the order
          // is deterministic and so we just need to wait for the last one.
          resolve
        );
      });
      browser.runtime.openOptionsPage();
    },
  });

  const aboutAddonsTab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "about:addons"
  );

  await extension.startup();
  await extension.awaitMessage("options-page:loaded");

  const optionsBrowser = getInlineOptionsBrowser(gBrowser.selectedBrowser);
  const contentAreaContextMenu = await openContextMenuInOptionsPage(
    optionsBrowser
  );

  await extension.awaitMessage("contextmenu-overridden");

  const allVisibleMenuItems = Array.from(contentAreaContextMenu.children)
    .filter(elem => {
      return !elem.hidden;
    })
    .map(elem => elem.id);

  Assert.deepEqual(
    allVisibleMenuItems,
    [
      `${makeWidgetId(extension.id)}-menuitem-_bg_1_1`,
      `${makeWidgetId(extension.id)}-menuitem-_bg_1_2`,
    ],
    "Expected only extension menu items"
  );

  await contextMenuClosed(contentAreaContextMenu);

  BrowserTestUtils.removeTab(aboutAddonsTab);

  await extension.unload();
});
