Bug 976792 - [australis] CustomizableUI's insertNodeInWindow should go through placements and try followup next nodes. r=Gijs r=jaws
authorBlair McBride <bmcbride@mozilla.com>
Fri, 07 Mar 2014 04:59:00 -0500
changeset 190662 8f6765a920929e8b4849a79dbddfe250a4cd209c
parent 190661 e2a0e60d95e2f1c88df21e772651ba3cbc916b58
child 190663 15516aebd58d79d954a4218b0f7d328561d8fec2
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, jaws
bugs976792
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 976792 - [australis] CustomizableUI's insertNodeInWindow should go through placements and try followup next nodes. r=Gijs r=jaws
browser/components/customizableui/src/CustomizableUI.jsm
browser/components/customizableui/test/browser.ini
browser/components/customizableui/test/browser_976792_insertNodeInWindow.js
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -879,25 +879,24 @@ let CustomizableUIInternal = {
 
     let placements = gPlacements.get(aArea);
     if (!placements) {
       ERROR("Could not find any placements for " + aArea +
             " when moving a widget.");
       return;
     }
 
-    let nextNodeId = placements[aPosition + 1];
     // Go through each of the nodes associated with this area and move the
     // widget to the requested location.
     for (let areaNode of areaNodes) {
-      this.insertNodeInWindow(aWidgetId, areaNode, nextNodeId, isNew);
+      this.insertNodeInWindow(aWidgetId, areaNode, isNew);
     }
   },
 
-  insertNodeInWindow: function(aWidgetId, aAreaNode, aNextNodeId, isNew) {
+  insertNodeInWindow: function(aWidgetId, aAreaNode, isNew) {
     let window = aAreaNode.ownerDocument.defaultView;
     let showInPrivateBrowsing = gPalette.has(aWidgetId)
                               ? gPalette.get(aWidgetId).showInPrivateBrowsing
                               : true;
 
     if (!showInPrivateBrowsing && PrivateBrowsingUtils.isWindowPrivate(window)) {
       return;
     }
@@ -911,36 +910,47 @@ let CustomizableUIInternal = {
     let areaId = aAreaNode.id;
     if (isNew) {
       this.ensureButtonContextMenu(widgetNode, aAreaNode);
       if (widgetNode.localName == "toolbarbutton" && areaId == CustomizableUI.AREA_PANEL) {
         widgetNode.setAttribute("wrap", "true");
       }
     }
 
-    let container = aAreaNode.customizationTarget;
-    let [insertionContainer, nextNode] = this.findInsertionPoints(widgetNode, aNextNodeId, aAreaNode);
+    let [insertionContainer, nextNode] = this.findInsertionPoints(widgetNode, aAreaNode);
     this.insertWidgetBefore(widgetNode, nextNode, insertionContainer, areaId);
 
     if (gAreas.get(areaId).get("type") == CustomizableUI.TYPE_TOOLBAR) {
       aAreaNode.setAttribute("currentset", gPlacements.get(areaId).join(','));
     }
   },
 
-  findInsertionPoints: function(aNode, aNextNodeId, aAreaNode) {
-    let props = gAreas.get(aAreaNode.id);
-    if (props.get("type") == CustomizableUI.TYPE_TOOLBAR && props.get("overflowable") &&
-        aAreaNode.getAttribute("overflowing") == "true") {
-      return aAreaNode.overflowable.getOverflowedInsertionPoints(aNode, aNextNodeId);
+  findInsertionPoints: function(aNode, aAreaNode) {
+    let areaId = aAreaNode.id;
+    let props = gAreas.get(areaId);
+
+    // For overflowable toolbars, rely on them (because the work is more complicated):
+    if (props.get("type") == CustomizableUI.TYPE_TOOLBAR && props.get("overflowable")) {
+      return aAreaNode.overflowable.findOverflowedInsertionPoints(aNode);
     }
-    let nextNode = null;
-    if (aNextNodeId) {
-      nextNode = aAreaNode.customizationTarget.getElementsByAttribute("id", aNextNodeId)[0];
+
+    let container = aAreaNode.customizationTarget;
+    let placements = gPlacements.get(areaId);
+    let nodeIndex = placements.indexOf(aNode.id);
+
+    while (++nodeIndex < placements.length) {
+      let nextNodeId = placements[nodeIndex];
+      let nextNode = container.getElementsByAttribute("id", nextNodeId).item(0);
+
+      if (nextNode) {
+        return [container, nextNode];
+      }
     }
-    return [aAreaNode.customizationTarget, nextNode];
+
+    return [container, null];
   },
 
   insertWidgetBefore: function(aNode, aNextNode, aContainer, aArea) {
     this.notifyListeners("onWidgetBeforeDOMChange", aNode, aNextNode, aContainer);
     this.setLocationAttributes(aNode, aArea);
     aContainer.insertBefore(aNode, aNextNode);
     this.notifyListeners("onWidgetAfterDOMChange", aNode, aNextNode, aContainer);
   },
@@ -2279,19 +2289,17 @@ let CustomizableUIInternal = {
     if (!container.length) {
       return false;
     }
     let existingNode = container[0].getElementsByAttribute("id", aWidgetId)[0];
     if (existingNode) {
       return true;
     }
 
-    let placementAry = gPlacements.get(placement.area);
-    let nextNodeId = placementAry[placement.position + 1];
-    this.insertNodeInWindow(aWidgetId, container[0], nextNodeId, true);
+    this.insertNodeInWindow(aWidgetId, container[0], true);
     return true;
   },
 
   get inDefaultState() {
     for (let [areaId, props] of gAreas) {
       let defaultPlacements = props.get("defaultPlacements");
       // Areas without default placements (like legacy ones?) get skipped
       if (!defaultPlacements) {
@@ -3767,33 +3775,47 @@ OverflowableToolbar.prototype = {
           // If it's now the first item in the overflow list,
           // maybe we can return it:
           this._moveItemsBackToTheirOrigin();
         }
       }
     }
   },
 
-  getOverflowedInsertionPoints: function(aNode, aNextNodeId) {
-    if (aNode.getAttribute("overflows") == "false") {
-      return [this._target, null];
-    }
-    // Inserting at the end means we're in the overflow list by definition:
-    if (!aNextNodeId) {
-      return [this._list, null];
+  findOverflowedInsertionPoints: function(aNode) {
+    let newNodeCanOverflow = aNode.getAttribute("overflows") != "false";
+    let areaId = this._toolbar.id;
+    let placements = gPlacements.get(areaId);
+    let nodeIndex = placements.indexOf(aNode.id);
+    let nodeBeforeNewNodeIsOverflown = false;
+
+    let loopIndex = -1;
+    while (++loopIndex < placements.length) {
+      let nextNodeId = placements[loopIndex];
+      if (loopIndex > nodeIndex) {
+        if (newNodeCanOverflow && this._collapsed.has(nextNodeId)) {
+          let nextNode = this._list.getElementsByAttribute("id", nextNodeId).item(0);
+          if (nextNode) {
+            return [this._list, nextNode];
+          }
+        }
+        if (!nodeBeforeNewNodeIsOverflown || !newNodeCanOverflow) {
+          let nextNode = this._target.getElementsByAttribute("id", nextNodeId).item(0);
+          if (nextNode) {
+            return [this._target, nextNode];
+          }
+        }
+      } else if (loopIndex < nodeIndex && this._collapsed.has(nextNodeId)) {
+        nodeBeforeNewNodeIsOverflown = true;
+      }
     }
 
-    let nextNode = this._list.getElementsByAttribute("id", aNextNodeId)[0];
-    // If this is the first item, we can actually just append the node
-    // to the end of the toolbar.  If it results in an overflow event, we'll move
-    // the new node to the overflow target.
-    if (!nextNode.previousSibling) {
-      return [this._target, null];
-    }
-    return [this._list, nextNode];
+    let containerForAppending = (this._collapsed.size && newNodeCanOverflow) ?
+                                this._list : this._target;
+    return [containerForAppending, null];
   },
 
   getContainerFor: function(aNode) {
     if (aNode.getAttribute("overflowedItem") == "true") {
       return this._list;
     }
     return this._target;
   },
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -68,12 +68,16 @@ skip-if = os == "linux"
 [browser_968447_bookmarks_toolbar_items_in_panel.js]
 [browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
 [browser_972267_customizationchange_events.js]
 [browser_973932_addonbar_currentset.js]
 [browser_975719_customtoolbars_behaviour.js]
+
+[browser_976792_insertNodeInWindow.js]
+skip-if = os == "linux"
+
 [browser_978084_dragEnd_after_move.js]
 [browser_980155_add_overflow_toolbar.js]
 [browser_981418-widget-onbeforecreated-handler.js]
 [browser_panel_toggle.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_976792_insertNodeInWindow.js
@@ -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/. */
+
+"use strict";
+
+const kToolbarName = "test-insertNodeInWindow-placements-toolbar";
+const kTestWidgetPrefix = "test-widget-for-insertNodeInWindow-placements-";
+
+
+/*
+Tries to replicate the situation of having a placement list like this:
+
+exists-1,trying-to-insert-this,doesn't-exist,exists-2
+*/
+add_task(function() {
+  let testWidgetExists = [true, false, false, true];
+  let widgetIds = [];
+  for (let i = 0; i < testWidgetExists.length; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (testWidgetExists[i]) {
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i};
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+
+  let btnId = kTestWidgetPrefix + 1;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName, "New XUL widget should be placed inside new toolbar");
+
+  is(btn.previousSibling.id, toolbarNode.firstChild.id,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  btn.remove();
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+situation similar to:
+
+exists-1,exists-2,overflow-1,trying-to-insert-this,overflow-2
+*/
+add_task(function() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 2,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,doesn't-exist,trying-to-insert-this,overflow-2
+*/
+add_task(function() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 2);
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,doesn't-exist,trying-to-insert-this,doesn't-exist
+*/
+add_task(function() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 2);
+  CustomizableUI.destroyWidget(testWidgetId);
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 4);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling, null,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,can't-overflow,trying-to-insert-this,overflow-2
+*/
+add_task(function() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 5; i >= 0; i--) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar", 0);
+  }
+
+  for (let i = 10; i < 15; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  // Find last widget that doesn't allow overflowing
+  let nonOverflowing = navbar.customizationTarget.lastChild;
+  is(nonOverflowing.getAttribute("overflows"), "false", "Last child is expected to not allow overflowing");
+  isnot(nonOverflowing.getAttribute("skipintoolbarset"), "true", "Last child is expected to not be skipintoolbarset");
+
+  let testWidgetId = kTestWidgetPrefix + 10;
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 11,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,trying-to-insert-this,can't-overflow,overflow-2
+*/
+add_task(function() {
+  let widgetIds = [];
+  let missingId = 2;
+  let nonOverflowableId = 3;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) {
+                    node.style.minWidth = "200px";
+                    if (id == (kTestWidgetPrefix + nonOverflowableId)) {
+                      node.setAttribute("overflows", false);
+                    }
+                 }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-overflow-list", "New XUL widget should be placed inside new toolbar's overflow");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests nodes do *not* get placed in the toolbar's overflow. Replicates a
+plcements situation similar to:
+
+exists-1,trying-to-insert-this,exists-2,overflowed-1
+*/
+add_task(function() {
+  let widgetIds = [];
+  let missingId = 1;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) { node.style.minWidth = "100px"; }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-target", "New XUL widget should be placed inside new toolbar");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests inserting a node onto the end of an overflowing toolbar *doesn't* put it in
+the overflow list when the widget disallows overflowing. ie:
+
+exists-1,exists-2,overflows-1,trying-to-insert-this
+
+Where trying-to-insert-this has overflows=false
+*/
+add_task(function() {
+  let widgetIds = [];
+  let missingId = 3;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) { node.style.minWidth = "200px"; }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  btn.setAttribute("overflows", false);
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-target", "New XUL widget should be placed inside new toolbar");
+  is(btn.nextSibling, null,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+add_task(function asyncCleanUp() {
+  yield resetCustomization();
+});