/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const kXULWidgetId = "a-test-button"; // we'll create a button with this ID. const kAPIWidgetId = "save-page-button"; const kPanel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL; const kToolbar = CustomizableUI.AREA_NAVBAR; const kVisiblePalette = "customization-palette"; function checkWrapper(id) { is( document.querySelectorAll("#wrapper-" + id).length, 1, "There should be exactly 1 wrapper for " + id + " in the customizing window." ); } async function ensureVisible(node) { let isInPalette = node.parentNode.parentNode == gNavToolbox.palette; if (isInPalette) { node.scrollIntoView(); } let dwu = window.windowUtils; await BrowserTestUtils.waitForCondition(() => { let nodeBounds = dwu.getBoundsWithoutFlushing(node); if (isInPalette) { let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette); if ( !( nodeBounds.top >= paletteBounds.top && nodeBounds.bottom <= paletteBounds.bottom ) ) { return false; } } return nodeBounds.height && nodeBounds.width; }); } var move = { async drag(id, target) { let targetNode = document.getElementById(target); if (CustomizableUI.getCustomizationTarget(targetNode)) { targetNode = CustomizableUI.getCustomizationTarget(targetNode); } let nodeToMove = document.getElementById(id); await ensureVisible(nodeToMove); simulateItemDrag(nodeToMove, targetNode, "end"); }, async dragToItem(id, target) { let targetNode = document.getElementById(target); if (CustomizableUI.getCustomizationTarget(targetNode)) { targetNode = CustomizableUI.getCustomizationTarget(targetNode); } let items = targetNode.querySelectorAll("toolbarpaletteitem"); if (target == kPanel) { targetNode = items[items.length - 1]; } else { targetNode = items[0]; } let nodeToMove = document.getElementById(id); await ensureVisible(nodeToMove); simulateItemDrag(nodeToMove, targetNode, "start"); }, API(id, target) { if (target == kVisiblePalette) { return CustomizableUI.removeWidgetFromArea(id); } return CustomizableUI.addWidgetToArea(id, target, null); }, }; function isLast(containerId, defaultPlacements, id) { assertAreaPlacements(containerId, defaultPlacements.concat([id])); let thisTarget = CustomizableUI.getCustomizationTarget( document.getElementById(containerId) ); is( thisTarget.lastElementChild.firstElementChild.id, id, "Widget " + id + " should be in " + containerId + " in customizing window." ); let otherTarget = CustomizableUI.getCustomizationTarget( otherWin.document.getElementById(containerId) ); is( otherTarget.lastElementChild.id, id, "Widget " + id + " should be in " + containerId + " in other window." ); } function getLastVisibleNodeInToolbar(containerId, win = window) { let container = CustomizableUI.getCustomizationTarget( win.document.getElementById(containerId) ); let rv = container.lastElementChild; while (rv?.hidden || rv?.firstElementChild?.hidden) { rv = rv.previousElementSibling; } return rv; } function isLastVisibleInToolbar(containerId, defaultPlacements, id) { let newPlacements; for (let i = defaultPlacements.length - 1; i >= 0; i--) { let el = document.getElementById(defaultPlacements[i]); if (el && !el.hidden) { newPlacements = [...defaultPlacements]; newPlacements.splice(i + 1, 0, id); break; } } if (!newPlacements) { assertAreaPlacements(containerId, defaultPlacements.concat([id])); } else { assertAreaPlacements(containerId, newPlacements); } is( getLastVisibleNodeInToolbar(containerId).firstElementChild.id, id, "Widget " + id + " should be in " + containerId + " in customizing window." ); is( getLastVisibleNodeInToolbar(containerId, otherWin).id, id, "Widget " + id + " should be in " + containerId + " in other window." ); } function isFirst(containerId, defaultPlacements, id) { assertAreaPlacements(containerId, [id].concat(defaultPlacements)); let thisTarget = CustomizableUI.getCustomizationTarget( document.getElementById(containerId) ); is( thisTarget.firstElementChild.firstElementChild.id, id, "Widget " + id + " should be in " + containerId + " in customizing window." ); let otherTarget = CustomizableUI.getCustomizationTarget( otherWin.document.getElementById(containerId) ); is( otherTarget.firstElementChild.id, id, "Widget " + id + " should be in " + containerId + " in other window." ); } async function checkToolbar(id, method) { // Place at start of the toolbar: let toolbarPlacements = getAreaWidgetIds(kToolbar); await move[method](id, kToolbar); if (method == "dragToItem") { isFirst(kToolbar, toolbarPlacements, id); } else if (method == "drag") { isLastVisibleInToolbar(kToolbar, toolbarPlacements, id); } else { isLast(kToolbar, toolbarPlacements, id); } checkWrapper(id); } async function checkPanel(id, method) { let panelPlacements = getAreaWidgetIds(kPanel); await move[method](id, kPanel); let children = document .getElementById(kPanel) .querySelectorAll("toolbarpaletteitem"); let otherChildren = otherWin.document.getElementById(kPanel).children; let newPlacements = panelPlacements.concat([id]); // Relative position of the new item from the end: let position = -1; // For the drag to item case, we drag to the last item, making the dragged item the // penultimate item. We can't well use the first item because the panel has complicated // rules about rearranging wide items (which, by default, the first two items are). if (method == "dragToItem") { newPlacements.pop(); newPlacements.splice(panelPlacements.length - 1, 0, id); position = -2; } assertAreaPlacements(kPanel, newPlacements); is( children[children.length + position].firstElementChild.id, id, "Widget " + id + " should be in " + kPanel + " in customizing window." ); is( otherChildren[otherChildren.length + position].id, id, "Widget " + id + " should be in " + kPanel + " in other window." ); checkWrapper(id); } async function checkPalette(id, method) { // Move back to palette: await move[method](id, kVisiblePalette); ok(CustomizableUI.inDefaultState, "Should end in default state"); let visibleChildren = gCustomizeMode.visiblePalette.children; let expectedChild = method == "dragToItem" ? visibleChildren[0] : visibleChildren[visibleChildren.length - 1]; // Items dragged to the end of the palette should be the final item. That they're the penultimate // item when dragged is tracked in bug 1395950. Once that's fixed, this hack can be removed. if (method == "drag") { expectedChild = expectedChild.previousElementSibling; } is( expectedChild.firstElementChild.id, id, "Widget " + id + " was moved using " + method + " and should now be wrapped in palette in customizing window." ); if (id == kXULWidgetId) { ok( otherWin.gNavToolbox.palette.querySelector("#" + id), "Widget " + id + " should be in invisible palette in other window." ); } checkWrapper(id); } // This test needs a XUL button that's in the palette by default. No such // button currently exists, so we create a simple one. function createXULButtonForWindow(win) { createDummyXULButton(kXULWidgetId, "test-button", win); } function removeXULButtonForWindow(win) { win.gNavToolbox.palette.querySelector(`#${kXULWidgetId}`).remove(); } var otherWin; // Moving widgets in two windows, one with customize mode and one without, should work. add_task(async function MoveWidgetsInTwoWindows() { CustomizableUI.createWidget({ id: "cui-mode-wrapping-some-panel-item", label: "Test panel wrapping", }); await startCustomizing(); otherWin = await openAndLoadWindow(null, true); await otherWin.PanelUI.ensureReady(); // Create the XUL button to use in the test in both windows. createXULButtonForWindow(window); createXULButtonForWindow(otherWin); ok(CustomizableUI.inDefaultState, "Should start in default state"); for (let widgetId of [kXULWidgetId, kAPIWidgetId]) { for (let method of ["API", "drag", "dragToItem"]) { info("Moving widget " + widgetId + " using " + method); await checkToolbar(widgetId, method); // We add an item to the panel because otherwise we can't test dragging // to items that are already there. We remove it because // 'checkPalette' checks that we leave the browser in the default state. CustomizableUI.addWidgetToArea( "cui-mode-wrapping-some-panel-item", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL ); await checkPanel(widgetId, method); CustomizableUI.removeWidgetFromArea("cui-mode-wrapping-some-panel-item"); await checkPalette(widgetId, method); CustomizableUI.addWidgetToArea( "cui-mode-wrapping-some-panel-item", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL ); await checkPanel(widgetId, method); await checkToolbar(widgetId, method); CustomizableUI.removeWidgetFromArea("cui-mode-wrapping-some-panel-item"); await checkPalette(widgetId, method); } } await promiseWindowClosed(otherWin); otherWin = null; await endCustomizing(); removeXULButtonForWindow(window); }); add_task(async function asyncCleanup() { CustomizableUI.destroyWidget("cui-mode-wrapping-some-panel-item"); await resetCustomization(); });