tor-browser

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

browser_876926_customize_mode_wrapping.js (9986B)


      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 add_setup(async function () {
      8  await SpecialPowers.pushPrefEnv({
      9    set: [["test.wait300msAfterTabSwitch", true]],
     10  });
     11 });
     12 
     13 const kXULWidgetId = "a-test-button"; // we'll create a button with this ID.
     14 const kAPIWidgetId = "save-page-button";
     15 const kPanel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL;
     16 const kToolbar = CustomizableUI.AREA_NAVBAR;
     17 const kVisiblePalette = "customization-palette";
     18 
     19 function checkWrapper(id) {
     20  is(
     21    document.querySelectorAll("#wrapper-" + id).length,
     22    1,
     23    "There should be exactly 1 wrapper for " +
     24      id +
     25      " in the customizing window."
     26  );
     27 }
     28 
     29 async function ensureVisible(node) {
     30  let isInPalette = node.parentNode.parentNode == gNavToolbox.palette;
     31  if (isInPalette) {
     32    node.scrollIntoView();
     33  }
     34  let dwu = window.windowUtils;
     35  await BrowserTestUtils.waitForCondition(() => {
     36    let nodeBounds = dwu.getBoundsWithoutFlushing(node);
     37    if (isInPalette) {
     38      let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette);
     39      if (
     40        !(
     41          nodeBounds.top >= paletteBounds.top &&
     42          nodeBounds.bottom <= paletteBounds.bottom
     43        )
     44      ) {
     45        return false;
     46      }
     47    }
     48    return nodeBounds.height && nodeBounds.width;
     49  });
     50 }
     51 
     52 var move = {
     53  async drag(id, target) {
     54    let targetNode = document.getElementById(target);
     55    if (CustomizableUI.getCustomizationTarget(targetNode)) {
     56      targetNode = CustomizableUI.getCustomizationTarget(targetNode);
     57    }
     58    let nodeToMove = document.getElementById(id);
     59    await ensureVisible(nodeToMove);
     60 
     61    simulateItemDrag(nodeToMove, targetNode, "end");
     62  },
     63  async dragToItem(id, target) {
     64    let targetNode = document.getElementById(target);
     65    if (CustomizableUI.getCustomizationTarget(targetNode)) {
     66      targetNode = CustomizableUI.getCustomizationTarget(targetNode);
     67    }
     68    let items = targetNode.querySelectorAll("toolbarpaletteitem");
     69    if (target == kPanel) {
     70      targetNode = items[items.length - 1];
     71    } else {
     72      targetNode = items[0];
     73    }
     74    let nodeToMove = document.getElementById(id);
     75    await ensureVisible(nodeToMove);
     76    simulateItemDrag(nodeToMove, targetNode, "start");
     77  },
     78  API(id, target) {
     79    if (target == kVisiblePalette) {
     80      return CustomizableUI.removeWidgetFromArea(id);
     81    }
     82    return CustomizableUI.addWidgetToArea(id, target, null);
     83  },
     84 };
     85 
     86 function isLast(containerId, defaultPlacements, id) {
     87  assertAreaPlacements(containerId, defaultPlacements.concat([id]));
     88  let thisTarget = CustomizableUI.getCustomizationTarget(
     89    document.getElementById(containerId)
     90  );
     91  is(
     92    thisTarget.lastElementChild.firstElementChild.id,
     93    id,
     94    "Widget " + id + " should be in " + containerId + " in customizing window."
     95  );
     96  let otherTarget = CustomizableUI.getCustomizationTarget(
     97    otherWin.document.getElementById(containerId)
     98  );
     99  is(
    100    otherTarget.lastElementChild.id,
    101    id,
    102    "Widget " + id + " should be in " + containerId + " in other window."
    103  );
    104 }
    105 
    106 function getLastVisibleNodeInToolbar(containerId, win = window) {
    107  let container = CustomizableUI.getCustomizationTarget(
    108    win.document.getElementById(containerId)
    109  );
    110  let rv = container.lastElementChild;
    111  while (rv?.hidden || rv?.firstElementChild?.hidden) {
    112    rv = rv.previousElementSibling;
    113  }
    114  return rv;
    115 }
    116 
    117 function isLastVisibleInToolbar(containerId, defaultPlacements, id) {
    118  let newPlacements;
    119  for (let i = defaultPlacements.length - 1; i >= 0; i--) {
    120    let el = document.getElementById(defaultPlacements[i]);
    121    if (el && !el.hidden) {
    122      newPlacements = [...defaultPlacements];
    123      newPlacements.splice(i + 1, 0, id);
    124      break;
    125    }
    126  }
    127  if (!newPlacements) {
    128    assertAreaPlacements(containerId, defaultPlacements.concat([id]));
    129  } else {
    130    assertAreaPlacements(containerId, newPlacements);
    131  }
    132  is(
    133    getLastVisibleNodeInToolbar(containerId).firstElementChild.id,
    134    id,
    135    "Widget " + id + " should be in " + containerId + " in customizing window."
    136  );
    137  is(
    138    getLastVisibleNodeInToolbar(containerId, otherWin).id,
    139    id,
    140    "Widget " + id + " should be in " + containerId + " in other window."
    141  );
    142 }
    143 
    144 function isFirst(containerId, defaultPlacements, id) {
    145  assertAreaPlacements(containerId, [id].concat(defaultPlacements));
    146  let thisTarget = CustomizableUI.getCustomizationTarget(
    147    document.getElementById(containerId)
    148  );
    149  is(
    150    thisTarget.firstElementChild.firstElementChild.id,
    151    id,
    152    "Widget " + id + " should be in " + containerId + " in customizing window."
    153  );
    154  let otherTarget = CustomizableUI.getCustomizationTarget(
    155    otherWin.document.getElementById(containerId)
    156  );
    157  is(
    158    otherTarget.firstElementChild.id,
    159    id,
    160    "Widget " + id + " should be in " + containerId + " in other window."
    161  );
    162 }
    163 
    164 async function checkToolbar(id, method) {
    165  // Place at start of the toolbar:
    166  let toolbarPlacements = getAreaWidgetIds(kToolbar);
    167  await move[method](id, kToolbar);
    168  if (method == "dragToItem") {
    169    isFirst(kToolbar, toolbarPlacements, id);
    170  } else if (method == "drag") {
    171    isLastVisibleInToolbar(kToolbar, toolbarPlacements, id);
    172  } else {
    173    isLast(kToolbar, toolbarPlacements, id);
    174  }
    175  checkWrapper(id);
    176 }
    177 
    178 async function checkPanel(id, method) {
    179  let panelPlacements = getAreaWidgetIds(kPanel);
    180  await move[method](id, kPanel);
    181  let children = document
    182    .getElementById(kPanel)
    183    .querySelectorAll("toolbarpaletteitem");
    184  let otherChildren = otherWin.document.getElementById(kPanel).children;
    185  let newPlacements = panelPlacements.concat([id]);
    186  // Relative position of the new item from the end:
    187  let position = -1;
    188  // For the drag to item case, we drag to the last item, making the dragged item the
    189  // penultimate item. We can't well use the first item because the panel has complicated
    190  // rules about rearranging wide items (which, by default, the first two items are).
    191  if (method == "dragToItem") {
    192    newPlacements.pop();
    193    newPlacements.splice(panelPlacements.length - 1, 0, id);
    194    position = -2;
    195  }
    196  assertAreaPlacements(kPanel, newPlacements);
    197  is(
    198    children[children.length + position].firstElementChild.id,
    199    id,
    200    "Widget " + id + " should be in " + kPanel + " in customizing window."
    201  );
    202  is(
    203    otherChildren[otherChildren.length + position].id,
    204    id,
    205    "Widget " + id + " should be in " + kPanel + " in other window."
    206  );
    207  checkWrapper(id);
    208 }
    209 
    210 async function checkPalette(id, method) {
    211  // Move back to palette:
    212  await move[method](id, kVisiblePalette);
    213  ok(CustomizableUI.inDefaultState, "Should end in default state");
    214  let visibleChildren = gCustomizeMode.visiblePalette.children;
    215  let expectedChild =
    216    method == "dragToItem"
    217      ? visibleChildren[0]
    218      : visibleChildren[visibleChildren.length - 1];
    219  // Items dragged to the end of the palette should be the final item. That they're the penultimate
    220  // item when dragged is tracked in bug 1395950. Once that's fixed, this hack can be removed.
    221  if (method == "drag") {
    222    expectedChild = expectedChild.previousElementSibling;
    223  }
    224  is(
    225    expectedChild.firstElementChild.id,
    226    id,
    227    "Widget " +
    228      id +
    229      " was moved using " +
    230      method +
    231      " and should now be wrapped in palette in customizing window."
    232  );
    233  if (id == kXULWidgetId) {
    234    ok(
    235      otherWin.gNavToolbox.palette.querySelector("#" + id),
    236      "Widget " + id + " should be in invisible palette in other window."
    237    );
    238  }
    239  checkWrapper(id);
    240 }
    241 
    242 // This test needs a XUL button that's in the palette by default. No such
    243 // button currently exists, so we create a simple one.
    244 function createXULButtonForWindow(win) {
    245  createDummyXULButton(kXULWidgetId, "test-button", win);
    246 }
    247 
    248 function removeXULButtonForWindow(win) {
    249  win.gNavToolbox.palette.querySelector(`#${kXULWidgetId}`).remove();
    250 }
    251 
    252 var otherWin;
    253 
    254 // Moving widgets in two windows, one with customize mode and one without, should work.
    255 add_task(async function MoveWidgetsInTwoWindows() {
    256  CustomizableUI.createWidget({
    257    id: "cui-mode-wrapping-some-panel-item",
    258    label: "Test panel wrapping",
    259  });
    260  await startCustomizing();
    261  otherWin = await openAndLoadWindow(null, true);
    262  await otherWin.PanelUI.ensureReady();
    263  // Create the XUL button to use in the test in both windows.
    264  createXULButtonForWindow(window);
    265  createXULButtonForWindow(otherWin);
    266  ok(CustomizableUI.inDefaultState, "Should start in default state");
    267 
    268  for (let widgetId of [kXULWidgetId, kAPIWidgetId]) {
    269    for (let method of ["API", "drag", "dragToItem"]) {
    270      info("Moving widget " + widgetId + " using " + method);
    271      await checkToolbar(widgetId, method);
    272      // We add an item to the panel because otherwise we can't test dragging
    273      // to items that are already there. We remove it because
    274      // 'checkPalette' checks that we leave the browser in the default state.
    275      CustomizableUI.addWidgetToArea(
    276        "cui-mode-wrapping-some-panel-item",
    277        CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
    278      );
    279      await checkPanel(widgetId, method);
    280      CustomizableUI.removeWidgetFromArea("cui-mode-wrapping-some-panel-item");
    281      await checkPalette(widgetId, method);
    282      CustomizableUI.addWidgetToArea(
    283        "cui-mode-wrapping-some-panel-item",
    284        CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
    285      );
    286      await checkPanel(widgetId, method);
    287      await checkToolbar(widgetId, method);
    288      CustomizableUI.removeWidgetFromArea("cui-mode-wrapping-some-panel-item");
    289      await checkPalette(widgetId, method);
    290    }
    291  }
    292  await promiseWindowClosed(otherWin);
    293  otherWin = null;
    294  await endCustomizing();
    295  removeXULButtonForWindow(window);
    296 });
    297 
    298 add_task(async function asyncCleanup() {
    299  CustomizableUI.destroyWidget("cui-mode-wrapping-some-panel-item");
    300  await resetCustomization();
    301 });