Bug 880382 - When customizing, dragging wide widgets in the panel should cause panel to break by rows. r=mconley

This commit is contained in:
Jared Wein 2013-06-20 19:13:01 -04:00
parent 39d09e14c8
commit 05d6de1060
5 changed files with 609 additions and 73 deletions

View File

@ -15,6 +15,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
const kWidePanelItemClass = "panel-combined-item";
let gModuleName = "[CustomizableWidgets]";
#include logging.js
@ -32,6 +33,39 @@ function setAttributes(aNode, aAttrs) {
}
}
// This function is called whenever an item gets moved in the menu panel. It
// adjusts the position of widgets within the panel to reduce single-column
// buttons from being placed in a row by themselves.
function adjustPosition(aNode) {
// TODO(bug 885574): Merge this constant with the one in CustomizeMode.jsm,
// maybe just use a pref for this.
const kColumnsInMenuPanel = 3;
// Make sure that there are n % columns = 0 narrow buttons before the widget.
let prevSibling = aNode.previousElementSibling;
let previousSiblingCount = 0;
while (prevSibling) {
if (!prevSibling.classList.contains(kWidePanelItemClass)) {
previousSiblingCount++;
}
prevSibling = prevSibling.previousElementSibling;
}
if (previousSiblingCount % kColumnsInMenuPanel) {
let previousElement = aNode.previousElementSibling;
if (!previousElement ||
previousElement.classList.contains(kWidePanelItemClass)) {
return;
}
let position = Array.prototype.indexOf.call(aNode.parentNode.children, aNode);
// We don't need to move all of the items in this pass, because
// this move will trigger adjustPosition to get called again. The
// function will stop recursing when it finds that there is no
// more work that is needed.
CustomizableUI.moveWidgetWithinArea(aNode.id, position - 1);
}
}
const CustomizableWidgets = [{
id: "history-panelmenu",
type: "view",
@ -232,6 +266,7 @@ const CustomizableWidgets = [{
if (inPanel)
node.setAttribute("flex", "1");
node.classList.add("chromeclass-toolbar-additional");
node.classList.add(kWidePanelItemClass);
buttons.forEach(function(aButton) {
let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
@ -275,6 +310,10 @@ const CustomizableWidgets = [{
let listener = {
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
@ -282,6 +321,10 @@ const CustomizableWidgets = [{
}.bind(this),
onWidgetRemoved: function(aWidgetId, aPrevArea) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
@ -298,6 +341,10 @@ const CustomizableWidgets = [{
}.bind(this),
onWidgetMoved: function(aWidgetId, aArea) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
updateWidgetStyle(aArea == CustomizableUI.AREA_PANEL);
@ -357,6 +404,7 @@ const CustomizableWidgets = [{
if (inPanel)
node.setAttribute("flex", "1");
node.classList.add("chromeclass-toolbar-additional");
node.classList.add(kWidePanelItemClass);
buttons.forEach(function(aButton) {
let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
@ -380,12 +428,20 @@ const CustomizableWidgets = [{
let listener = {
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
updateWidgetStyle(aArea == CustomizableUI.AREA_PANEL);
}.bind(this),
onWidgetRemoved: function(aWidgetId, aPrevArea) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
// When a widget is demoted to the palette ('removed'), it's visual
@ -400,6 +456,10 @@ const CustomizableWidgets = [{
}.bind(this),
onWidgetMoved: function(aWidgetId, aArea) {
if (this.currentArea == CustomizableUI.AREA_PANEL) {
adjustPosition(node);
}
if (aWidgetId != this.id)
return;
updateWidgetStyle(aArea == CustomizableUI.AREA_PANEL);

View File

@ -13,6 +13,9 @@ const kPaletteId = "customization-palette";
const kAboutURI = "about:customizing";
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
const kPlaceholderClass = "panel-customization-placeholder";
// TODO(bug 885574): Merge this constant with the one in CustomizableWidgets.jsm,
// maybe just use a pref for this.
const kColumnsInMenuPanel = 3;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/CustomizableUI.jsm");
@ -449,6 +452,9 @@ CustomizeMode.prototype = {
aWrapper.removeEventListener("mouseup", this);
let toolbarItem = aWrapper.firstChild;
if (!toolbarItem) {
ERROR("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
}
if (aWrapper.hasAttribute("itemchecked")) {
toolbarItem.checked = true;
@ -493,8 +499,35 @@ CustomizeMode.prototype = {
}.bind(this));
},
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
_wrapToolbarItemsSync: function() {
let window = this.window;
// Add drag-and-drop event handlers to all of the customizable areas.
this.areas = [];
for (let area of CustomizableUI.areas) {
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
target.addEventListener("dragstart", this, true);
target.addEventListener("dragover", this, true);
target.addEventListener("dragexit", this, true);
target.addEventListener("drop", this, true);
target.addEventListener("dragend", this, true);
for (let child of target.children) {
if (this.isCustomizableItem(child)) {
this.wrapToolbarItem(child, getPlaceForItem(child));
}
}
this.areas.push(target);
}
},
_unwrapToolbarItems: function() {
return Task.spawn(function() {
this._unwrapToolbarItemsSync();
}.bind(this));
},
// TODO(bug 885575): Merge into _unwrapToolbarItems.
_unwrapToolbarItemsSync: function() {
for (let target of this.areas) {
for (let toolbarItem of target.children) {
if (this.isWrappedToolbarItem(toolbarItem)) {
@ -507,7 +540,6 @@ CustomizeMode.prototype = {
target.removeEventListener("drop", this, true);
target.removeEventListener("dragend", this, true);
}
}.bind(this));
},
persistCurrentSets: function() {
@ -719,115 +751,122 @@ CustomizeMode.prototype = {
_onDragDrop: function(aEvent) {
__dumpDragData(aEvent);
this._setDragActive(this._dragOverItem, false);
this._removePanelCustomizationPlaceholders();
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
let document = aEvent.target.ownerDocument;
let documentId = document.documentElement.id;
let {id: draggedItemId} =
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
draggedWrapper.hidden = false;
draggedWrapper.removeAttribute("mousedown");
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
let originArea = this._getCustomizableParent(draggedWrapper);
// Do nothing if the target area or origin area are not customizable.
if (!targetArea || !originArea) {
return;
}
let targetNode = this._getDragOverNode(aEvent.target, targetArea);
if (targetNode.tagName == "toolbarpaletteitem") {
targetNode = targetNode.firstChild;
}
this._setDragActive(this._dragOverItem, false);
this._removePanelCustomizationPlaceholders();
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
this._unwrapToolbarItemsSync();
let paletteChild = this.visiblePalette.firstChild;
let nextChild;
while (paletteChild) {
nextChild = paletteChild.nextElementSibling;
this.unwrapToolbarItem(paletteChild);
paletteChild = nextChild;
}
try {
this._applyDrop(aEvent, targetArea, originArea, draggedItemId, targetNode);
} catch (ex) {
ERROR(ex, ex.stack);
}
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
this._wrapToolbarItemsSync();
// Re-wrap palette items.
let paletteChild = this.visiblePalette.firstChild;
let nextChild;
while (paletteChild) {
nextChild = paletteChild.nextElementSibling;
this.wrapToolbarItem(paletteChild, "palette");
paletteChild = nextChild;
}
this._showPanelCustomizationPlaceholders();
},
_applyDrop: function(aEvent, aTargetArea, aOriginArea, aDraggedItemId, aTargetNode) {
let document = aEvent.target.ownerDocument;
let draggedItem = document.getElementById(aDraggedItemId);
draggedItem.hidden = false;
draggedItem.removeAttribute("mousedown");
// Do nothing if the target was dropped onto itself (ie, no change in area
// or position).
if (draggedWrapper == targetNode) {
if (draggedItem == aTargetNode) {
return;
}
// Is the target area the customization palette? If so, we have two cases -
// either the originArea was the palette, or a customizable area.
if (targetArea.id == kPaletteId) {
if (originArea.id !== kPaletteId) {
if (!CustomizableUI.isWidgetRemovable(draggedItemId)) {
// either the origin area was the palette, or a customizable area.
if (aTargetArea.id == kPaletteId) {
if (aOriginArea.id !== kPaletteId) {
if (!CustomizableUI.isWidgetRemovable(aDraggedItemId)) {
return;
}
let widget = this.unwrapToolbarItem(draggedWrapper);
CustomizableUI.removeWidgetFromArea(draggedItemId);
draggedWrapper = this.wrapToolbarItem(widget, "palette");
CustomizableUI.removeWidgetFromArea(aDraggedItemId);
}
// If the targetNode is the palette itself, just append
if (targetNode == this.visiblePalette) {
this.visiblePalette.appendChild(draggedWrapper);
// If the target node is the palette itself, just append
if (aTargetNode == this.visiblePalette) {
this.visiblePalette.appendChild(draggedItem);
} else {
this.visiblePalette.insertBefore(draggedWrapper, targetNode);
this.visiblePalette.insertBefore(draggedItem, aTargetNode);
}
this._showPanelCustomizationPlaceholders();
return;
}
if (!CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
if (!CustomizableUI.canWidgetMoveToArea(aDraggedItemId, aTargetArea.id)) {
return;
}
// Is the target the customization area itself? If so, we just add the
// widget to the end of the area.
if (targetNode == targetArea.customizationTarget) {
let widget = this.unwrapToolbarItem(draggedWrapper);
CustomizableUI.addWidgetToArea(draggedItemId, targetArea.id);
this.wrapToolbarItem(widget, getPlaceForItem(targetNode));
this._showPanelCustomizationPlaceholders();
if (aTargetNode == aTargetArea.customizationTarget) {
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id);
return;
}
// We need to determine the place that the widget is being dropped in
// the target.
let placement;
if (!targetNode.classList.contains(kPlaceholderClass)) {
let targetNodeId = (targetNode.nodeName == "toolbarpaletteitem") ?
targetNode.firstChild && targetNode.firstChild.id :
targetNode.id;
if (!aTargetNode.classList.contains(kPlaceholderClass)) {
let targetNodeId = (aTargetNode.nodeName == "toolbarpaletteitem") ?
aTargetNode.firstChild && aTargetNode.firstChild.id :
aTargetNode.id;
placement = CustomizableUI.getPlacementOfWidget(targetNodeId);
}
if (!placement) {
LOG("Could not get a position for " + targetNode + "#" + targetNode.id + "." + targetNode.className);
LOG("Could not get a position for " + aTargetNode + "#" + aTargetNode.id + "." + aTargetNode.className);
}
let position = placement ? placement.position :
targetArea.childElementCount;
aTargetArea.childElementCount;
// Is the target area the same as the origin? Since we've already handled
// the possibility that the target is the customization palette, we know
// that the widget is moving within a customizable area.
if (targetArea == originArea) {
let properPlace = getPlaceForItem(targetNode);
// We unwrap the moving widget, as well as the widget that we're dropping
// on (the target) so that moveWidgetWithinArea can correctly insert the
// moving widget before the target widget.
let widget = this.unwrapToolbarItem(draggedWrapper);
let targetWidget = this.unwrapToolbarItem(targetNode);
CustomizableUI.moveWidgetWithinArea(draggedItemId, position);
this.wrapToolbarItem(targetWidget, properPlace);
this.wrapToolbarItem(widget, properPlace);
this._showPanelCustomizationPlaceholders();
if (aTargetArea == aOriginArea) {
CustomizableUI.moveWidgetWithinArea(aDraggedItemId, position);
return;
}
// A little hackery - we quickly unwrap the item and use CustomizableUI's
// addWidgetToArea to move the widget to the right place for every window,
// then we re-wrap the widget. We have to unwrap the target widget too so
// that addWidgetToArea inserts the new widget into the right place.
let properPlace = getPlaceForItem(targetNode);
let widget = this.unwrapToolbarItem(draggedWrapper);
let targetWidget = this.unwrapToolbarItem(targetNode);
CustomizableUI.addWidgetToArea(draggedItemId, targetArea.id, position);
this.wrapToolbarItem(targetWidget, properPlace);
draggedWrapper = this.wrapToolbarItem(widget, properPlace);
this._showPanelCustomizationPlaceholders();
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id, position);
},
_onDragExit: function(aEvent) {
@ -975,13 +1014,15 @@ CustomizeMode.prototype = {
},
_showPanelCustomizationPlaceholders: function() {
const kColumns = 3;
this._removePanelCustomizationPlaceholders();
let doc = this.document;
let contents = this.panelUIContents;
let visibleCombinedButtons = contents.querySelectorAll("toolbarpaletteitem:not([hidden]) > .panel-combined-item");
let visibleChildren = contents.querySelectorAll("toolbarpaletteitem:not([hidden])");
let hangingItems = visibleChildren.length % kColumns;
let newPlaceholders = kColumns - hangingItems;
// TODO(bug 885578): Still doesn't handle a hole when there is a wide
// widget located at the bottom of the panel.
let hangingItems = (visibleChildren.length - visibleCombinedButtons.length) % kColumnsInMenuPanel;
let newPlaceholders = kColumnsInMenuPanel - hangingItems;
while (newPlaceholders--) {
let placeholder = doc.createElement("toolbarpaletteitem");
placeholder.classList.add(kPlaceholderClass);

View File

@ -15,6 +15,7 @@ MOCHITEST_BROWSER_FILES = \
browser_877178_unregisterArea.js \
browser_877447_skip_missing_ids.js \
browser_878452_drag_to_panel.js \
browser_880382_drag_wide_widgets_in_panel.js \
head.js \
$(NULL)

View File

@ -0,0 +1,414 @@
/* 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/. */
let gTests = [
{
desc: "Dragging the zoom controls to be before the print button " +
"should not move any controls.",
setup: startCustomizing,
run: function() {
let zoomControls = document.getElementById("zoom-controls");
let printButton = document.getElementById("print-button");
let placementsAfterMove = ["edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"zoom-controls",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(zoomControls, printButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
let newWindowButton = document.getElementById("new-window-button");
simulateItemDrag(zoomControls, newWindowButton);
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
},
teardown: endCustomizing
},
{
desc: "Dragging the zoom controls to be before the save button " +
"should not move any controls.",
setup: startCustomizing,
run: function() {
let zoomControls = document.getElementById("zoom-controls");
let savePageButton = document.getElementById("save-page-button");
let placementsAfterMove = ["edit-controls",
"zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(zoomControls, savePageButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(CustomizableUI.inDefaultState, "Should be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the zoom controls to be before the new-window " +
"button should not move any widgets.",
setup: startCustomizing,
run: function() {
let zoomControls = document.getElementById("zoom-controls");
let newWindowButton = document.getElementById("new-window-button");
let placementsAfterMove = ["edit-controls",
"zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(zoomControls, newWindowButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the zoom controls to be before the history-panelmenu " +
"should move the zoom-controls in to the row higher than the " +
"history-panelmenu.",
setup: startCustomizing,
run: function() {
let zoomControls = document.getElementById("zoom-controls");
let historyPanelMenu = document.getElementById("history-panelmenu");
let placementsAfterMove = ["edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"zoom-controls",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(zoomControls, historyPanelMenu);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
let newWindowButton = document.getElementById("new-window-button");
simulateItemDrag(zoomControls, newWindowButton);
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
},
teardown: endCustomizing
},
{
desc: "Dragging the zoom controls to be before the preferences-button " +
"should move the zoom-controls in to the row higher than the " +
"preferences-button.",
setup: startCustomizing,
run: function() {
let zoomControls = document.getElementById("zoom-controls");
let preferencesButton = document.getElementById("preferences-button");
let placementsAfterMove = ["edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"zoom-controls",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(zoomControls, preferencesButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
let newWindowButton = document.getElementById("new-window-button");
simulateItemDrag(zoomControls, newWindowButton);
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
},
teardown: endCustomizing
},
{
desc: "Dragging an item from the palette to before the zoom-controls " +
"should move it and two other buttons before the zoom controls.",
setup: startCustomizing,
run: function() {
let developerButton = document.getElementById("developer-button");
let zoomControls = document.getElementById("zoom-controls");
let expectedPlacementsAfterInsert = ["edit-controls",
"developer-button",
"new-window-button",
"privatebrowsing-button",
"zoom-controls",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(developerButton, zoomControls);
// Currently, the developer-button is placed after the zoom-controls, but it should be
// placed like expectedPlacementsAfterInsert describes.
todoAssertAreaPlacements(CustomizableUI.AREA_PANEL, expectedPlacementsAfterInsert);
let actualPlacementsAfterInsert = ["edit-controls",
"zoom-controls",
"developer-button",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
assertAreaPlacements(CustomizableUI.AREA_PANEL, actualPlacementsAfterInsert);
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
let palette = document.getElementById("customization-palette");
// Check that the palette items are re-wrapped correctly.
let feedWrapper = document.getElementById("wrapper-feed-button");
let feedButton = document.getElementById("feed-button");
is(feedButton.parentNode, feedWrapper,
"feed-button should be a child of wrapper-feed-button");
is(feedWrapper.getAttribute("place"), "palette",
"The feed-button wrapper should have it's place set to 'palette'");
simulateItemDrag(developerButton, palette);
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
"The developer-button should be wrapped by a toolbarpaletteitem");
let newWindowButton = document.getElementById("new-window-button");
simulateItemDrag(zoomControls, newWindowButton);
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to be before the zoom-controls button " +
"should not move any widgets.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let zoomControls = document.getElementById("zoom-controls");
let placementsAfterMove = ["edit-controls",
"zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(editControls, zoomControls);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to be before the new-window-button should " +
"move the zoom-controls before the edit-controls.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let newWindowButton = document.getElementById("new-window-button");
let placementsAfterMove = ["zoom-controls",
"edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(editControls, newWindowButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to be before the privatebrowsing-button " +
"should move the edit-controls in to the row higher than the " +
"privatebrowsing-button.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let privateBrowsingButton = document.getElementById("privatebrowsing-button");
let placementsAfterMove = ["zoom-controls",
"edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(editControls, privateBrowsingButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to be before the save-page-button " +
"should move the edit-controls in to the row higher than the " +
"save-page-button.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let savePageButton = document.getElementById("save-page-button");
let placementsAfterMove = ["zoom-controls",
"edit-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
simulateItemDrag(editControls, savePageButton);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to the panel itself should append " +
"the edit controls to the bottom of the panel.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
let placementsAfterMove = ["zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button",
"edit-controls"];
simulateItemDrag(editControls, panel);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to the customization-palette and " +
"back should work.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let palette = document.getElementById("customization-palette");
let placementsAfterMove = ["zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button"];
let paletteChildElementCount = palette.childElementCount;
simulateItemDrag(editControls, palette);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
is(paletteChildElementCount + 1, palette.childElementCount,
"The palette should have a new child, congratulations!");
is(editControls.parentNode.id, "wrapper-edit-controls",
"The edit-controls should be properly wrapped.");
is(editControls.parentNode.getAttribute("place"), "palette",
"The edit-controls should have the place of 'palette'.");
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
is(paletteChildElementCount, palette.childElementCount,
"The palette child count should have returned to its prior value.");
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
{
desc: "Dragging the edit-controls to each of the panel placeholders " +
"should append the edit-controls to the bottom of the panel.",
setup: startCustomizing,
run: function() {
let editControls = document.getElementById("edit-controls");
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
for (let i = 0; i < 3; i++) {
// NB: We can't just iterate over all of the placeholders
// because each drag-drop action recreates them.
let placeholder = panel.getElementsByClassName("panel-customization-placeholder")[i];
let placementsAfterMove = ["zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button",
"edit-controls"];
simulateItemDrag(editControls, placeholder);
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
let zoomControls = document.getElementById("zoom-controls");
simulateItemDrag(editControls, zoomControls);
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
}
},
teardown: endCustomizing
},
{
desc: "Dragging the developer-button back on to itself should work.",
setup: startCustomizing,
run: function() {
let developerButton = document.getElementById("developer-button");
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
"developer-button should be wrapped by a toolbarpaletteitem");
simulateItemDrag(developerButton, developerButton);
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
"developer-button should be wrapped by a toolbarpaletteitem");
let editControls = document.getElementById("edit-controls");
is(editControls.parentNode.tagName, "toolbarpaletteitem",
"edit-controls should be wrapped by a toolbarpaletteitem");
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
},
teardown: endCustomizing
},
];
function asyncCleanup() {
yield resetCustomization();
}
function test() {
waitForExplicitFinish();
requestLongerTimeout(5);
runTests(gTests, asyncCleanup);
}

View File

@ -70,6 +70,24 @@ function assertAreaPlacements(areaId, expectedPlacements) {
}
}
function todoAssertAreaPlacements(areaId, expectedPlacements) {
let actualPlacements = getAreaWidgetIds(areaId);
let isPassing = actualPlacements.length == expectedPlacements.length;
let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
for (let i = 0; i < minItems; i++) {
if (typeof expectedPlacements[i] == "string") {
isPassing = isPassing && actualPlacements[i] == expectedPlacements[i];
} else if (expectedPlacements[i] instanceof RegExp) {
isPassing = isPassing && expectedPlacements[i].test(actualPlacements[i]);
} else {
ok(false, "Unknown type of expected placement passed to " +
" assertAreaPlacements. Is your test broken?");
}
}
todo(isPassing, "The area placements for " + areaId +
" should equal the expected placements.")
}
function getAreaWidgetIds(areaId) {
let widgetAry = CustomizableUI.getWidgetsInArea(areaId);
return widgetAry.map(x => x.id);
@ -146,7 +164,9 @@ function testRunner(testAry, asyncCleanup) {
function runTests(testAry, asyncCleanup) {
Task.spawn(testRunner(gTests, asyncCleanup)).then(finish, ex => {
ok(false, "Unexpected exception: " + ex);
// The stack of ok() here is misleading due to Promises. The stack of the
// actual exception is likely much more valuable, hence concatentating it.
ok(false, "Unexpected exception: " + ex + " With stack: " + ex.stack);
finish();
});
}