"use strict";

async function synthesizeMouseAndWait(aBrowser, aEvent) {
  let promise = SpecialPowers.spawn(aBrowser, [aEvent], async event => {
    await new Promise(resolve => {
      content.document.documentElement.addEventListener(event, resolve, {
        once: true,
      });
    });
  });
  // Ensure content has been added event listener.
  await SpecialPowers.spawn(aBrowser, [], () => {});
  EventUtils.synthesizeMouse(aBrowser, 10, 10, { type: aEvent });
  return promise;
}

function AddMouseEventListener(aBrowser) {
  return SpecialPowers.spawn(aBrowser, [], () => {
    content.catchedEvents = [];
    let listener = function (aEvent) {
      content.catchedEvents.push(aEvent.type);
    };

    let target = content.document.querySelector("p");
    target.onmouseenter = listener;
    target.onmouseleave = listener;
  });
}

function clearMouseEventListenerAndCheck(aBrowser, aExpectedEvents) {
  return SpecialPowers.spawn(aBrowser, [aExpectedEvents], events => {
    let target = content.document.querySelector("p");
    target.onmouseenter = null;
    target.onmouseleave = null;

    Assert.deepEqual(content.catchedEvents, events);
  });
}

add_task(async function testSwitchTabs() {
  const tabFirst = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "http://example.com/browser/browser/base/content/test/general/dummy_page.html",
    true
  );

  info("Initial mouse move");
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabFirst.linkedBrowser,
    10,
    10
  );

  info("Open and move to a new tab");
  await AddMouseEventListener(tabFirst.linkedBrowser);
  const tabSecond = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "http://example.com/browser/browser/base/content/test/general/dummy_page.html"
  );
  // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
  // events.
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabSecond.linkedBrowser,
    10,
    10
  );
  // Wait a bit to see if there is any unexpected mouse event.
  await TestUtils.waitForTick();
  await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseleave"]);

  info("switch back to the previous tab");
  await AddMouseEventListener(tabFirst.linkedBrowser);
  await AddMouseEventListener(tabSecond.linkedBrowser);
  await BrowserTestUtils.switchTab(gBrowser, tabFirst);
  // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
  // events.
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabFirst.linkedBrowser,
    10,
    10
  );
  // Wait a bit to see if there is any unexpected mouse event.
  await TestUtils.waitForTick();
  await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseenter"]);
  await clearMouseEventListenerAndCheck(tabSecond.linkedBrowser, [
    "mouseleave",
  ]);

  info("Close tabs");
  BrowserTestUtils.removeTab(tabFirst);
  BrowserTestUtils.removeTab(tabSecond);
});

add_task(async function testSwitchTabsWithMouseDown() {
  const tabFirst = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "http://example.com/browser/browser/base/content/test/general/dummy_page.html",
    true
  );

  info("Initial mouse move");
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabFirst.linkedBrowser,
    10,
    10
  );

  info("mouse down");
  await synthesizeMouseAndWait(tabFirst.linkedBrowser, "mousedown");

  info("Open and move to a new tab");
  await AddMouseEventListener(tabFirst.linkedBrowser);
  const tabSecond = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "http://example.com/browser/browser/base/content/test/general/dummy_page.html"
  );
  // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
  // events.
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabSecond.linkedBrowser,
    10,
    10
  );

  info("mouse up");
  await synthesizeMouseAndWait(tabSecond.linkedBrowser, "mouseup");
  // Wait a bit to see if there is any unexpected mouse event.
  await TestUtils.waitForTick();
  await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseleave"]);

  info("mouse down");
  await synthesizeMouseAndWait(tabSecond.linkedBrowser, "mousedown");

  info("switch back to the previous tab");
  await AddMouseEventListener(tabFirst.linkedBrowser);
  await AddMouseEventListener(tabSecond.linkedBrowser);
  await BrowserTestUtils.switchTab(gBrowser, tabFirst);
  // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
  // events.
  await EventUtils.synthesizeAndWaitNativeMouseMove(
    tabFirst.linkedBrowser,
    10,
    10
  );

  info("mouse up");
  await synthesizeMouseAndWait(tabFirst.linkedBrowser, "mouseup");
  // Wait a bit to see if there is any unexpected mouse event.
  await TestUtils.waitForTick();
  await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseenter"]);
  await clearMouseEventListenerAndCheck(tabSecond.linkedBrowser, [
    "mouseleave",
  ]);

  info("Close tabs");
  BrowserTestUtils.removeTab(tabFirst);
  BrowserTestUtils.removeTab(tabSecond);
});