/**
 * This test checks that editing tags doesn't scroll the tags selector
 * listbox to wrong positions.
 */

const TEST_URL = "about:buildconfig";

function scrolledIntoView(item, parentItem) {
  let itemRect = item.getBoundingClientRect();
  let parentItemRect = parentItem.getBoundingClientRect();
  let pointInView = y => parentItemRect.top < y && y < parentItemRect.bottom;

  // Partially visible items are also considered visible.
  return pointInView(itemRect.top) || pointInView(itemRect.bottom);
}

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["test.wait300msAfterTabSwitch", true]],
  });
});

add_task(async function runTest() {
  await PlacesUtils.bookmarks.eraseEverything();
  let tags = [
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g",
    "h",
    "i",
    "l",
    "m",
    "n",
    "o",
    "p",
  ];

  // Add a bookmark and tag it.
  let uri1 = Services.io.newURI(TEST_URL);
  let bm1 = await PlacesUtils.bookmarks.insert({
    parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    title: "mozilla",
    url: uri1.spec,
  });
  PlacesUtils.tagging.tagURI(uri1, tags);

  // Add a second bookmark so that tags won't disappear when unchecked.
  let uri2 = Services.io.newURI("https://www2.mozilla.org/");
  let bm2 = await PlacesUtils.bookmarks.insert({
    parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    title: "mozilla",
    url: uri2.spec,
  });
  PlacesUtils.tagging.tagURI(uri2, tags);

  let win = await BrowserTestUtils.openNewBrowserWindow();
  await BrowserTestUtils.openNewForegroundTab({
    gBrowser: win.gBrowser,
    opening: TEST_URL,
    waitForStateStop: true,
  });

  registerCleanupFunction(async () => {
    bookmarkPanel.removeAttribute("animate");
    await BrowserTestUtils.closeWindow(win);
    await PlacesUtils.bookmarks.eraseEverything();
  });

  win.StarUI._createPanelIfNeeded();
  let bookmarkPanel = win.document.getElementById("editBookmarkPanel");
  bookmarkPanel.setAttribute("animate", false);
  let shownPromise = promisePopupShown(bookmarkPanel);

  let bookmarkStar = win.BookmarkingUI.star;
  bookmarkStar.click();

  await shownPromise;

  // Init panel.
  ok(win.gEditItemOverlay, "gEditItemOverlay is in context");
  ok(win.gEditItemOverlay.initialized, "gEditItemOverlay is initialized");

  await openTagSelector(win);
  let tagsSelector = win.document.getElementById("editBMPanel_tagsSelector");

  // Go by two so there is some untouched tag in the middle.
  for (let i = 8; i < tags.length; i += 2) {
    tagsSelector.selectedIndex = i;
    let listItem = tagsSelector.selectedItem;
    isnot(listItem, null, "Valid listItem found");

    tagsSelector.ensureElementIsVisible(listItem);
    let scrollTop = tagsSelector.scrollTop;

    ok(listItem.hasAttribute("checked"), "Item is checked " + i);
    let selectedTag = listItem.label;

    // Uncheck the tag.
    let promise = BrowserTestUtils.waitForEvent(
      tagsSelector,
      "BookmarkTagsSelectorUpdated"
    );
    EventUtils.synthesizeMouseAtCenter(listItem.firstElementChild, {}, win);
    await promise;
    is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");

    // The listbox is rebuilt, so we have to get the new element.
    let newItem = tagsSelector.selectedItem;
    isnot(newItem, null, "Valid new listItem found");
    ok(!newItem.hasAttribute("checked"), "New listItem is unchecked " + i);
    is(newItem.label, selectedTag, "Correct tag is still selected");

    // Check the tag.
    promise = BrowserTestUtils.waitForEvent(
      tagsSelector,
      "BookmarkTagsSelectorUpdated"
    );
    EventUtils.synthesizeMouseAtCenter(newItem.firstElementChild, {}, win);
    await promise;
    is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");
  }

  // Remove the second bookmark, then nuke some of the tags.
  await PlacesUtils.bookmarks.remove(bm2);

  // Allow the tag updates to complete
  await PlacesTestUtils.promiseAsyncUpdates();

  // Doing this backwords tests more interesting paths.
  for (let i = tags.length - 1; i >= 0; i -= 2) {
    tagsSelector.selectedIndex = i;
    let listItem = tagsSelector.selectedItem;
    isnot(listItem, null, "Valid listItem found");

    tagsSelector.ensureElementIsVisible(listItem);

    ok(listItem.hasAttribute("checked"), "Item is checked " + i);

    // Uncheck the tag.
    let promise = BrowserTestUtils.waitForEvent(
      tagsSelector,
      "BookmarkTagsSelectorUpdated"
    );
    EventUtils.synthesizeMouseAtCenter(listItem.firstElementChild, {}, win);
    await promise;
  }

  let hiddenPromise = promisePopupHidden(bookmarkPanel);
  let doneButton = win.document.getElementById("editBookmarkPanelDoneButton");
  doneButton.click();
  await hiddenPromise;
  // Cleanup.
  await PlacesUtils.bookmarks.remove(bm1);
});

function openTagSelector(win) {
  let promise = BrowserTestUtils.waitForEvent(
    win.document.getElementById("editBMPanel_tagsSelector"),
    "BookmarkTagsSelectorUpdated"
  );
  // Open the tags selector.
  win.document.getElementById("editBMPanel_tagsSelectorExpander").doCommand();
  return promise;
}
