tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_984455_bookmarks_items_reparenting.js (11623B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 var gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
      8 var gOverflowList = document.getElementById(
      9  gNavBar.getAttribute("default-overflowtarget")
     10 );
     11 
     12 const kBookmarksButton = "bookmarks-menu-button";
     13 const kBookmarksItems = "personal-bookmarks";
     14 const kOriginalWindowWidth = window.outerWidth;
     15 
     16 /**
     17 * Helper function that opens the bookmarks menu, and returns a Promise that
     18 * resolves as soon as the menu is ready for interaction.
     19 */
     20 function bookmarksMenuPanelShown() {
     21  return new Promise(resolve => {
     22    let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
     23    let onPopupShown = e => {
     24      if (e.target == bookmarksMenuPopup) {
     25        bookmarksMenuPopup.removeEventListener("popupshown", onPopupShown);
     26        resolve();
     27      }
     28    };
     29    bookmarksMenuPopup.addEventListener("popupshown", onPopupShown);
     30  });
     31 }
     32 
     33 /**
     34 * Checks that the placesContext menu is correctly attached to the
     35 * controller of some view. Returns a Promise that resolves as soon
     36 * as the context menu is closed.
     37 *
     38 * @param aItemWithContextMenu the item that we need to synthesize the
     39 *        right click on in order to open the context menu.
     40 */
     41 function checkPlacesContextMenu(aItemWithContextMenu) {
     42  return (async function () {
     43    let contextMenu = document.getElementById("placesContext");
     44    let newBookmarkItem = document.getElementById("placesContext_new:bookmark");
     45    info("Waiting for context menu on " + aItemWithContextMenu.id);
     46    let shownPromise = popupShown(contextMenu);
     47    EventUtils.synthesizeMouseAtCenter(aItemWithContextMenu, {
     48      type: "contextmenu",
     49      button: 2,
     50    });
     51    await shownPromise;
     52 
     53    ok(
     54      !newBookmarkItem.hasAttribute("disabled"),
     55      "New bookmark item shouldn't be disabled"
     56    );
     57 
     58    info("Closing context menu");
     59    let hiddenPromise = popupHidden(contextMenu);
     60    // Use hidePopup instead of the closePopup helper because macOS native
     61    // context menus can't be closed by synthesized ESC in automation.
     62    contextMenu.hidePopup();
     63    await hiddenPromise;
     64  })();
     65 }
     66 
     67 /**
     68 * Opens the bookmarks menu panel, and then opens each of the "special"
     69 * submenus in that list. Then it checks that those submenu's context menus
     70 * are properly hooked up to a controller.
     71 */
     72 function checkSpecialContextMenus() {
     73  return (async function () {
     74    let bookmarksMenuButton = document.getElementById(kBookmarksButton);
     75    let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
     76 
     77    const kSpecialItemIDs = {
     78      BMB_bookmarksToolbar: "BMB_bookmarksToolbarPopup",
     79      BMB_unsortedBookmarks: "BMB_unsortedBookmarksPopup",
     80    };
     81 
     82    // Open the bookmarks menu button context menus and ensure that
     83    // they have the proper views attached.
     84    let shownPromise = bookmarksMenuPanelShown();
     85 
     86    EventUtils.synthesizeMouseAtCenter(bookmarksMenuButton, {});
     87    info("Waiting for bookmarks menu popup to show after clicking dropmarker.");
     88    await shownPromise;
     89 
     90    for (let menuID in kSpecialItemIDs) {
     91      let menuItem = document.getElementById(menuID);
     92      let menuPopup = document.getElementById(kSpecialItemIDs[menuID]);
     93      info("Waiting to open menu for " + menuID);
     94      shownPromise = popupShown(menuPopup);
     95      menuPopup.openPopup(menuItem, null, 0, 0, false, false, null);
     96      await shownPromise;
     97 
     98      await checkPlacesContextMenu(menuPopup);
     99      info("Closing menu for " + menuID);
    100      await closePopup(menuPopup);
    101    }
    102 
    103    info("Closing bookmarks menu");
    104    await closePopup(bookmarksMenuPopup);
    105  })();
    106 }
    107 
    108 /**
    109 * Closes a focused popup by simulating pressing the Escape key,
    110 * and returns a Promise that resolves as soon as the popup is closed.
    111 *
    112 * @param aPopup the popup node to close.
    113 */
    114 function closePopup(aPopup) {
    115  let hiddenPromise = popupHidden(aPopup);
    116  EventUtils.synthesizeKey("KEY_Escape");
    117  return hiddenPromise;
    118 }
    119 
    120 /**
    121 * Helper function that checks that the context menu of the
    122 * bookmark toolbar items chevron popup is correctly hooked up
    123 * to the controller of a view.
    124 */
    125 function checkBookmarksItemsChevronContextMenu() {
    126  return (async function () {
    127    let chevronPopup = document.getElementById("PlacesChevronPopup");
    128    let shownPromise = popupShown(chevronPopup);
    129    let chevron = document.getElementById("PlacesChevron");
    130    EventUtils.synthesizeMouseAtCenter(chevron, {});
    131    info("Waiting for bookmark toolbar item chevron popup to show");
    132    await shownPromise;
    133    await TestUtils.waitForCondition(() => {
    134      for (let child of chevronPopup.children) {
    135        if (child.style.visibility != "hidden") {
    136          return true;
    137        }
    138      }
    139      return false;
    140    });
    141    await checkPlacesContextMenu(chevronPopup);
    142    info("Waiting for bookmark toolbar item chevron popup to close");
    143    await closePopup(chevronPopup);
    144  })();
    145 }
    146 
    147 /**
    148 * Forces the window to a width that causes the nav-bar to overflow
    149 * its contents. Returns a Promise that resolves as soon as the
    150 * overflowable nav-bar is showing its chevron.
    151 */
    152 function overflowEverything(win) {
    153  info("Waiting for overflow");
    154  let waitOverflowing = BrowserTestUtils.waitForMutationCondition(
    155    gNavBar,
    156    { attributes: true, attributeFilter: ["overflowing"] },
    157    () => gNavBar.hasAttribute("overflowing")
    158  );
    159  ensureToolbarOverflow(win, false);
    160  return waitOverflowing;
    161 }
    162 
    163 /**
    164 * Returns the window to its original size from the start of the test,
    165 * and returns a Promise that resolves when the nav-bar is no longer
    166 * overflowing.
    167 */
    168 function stopOverflowing(win, originalWindowWidth) {
    169  info("Waiting until we stop overflowing");
    170  let waitOverflowing = BrowserTestUtils.waitForMutationCondition(
    171    gNavBar,
    172    { attributes: true, attributeFilter: ["overflowing"] },
    173    () => !gNavBar.hasAttribute("overflowing")
    174  );
    175  unensureToolbarOverflow(win, originalWindowWidth);
    176  return waitOverflowing;
    177 }
    178 
    179 /**
    180 * Ensure bookmarks are visible on the toolbar.
    181 * @param {DOMWindow} win the browser window
    182 */
    183 async function waitBookmarksToolbarIsUpdated(win = window) {
    184  await TestUtils.waitForCondition(
    185    async () => (await win.PlacesToolbarHelper.getIsEmpty()) === false,
    186    "Waiting for the Bookmarks toolbar to have been rebuilt and not be empty"
    187  );
    188  if (
    189    win.PlacesToolbarHelper._viewElt._placesView._updateNodesVisibilityTimer
    190  ) {
    191    await BrowserTestUtils.waitForEvent(
    192      win,
    193      "BookmarksToolbarVisibilityUpdated"
    194    );
    195  }
    196 }
    197 
    198 /**
    199 * Checks that an item with ID aID is overflowing in the nav-bar.
    200 *
    201 * @param aID the ID of the node to check for overflowingness.
    202 */
    203 function checkOverflowing(aID) {
    204  ok(
    205    !gNavBar.querySelector("#" + aID),
    206    "Item with ID " + aID + " should no longer be in the gNavBar"
    207  );
    208  let item = gOverflowList.querySelector("#" + aID);
    209  ok(item, "Item with ID " + aID + " should be overflowing");
    210  is(
    211    item.getAttribute("overflowedItem"),
    212    "true",
    213    "Item with ID " + aID + " should have overflowedItem attribute"
    214  );
    215 }
    216 
    217 /**
    218 * Checks that an item with ID aID is not overflowing in the nav-bar.
    219 *
    220 * @param aID the ID of hte node to check for non-overflowingness.
    221 */
    222 function checkNotOverflowing(aID) {
    223  ok(
    224    !gOverflowList.querySelector("#" + aID),
    225    "Item with ID " + aID + " should no longer be overflowing"
    226  );
    227  let item = gNavBar.querySelector("#" + aID);
    228  ok(item, "Item with ID " + aID + " should be in the nav bar");
    229  ok(
    230    !item.hasAttribute("overflowedItem"),
    231    "Item with ID " + aID + " should not have overflowedItem attribute"
    232  );
    233 }
    234 
    235 /**
    236 * Test that overflowing the bookmarks menu button doesn't break the
    237 * context menus for the Unsorted and Bookmarks Toolbar menu items.
    238 */
    239 add_task(async function testOverflowingBookmarksButtonContextMenu() {
    240  ok(CustomizableUI.inDefaultState, "Should start in default state.");
    241  // The DevEdition has the DevTools button in the toolbar by default. Remove it
    242  // to prevent branch-specific available toolbar space.
    243  CustomizableUI.removeWidgetFromArea("developer-button");
    244  CustomizableUI.removeWidgetFromArea(
    245    "library-button",
    246    CustomizableUI.AREA_NAVBAR
    247  );
    248  CustomizableUI.addWidgetToArea(kBookmarksButton, CustomizableUI.AREA_NAVBAR);
    249  ok(
    250    !gNavBar.hasAttribute("overflowing"),
    251    "Should start with a non-overflowing toolbar."
    252  );
    253 
    254  // Open the Unsorted and Bookmarks Toolbar context menus and ensure
    255  // that they have views attached.
    256  await checkSpecialContextMenus();
    257 
    258  const originalWindowWidth = window.outerWidth;
    259  await overflowEverything(window);
    260  checkOverflowing(kBookmarksButton);
    261 
    262  await stopOverflowing(window, originalWindowWidth);
    263  checkNotOverflowing(kBookmarksButton);
    264 
    265  await checkSpecialContextMenus();
    266 });
    267 
    268 /**
    269 * Test that the bookmarks toolbar items context menu still works if moved
    270 * to the menu from the overflow panel, and then back to the toolbar.
    271 */
    272 add_task(async function testOverflowingBookmarksItemsContextMenu() {
    273  info("Adding a bookmark to the bookmarks toolbar.");
    274  let addedBookmark = await PlacesUtils.bookmarks.insert({
    275    parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    276    title: "Test",
    277    url: "https://example.com",
    278  });
    279 
    280  registerCleanupFunction(async () => {
    281    await PlacesUtils.bookmarks.remove(addedBookmark);
    282  });
    283 
    284  info("Ensuring panel is ready.");
    285  await PanelUI.ensureReady();
    286 
    287  let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
    288  await gCustomizeMode.addToToolbar(bookmarksToolbarItems);
    289  await waitBookmarksToolbarIsUpdated();
    290  await checkPlacesContextMenu(bookmarksToolbarItems);
    291 
    292  let originalWindowWidth = window.outerWidth;
    293  await overflowEverything(window);
    294  checkOverflowing(kBookmarksItems);
    295 
    296  await gCustomizeMode.addToPanel(bookmarksToolbarItems);
    297 
    298  await stopOverflowing(window, originalWindowWidth);
    299 
    300  await gCustomizeMode.addToToolbar(bookmarksToolbarItems);
    301  await waitBookmarksToolbarIsUpdated();
    302  await checkPlacesContextMenu(bookmarksToolbarItems);
    303 });
    304 
    305 /**
    306 * Test that overflowing the bookmarks toolbar items doesn't cause the
    307 * context menu in the bookmarks toolbar items chevron to stop working.
    308 */
    309 add_task(async function testOverflowingBookmarksItemsChevronContextMenu() {
    310  // If it's not already there, let's move the bookmarks toolbar items to
    311  // the nav-bar.
    312  let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
    313  await gCustomizeMode.addToToolbar(bookmarksToolbarItems);
    314 
    315  // We make the PlacesToolbarItems element be super tiny in order to force
    316  // the bookmarks toolbar items into overflowing and making the chevron
    317  // show itself.
    318  let placesToolbarItems = document.getElementById("PlacesToolbarItems");
    319  let placesChevron = document.getElementById("PlacesChevron");
    320  placesToolbarItems.style.maxWidth = "10px";
    321  info("Waiting for chevron to no longer be collapsed");
    322  await TestUtils.waitForCondition(() => !placesChevron.collapsed);
    323 
    324  await checkBookmarksItemsChevronContextMenu();
    325 
    326  let originalWindowWidth = window.outerWidth;
    327  await overflowEverything(window);
    328  checkOverflowing(kBookmarksItems);
    329 
    330  await stopOverflowing(window, originalWindowWidth);
    331  checkNotOverflowing(kBookmarksItems);
    332 
    333  await waitBookmarksToolbarIsUpdated();
    334  await checkBookmarksItemsChevronContextMenu();
    335 
    336  placesToolbarItems.style.removeProperty("max-width");
    337 });
    338 
    339 add_task(async function asyncCleanup() {
    340  window.resizeTo(kOriginalWindowWidth, window.outerHeight);
    341  await resetCustomization();
    342 });