Bug 653881 - Rework XBL insertion points and clean up related code to more closely follow the Web Components model. Instead of maintaining a hashtable of insertion points in bindings (and removing insertions points from the tree) leave the insertion points in the tree as explicit placeholders and teach all other relevant code how to walk the explicit children of elements via two iterators (ExplicitChildIterator and FlattenedChildIterator). Note that this patch does not maintain 100% compatibility with the previous code: there are bug fixes and behavior changes included. For example, by having explicit insertion points in the bindings, it is now easier to handle dynamic changes to the bound element correctly (as well as, eventually, handling dynamic changes to the binding correctly). Patch originally by sicking. r=bzbarsky
authorBlake Kaplan <mrbkap@gmail.com>
Wed, 01 May 2013 15:50:08 -0700
changeset 136868 731a5fcf13289e02bbd86f06950a3b93c7357e12
parent 136867 4df4f2767a69330a1d221abb71e9d0b993b6a973
child 136869 ee7bde7c1ec6c55dd92c7ef58de673ac7aa4316f
push id24898
push userphilringnalda@gmail.com
push dateSat, 29 Jun 2013 13:54:45 +0000
treeherdermozilla-central@cbb24a4a96af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs653881
milestone25.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 653881 - Rework XBL insertion points and clean up related code to more closely follow the Web Components model. Instead of maintaining a hashtable of insertion points in bindings (and removing insertions points from the tree) leave the insertion points in the tree as explicit placeholders and teach all other relevant code how to walk the explicit children of elements via two iterators (ExplicitChildIterator and FlattenedChildIterator). Note that this patch does not maintain 100% compatibility with the previous code: there are bug fixes and behavior changes included. For example, by having explicit insertion points in the bindings, it is now easier to handle dynamic changes to the bound element correctly (as well as, eventually, handling dynamic changes to the binding correctly). Patch originally by sicking. r=bzbarsky
accessible/src/base/nsCoreUtils.cpp
accessible/src/base/nsTextEquivUtils.cpp
accessible/src/generic/DocAccessible.cpp
accessible/src/html/HTMLTableAccessible.cpp
accessible/src/xul/XULElementAccessibles.cpp
accessible/src/xul/XULFormControlAccessible.cpp
accessible/src/xul/XULListboxAccessible.cpp
accessible/src/xul/XULMenuAccessible.cpp
content/base/public/nsIContent.h
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/src/ChildIterator.cpp
content/base/src/ChildIterator.h
content/base/src/Element.cpp
content/base/src/FragmentOrElement.cpp
content/base/src/moz.build
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsINode.cpp
content/base/src/nsNameSpaceManager.cpp
content/xbl/src/moz.build
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsBindingManager.h
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLBinding.h
content/xbl/src/nsXBLChildrenElement.cpp
content/xbl/src/nsXBLChildrenElement.h
content/xbl/src/nsXBLInsertionPoint.cpp
content/xbl/src/nsXBLInsertionPoint.h
content/xbl/src/nsXBLPrototypeBinding.cpp
content/xbl/src/nsXBLPrototypeBinding.h
content/xml/content/src/moz.build
content/xul/templates/src/nsXULTemplateBuilder.cpp
dom/base/nsFocusManager.cpp
layout/base/moz.build
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsChildIterator.cpp
layout/base/nsChildIterator.h
layout/base/nsFrameManager.cpp
layout/inspector/src/Makefile.in
layout/inspector/src/inDOMUtils.cpp
layout/inspector/src/inDeepTreeWalker.cpp
layout/style/nsCSSRuleProcessor.cpp
layout/xul/base/src/nsListBoxBodyFrame.cpp
layout/xul/base/src/nsListBoxObject.cpp
layout/xul/base/src/nsMenuBarFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsXULPopupManager.cpp
layout/xul/tree/Makefile.in
layout/xul/tree/nsTreeBodyFrame.cpp
layout/xul/tree/nsTreeBoxObject.cpp
layout/xul/tree/nsTreeContentView.cpp
layout/xul/tree/nsTreeUtils.cpp
toolkit/content/tests/chrome/test_bug562554.xul
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -192,17 +192,17 @@ nsCoreUtils::GetAccessKeyFor(nsIContent*
 
 nsIContent *
 nsCoreUtils::GetDOMElementFor(nsIContent *aContent)
 {
   if (aContent->IsElement())
     return aContent;
 
   if (aContent->IsNodeOfType(nsINode::eTEXT))
-    return aContent->GetParent();
+    return aContent->GetFlattenedTreeParent();
 
   return nullptr;
 }
 
 nsINode *
 nsCoreUtils::GetDOMNodeFromDOMPoint(nsINode *aNode, uint32_t aOffset)
 {
   if (aNode && aNode->IsElement()) {
@@ -535,17 +535,17 @@ nsCoreUtils::GetTreeBoxObject(nsIContent
       if (xulElement) {
         nsCOMPtr<nsIBoxObject> box;
         xulElement->GetBoxObject(getter_AddRefs(box));
         nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
         if (treeBox)
           return treeBox.forget();
       }
     }
-    currentContent = currentContent->GetParent();
+    currentContent = currentContent->GetFlattenedTreeParent();
   }
 
   return nullptr;
 }
 
 already_AddRefed<nsITreeColumn>
 nsCoreUtils::GetFirstSensibleColumn(nsITreeBoxObject *aTree)
 {
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -131,17 +131,17 @@ nsTextEquivUtils::AppendTextEquivFromCon
 
 nsresult
 nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
                                                  nsAString *aString)
 {
   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
     bool isHTMLBlock = false;
 
-    nsIContent *parentContent = aContent->GetParent();
+    nsIContent *parentContent = aContent->GetFlattenedTreeParent();
     if (parentContent) {
       nsIFrame *frame = parentContent->GetPrimaryFrame();
       if (frame) {
         // If this text is inside a block level frame (as opposed to span
         // level), we need to add spaces around that block's text, so we don't
         // get words jammed together in final name.
         const nsStyleDisplay* display = frame->StyleDisplay();
         if (display->IsBlockOutsideStyle() ||
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -1372,18 +1372,19 @@ DocAccessible::RecreateAccessible(nsICon
   }
 #endif
 
   // XXX: we shouldn't recreate whole accessible subtree, instead we should
   // subclass hide and show events to handle them separately and implement their
   // coalescence with normal hide and show events. Note, in this case they
   // should be coalesced with normal show/hide events.
 
-  ContentRemoved(aContent->GetParent(), aContent);
-  ContentInserted(aContent->GetParent(), aContent, aContent->GetNextSibling());
+  nsIContent* parent = aContent->GetFlattenedTreeParent();
+  ContentRemoved(parent, aContent);
+  ContentInserted(parent, aContent, aContent->GetNextSibling());
 }
 
 void
 DocAccessible::ProcessInvalidationList()
 {
   // Invalidate children of container accessible for each element in
   // invalidation list. Allow invalidation list insertions while container
   // children are recached.
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -309,16 +309,17 @@ HTMLTableHeaderCellAccessible::NativeRol
     case 0:
       return roles::COLUMNHEADER;
     case 1:
       return roles::ROWHEADER;
   }
 
   // Assume it's columnheader if there are headers in siblings, otherwise
   // rowheader.
+  // This should iterate the flattened tree
   nsIContent* parentContent = mContent->GetParent();
   if (!parentContent) {
     NS_ERROR("Deattached content on alive accessible?");
     return roles::NOTHING;
   }
 
   for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent;
        siblingContent = siblingContent->GetPreviousSibling()) {
--- a/accessible/src/xul/XULElementAccessibles.cpp
+++ b/accessible/src/xul/XULElementAccessibles.cpp
@@ -90,17 +90,17 @@ XULLabelAccessible::NativeState()
 }
 
 Relation
 XULLabelAccessible::RelationByType(uint32_t aType)
 {
   Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
   if (aType == nsIAccessibleRelation::RELATION_LABEL_FOR) {
     // Caption is the label for groupbox
-    nsIContent *parent = mContent->GetParent();
+    nsIContent* parent = mContent->GetFlattenedTreeParent();
     if (parent && parent->Tag() == nsGkAtoms::caption) {
       Accessible* parent = Parent();
       if (parent && parent->Role() == roles::GROUPING)
         rel.AppendTarget(parent);
     }
   }
 
   return rel;
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -239,17 +239,17 @@ XULDropmarkerAccessible::ActionCount()
 }
 
 bool
 XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen)
 {
   bool isOpen = false;
 
   nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
-    do_QueryInterface(mContent->GetParent());
+    do_QueryInterface(mContent->GetFlattenedTreeParent());
 
   if (parentButtonElement) {
     parentButtonElement->GetOpen(&isOpen);
     if (aToggleOpen)
       parentButtonElement->SetOpen(!isOpen);
   }
   else {
     nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
--- a/accessible/src/xul/XULListboxAccessible.cpp
+++ b/accessible/src/xul/XULListboxAccessible.cpp
@@ -98,17 +98,17 @@ XULColumnItemAccessible::DoAction(uint8_
 ////////////////////////////////////////////////////////////////////////////////
 // XULListboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULListboxAccessible::
   XULListboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULSelectControlAccessible(aContent, aDoc), xpcAccessibleTable(this)
 {
-  nsIContent* parentContent = mContent->GetParent();
+  nsIContent* parentContent = mContent->GetFlattenedTreeParent();
   if (parentContent) {
     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
       do_QueryInterface(parentContent);
     if (autoCompletePopupElm)
       mGenericTypes |= eAutoCompletePopup;
   }
 }
 
--- a/accessible/src/xul/XULMenuAccessible.cpp
+++ b/accessible/src/xul/XULMenuAccessible.cpp
@@ -434,17 +434,17 @@ XULMenupopupAccessible::
   XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULSelectControlAccessible(aContent, aDoc)
 {
   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   if (menuPopupFrame && menuPopupFrame->IsMenu())
     mType = eMenuPopupType;
 
   // May be the anonymous <menupopup> inside <menulist> (a combobox)
-  mSelectControl = do_QueryInterface(mContent->GetParent());
+  mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
   if (!mSelectControl)
     mGenericTypes &= ~eSelect;
 }
 
 uint64_t
 XULMenupopupAccessible::NativeState()
 {
   uint64_t state = Accessible::NativeState();
@@ -472,17 +472,17 @@ XULMenupopupAccessible::NativeState()
 }
 
 ENameValueFlag
 XULMenupopupAccessible::NativeName(nsString& aName)
 {
   nsIContent* content = mContent;
   while (content && aName.IsEmpty()) {
     content->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
-    content = content->GetParent();
+    content = content->GetFlattenedTreeParent();
   }
 
   return eNameOK;
 }
 
 role
 XULMenupopupAccessible::NativeRole()
 {
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -606,17 +606,18 @@ public:
    * of its GetParent() chain via child lists).
    *
    * @return the binding parent
    */
   virtual nsIContent *GetBindingParent() const = 0;
 
   /**
    * Returns the content node that is the parent of this node in the flattened
-   * tree.
+   * tree. For nodes that are not filtered into an insertion point, this
+   * simply returns their DOM parent in the original DOM tree.
    *
    * @return the flattened tree parent
    */
   nsIContent *GetFlattenedTreeParent() const;
 
   /**
    * API to check if this is a link that's traversed in response to user input
    * (e.g. a click event). Specializations for HTML/SVG/generic XML allow for
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1310,28 +1310,16 @@ public:
    * Check whether we've ever fired a DOMTitleChanged event for this
    * document.
    */
   bool HaveFiredDOMTitleChange() const {
     return mHaveFiredTitleChange;
   }
 
   /**
-   * See GetXBLChildNodesFor on nsBindingManager
-   */
-  virtual nsresult GetXBLChildNodesFor(nsIContent* aContent,
-                                       nsIDOMNodeList** aResult) = 0;
-
-  /**
-   * See GetContentListFor on nsBindingManager
-   */
-  virtual nsresult GetContentListFor(nsIContent* aContent,
-                                     nsIDOMNodeList** aResult) = 0;
-
-  /**
    * See GetAnonymousElementByAttribute on nsIDOMDocumentXBL.
    */
   virtual Element*
     GetAnonymousElementByAttribute(nsIContent* aElement,
                                    nsIAtom* aAttrName,
                                    const nsAString& aAttrValue) const = 0;
 
   /**
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -109,18 +109,16 @@ enum {
   NODE_MAY_BE_IN_BINDING_MNGR =           NODE_FLAG_BIT(6),
 
   NODE_IS_EDITABLE =                      NODE_FLAG_BIT(7),
 
   // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
   // node in fact has a class, but may be set even if it doesn't.
   NODE_MAY_HAVE_CLASS =                   NODE_FLAG_BIT(8),
 
-  NODE_IS_INSERTION_PARENT =              NODE_FLAG_BIT(9),
-
   // Node has an :empty or :-moz-only-whitespace selector
   NODE_HAS_EMPTY_SELECTOR =               NODE_FLAG_BIT(10),
 
   // A child of the node has a selector such that any insertion,
   // removal, or appending of children requires restyling the parent.
   NODE_HAS_SLOW_SELECTOR =                NODE_FLAG_BIT(11),
 
   // A child of the node has a :first-child, :-moz-first-node,
new file mode 100644
--- /dev/null
+++ b/content/base/src/ChildIterator.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* 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/. */
+
+#include "ChildIterator.h"
+#include "nsXBLChildrenElement.h"
+
+namespace mozilla {
+namespace dom {
+
+nsIContent*
+ExplicitChildIterator::GetNextChild()
+{
+  // If we're already in the inserted-children array, look there first
+  if (mIndexInInserted) {
+    MOZ_ASSERT(mChild);
+    MOZ_ASSERT(mChild->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL));
+    MOZ_ASSERT(!mDefaultChild);
+
+    nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(mChild);
+    if (mIndexInInserted < point->mInsertedChildren.Length()) {
+      return point->mInsertedChildren[mIndexInInserted++];
+    }
+    mIndexInInserted = 0;
+    mChild = mChild->GetNextSibling();
+  } else if (mDefaultChild) {
+    // If we're already in default content, check if there are more nodes there
+    MOZ_ASSERT(mChild);
+    MOZ_ASSERT(mChild->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL));
+
+    mDefaultChild = mDefaultChild->GetNextSibling();
+    if (mDefaultChild) {
+      return mDefaultChild;
+    }
+
+    mChild = mChild->GetNextSibling();
+  } else if (mIsFirst) {  // at the beginning of the child list
+    mChild = mParent->GetFirstChild();
+    mIsFirst = false;
+  } else if (mChild) { // in the middle of the child list
+    mChild = mChild->GetNextSibling();
+  }
+
+  // Iterate until we find a non-<children>, or a <children> with content.
+  while (mChild &&
+         mChild->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(mChild);
+    if (!point->mInsertedChildren.IsEmpty()) {
+      mIndexInInserted = 1;
+      return point->mInsertedChildren[0];
+    }
+
+    mDefaultChild = mChild->GetFirstChild();
+    if (mDefaultChild) {
+      return mDefaultChild;
+    }
+
+    mChild = mChild->GetNextSibling();
+  }
+
+  return mChild;
+}
+
+FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent)
+  : ExplicitChildIterator(aParent), mXBLInvolved(false)
+{
+  nsXBLBinding* binding =
+    aParent->OwnerDoc()->BindingManager()->GetBindingWithContent(aParent);
+
+  if (binding) {
+    nsIContent* anon = binding->GetAnonymousContent();
+    if (anon) {
+      mParent = anon;
+      mXBLInvolved = true;
+    }
+  }
+
+  // We set mXBLInvolved to true if either:
+  // - The node we're iterating has a binding with content attached to it.
+  // - The node is generated XBL content and has an <xbl:children> child.
+  if (!mXBLInvolved && aParent->GetBindingParent()) {
+    for (nsIContent* child = aParent->GetFirstChild();
+         child;
+         child = child->GetNextSibling()) {
+      if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+        mXBLInvolved = true;
+        break;
+      }
+    }
+  }
+}
+
+nsIContent* FlattenedChildIterator::Get()
+{
+  MOZ_ASSERT(!mIsFirst);
+
+  if (mIndexInInserted) {
+    nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(mChild);
+    return point->mInsertedChildren[mIndexInInserted - 1];
+  }
+  return mDefaultChild ? mDefaultChild : mChild;
+}
+
+nsIContent* FlattenedChildIterator::GetPreviousChild()
+{
+  // If we're already in the inserted-children array, look there first
+  if (mIndexInInserted) {
+    // NB: mIndexInInserted points one past the last returned child so we need
+    // to look *two* indices back in order to return the previous child.
+    nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(mChild);
+    if (--mIndexInInserted) {
+      return point->mInsertedChildren[mIndexInInserted - 1];
+    }
+    mChild = mChild->GetPreviousSibling();
+  } else if (mDefaultChild) {
+    // If we're already in default content, check if there are more nodes there
+    mDefaultChild = mDefaultChild->GetPreviousSibling();
+    if (mDefaultChild) {
+      return mDefaultChild;
+    }
+
+    mChild = mChild->GetPreviousSibling();
+  } else if (mIsFirst) { // at the beginning of the child list
+    return nullptr;
+  } else if (mChild) { // in the middle of the child list
+    mChild = mChild->GetPreviousSibling();
+  } else { // at the end of the child list
+    mChild = mParent->GetLastChild();
+  }
+
+  // Iterate until we find a non-<children>, or a <children> with content.
+  while (mChild &&
+         mChild->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(mChild);
+    if (!point->mInsertedChildren.IsEmpty()) {
+      mIndexInInserted = point->InsertedChildrenLength();
+      return point->mInsertedChildren[mIndexInInserted - 1];
+    }
+
+    mDefaultChild = mChild->GetLastChild();
+    if (mDefaultChild) {
+      return mDefaultChild;
+    }
+
+    mChild = mChild->GetPreviousSibling();
+  }
+
+  if (!mChild) {
+    mIsFirst = true;
+  }
+
+  return mChild;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/base/src/ChildIterator.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* 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/. */
+
+/**
+ * Iterates over the children on a node. If a child is an insertion point,
+ * iterates over the children inserted there instead, or the default content
+ * if no children are inserted there.
+ *
+ * The FlattenedChildIterator expands any anonymous content bound from an XBL
+ * binding's <xbl:content> element.
+ */
+
+#include "nsIContent.h"
+
+namespace mozilla {
+namespace dom {
+
+// This class iterates normal DOM child nodes of a given DOM node with
+// <xbl:children> nodes replaced by the elements that have been filtered into that
+// insertion point. Any bindings on the given element are ignored for purposes
+// of determining which insertion point children are filtered into.
+class ExplicitChildIterator
+{
+public:
+  ExplicitChildIterator(nsIContent* aParent)
+    : mParent(aParent),
+      mChild(nullptr),
+      mDefaultChild(nullptr),
+      mIndexInInserted(0),
+      mIsFirst(true)
+  {
+  }
+
+  nsIContent* GetNextChild();
+
+protected:
+  // The parent of the children being iterated. For the FlattenedChildIterator,
+  // if there is a binding attached to the original parent, mParent points to
+  // the <xbl:content> element for the binding.
+  nsIContent* mParent;
+
+  // The current child. When we encounter an <xbl:children> insertion point,
+  // mChild remains as the insertion point whose content we're iterating (and
+  // our state is controled by mDefaultChild or mIndexInInserted depending on
+  // whether the insertion point expands to its default content or not).
+  nsIContent* mChild;
+
+  // If non-null, this points to the current default content for the current
+  // insertion point that we're iterating (i.e. mChild, which must be an
+  // nsXBLChildrenElement). Once this transitions back to null,
+  // we continue iterating at mChild's next sibling.
+  nsIContent* mDefaultChild;
+
+  // If not zero, we're iterating inserted children for an insertion point. This
+  // is an index into mChild's inserted children array (mChild must be an
+  // nsXBLChildrenElement). The index is one past the "current" child (as
+  // opposed to mChild which represents the "current" child).
+  uint32_t mIndexInInserted;
+
+  // A flag to let us know that we haven't started iterating yet.
+  bool mIsFirst;
+};
+
+// Iterates over the flattened children of a node, which accounts for anonymous
+// children and nodes moved by insertion points. If a node has anonymous
+// children, those are iterated over.
+class FlattenedChildIterator : public ExplicitChildIterator
+{
+public:
+  FlattenedChildIterator(nsIContent* aParent);
+
+  // Returns the current target of this iterator (which might be an explicit
+  // child of the node, default content for an <xbl:children> element or
+  // an inserted child for an <xbl:children> element.
+  nsIContent* Get();
+
+  // The inverse of GetNextChild. Properly steps in and out of <xbl:children>
+  // elements.
+  nsIContent* GetPreviousChild();
+
+  // Looks for aChildToFind respecting XBL insertion points.
+  void Seek(nsIContent* aChildToFind)
+  {
+    // It would be nice to assert that we find aChildToFind, but bz thinks that
+    // we might not find aChildToFind when called from ContentInserted
+    // if first-letter frames are about.
+
+    nsIContent* child;
+    do {
+      child = GetNextChild();
+    } while (child && child != aChildToFind);
+  }
+
+  bool XBLInvolved() { return mXBLInvolved; }
+
+private:
+  // For certain optimizations, nsCSSFrameConstructor needs to know if the
+  // child list of the element that we're iterating matches its .childNodes.
+  bool mXBLInvolved;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -68,16 +68,17 @@
 #include "nsXBLBinding.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIBoxObject.h"
 #include "nsClientRect.h"
 #include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
+#include "ChildIterator.h"
 
 #include "nsIDOMEventListener.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "jsapi.h"
 
@@ -89,17 +90,16 @@
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIControllers.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
-#include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 
 #ifdef MOZ_XUL
@@ -904,70 +904,16 @@ Element::HasAttributeNS(const nsAString&
     // Unknown namespace means no attr...
     return false;
   }
 
   nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
   return HasAttr(nsid, name);
 }
 
-static nsXBLBinding*
-GetFirstBindingWithContent(nsBindingManager* aBmgr, nsIContent* aBoundElem)
-{
-  nsXBLBinding* binding = aBmgr->GetBinding(aBoundElem);
-  while (binding) {
-    if (binding->GetAnonymousContent()) {
-      return binding;
-    }
-    binding = binding->GetBaseBinding();
-  }
-  
-  return nullptr;
-}
-
-static nsresult
-BindNodesInInsertPoints(nsXBLBinding* aBinding, nsIContent* aInsertParent,
-                        nsIDocument* aDocument)
-{
-  NS_PRECONDITION(aBinding && aInsertParent, "Missing arguments");
-
-  nsresult rv;
-  // These should be refcounted or otherwise protectable.
-  nsInsertionPointList* inserts =
-    aBinding->GetExistingInsertionPointsFor(aInsertParent);
-  if (inserts) {
-    bool allowScripts = aBinding->AllowScripts();
-#ifdef MOZ_XUL
-    nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(aDocument);
-#endif
-    uint32_t i;
-    for (i = 0; i < inserts->Length(); ++i) {
-      nsCOMPtr<nsIContent> insertRoot =
-        inserts->ElementAt(i)->GetDefaultContent();
-      if (insertRoot) {
-        for (nsCOMPtr<nsIContent> child = insertRoot->GetFirstChild();
-             child;
-             child = child->GetNextSibling()) {
-          rv = child->BindToTree(aDocument, aInsertParent,
-                                 aBinding->GetBoundElement(), allowScripts);
-          NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef MOZ_XUL
-          if (xulDoc) {
-            xulDoc->AddSubtreeToDocument(child);
-          }
-#endif
-        }
-      }
-    }
-  }
-
-  return NS_OK;
-}
-
 already_AddRefed<nsIHTMLCollection>
 Element::GetElementsByClassName(const nsAString& aClassNames)
 {
   return nsContentUtils::GetElementsByClassName(this, aClassNames);
 }
 
 nsresult
 Element::GetElementsByClassName(const nsAString& aClassNames,
@@ -1099,43 +1045,27 @@ Element::BindToTree(nsIDocument* aDocume
   }
 
   // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
   // that also need to be told that they are moving.
   nsresult rv;
   if (hadForceXBL) {
     nsBindingManager* bmgr = OwnerDoc()->BindingManager();
 
+    nsXBLBinding* contBinding = bmgr->GetBindingWithContent(this);
     // First check if we have a binding...
-    nsXBLBinding* contBinding =
-      GetFirstBindingWithContent(bmgr, this);
     if (contBinding) {
       nsCOMPtr<nsIContent> anonRoot = contBinding->GetAnonymousContent();
       bool allowScripts = contBinding->AllowScripts();
       for (nsCOMPtr<nsIContent> child = anonRoot->GetFirstChild();
            child;
            child = child->GetNextSibling()) {
         rv = child->BindToTree(aDocument, this, this, allowScripts);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      // ...then check if we have content in insertion points that are
-      // direct children of the <content>
-      rv = BindNodesInInsertPoints(contBinding, this, aDocument);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    // ...and finally check if we're in a binding where we have content in
-    // insertion points.
-    if (aBindingParent) {
-      nsXBLBinding* binding = bmgr->GetBinding(aBindingParent);
-      if (binding) {
-        rv = BindNodesInInsertPoints(binding, this, aDocument);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
     }
   }
 
   UpdateEditableState(false);
 
   // Now recurse into our kids
   for (nsIContent* child = GetFirstChild(); child;
        child = child->GetNextSibling()) {
@@ -1178,33 +1108,30 @@ Element::BindToTree(nsIDocument* aDocume
 
   return NS_OK;
 }
 
 class RemoveFromBindingManagerRunnable : public nsRunnable {
 public:
   RemoveFromBindingManagerRunnable(nsBindingManager* aManager,
                                    Element* aElement,
-                                   nsIDocument* aDoc,
-                                   nsIContent* aBindingParent):
-    mManager(aManager), mElement(aElement), mDoc(aDoc),
-    mBindingParent(aBindingParent)
+                                   nsIDocument* aDoc):
+    mManager(aManager), mElement(aElement), mDoc(aDoc)
   {}
 
   NS_IMETHOD Run()
   {
-    mManager->RemovedFromDocumentInternal(mElement, mDoc, mBindingParent);
+    mManager->RemovedFromDocumentInternal(mElement, mDoc);
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsBindingManager> mManager;
   nsRefPtr<Element> mElement;
   nsCOMPtr<nsIDocument> mDoc;
-  nsCOMPtr<nsIContent> mBindingParent;
 };
 
 void
 Element::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()),
                   "Shallow unbind won't clear document and binding parent on "
                   "kids!");
@@ -1242,17 +1169,17 @@ Element::UnbindFromTree(bool aDeep, bool
   SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
 
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
       nsContentUtils::AddScriptRunner(
         new RemoveFromBindingManagerRunnable(document->BindingManager(), this,
-                                             document, GetBindingParent()));
+                                             document));
     }
 
     document->ClearBoxObjectFor(this);
   }
 
   // Ensure that CSS transitions don't continue on an element at a
   // different place in the tree (even if reinserted before next
   // animation refresh).
@@ -2101,54 +2028,46 @@ Element::List(FILE* out, int32_t aIndent
   // Note: not listing nsIAnonymousContentCreator-created content...
 
   nsBindingManager* bindingManager = document->BindingManager();
   nsCOMPtr<nsIDOMNodeList> anonymousChildren;
   bindingManager->GetAnonymousNodesFor(nonConstThis,
                                        getter_AddRefs(anonymousChildren));
 
   if (anonymousChildren) {
-    uint32_t length;
+    uint32_t length = 0;
     anonymousChildren->GetLength(&length);
-    if (length > 0) {
-      for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
-      fputs("anonymous-children<\n", out);
-
-      for (uint32_t i = 0; i < length; ++i) {
-        nsCOMPtr<nsIDOMNode> node;
-        anonymousChildren->Item(i, getter_AddRefs(node));
-        nsCOMPtr<nsIContent> child = do_QueryInterface(node);
-        child->List(out, aIndent + 1);
-      }
-
-      for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
-      fputs(">\n", out);
+
+    for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
+    fputs("anonymous-children<\n", out);
+
+    for (uint32_t i = 0; i < length; ++i) {
+      nsCOMPtr<nsIDOMNode> node;
+      anonymousChildren->Item(i, getter_AddRefs(node));
+      nsCOMPtr<nsIContent> child = do_QueryInterface(node);
+      child->List(out, aIndent + 1);
     }
-  }
-
-  if (bindingManager->HasContentListFor(nonConstThis)) {
-    nsCOMPtr<nsIDOMNodeList> contentList;
-    bindingManager->GetContentListFor(nonConstThis,
-                                      getter_AddRefs(contentList));
-
-    NS_ASSERTION(contentList != nullptr, "oops, binding manager lied");
-    
-    uint32_t length;
-    contentList->GetLength(&length);
-    if (length > 0) {
-      for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
-      fputs("content-list<\n", out);
-
-      for (uint32_t i = 0; i < length; ++i) {
-        nsCOMPtr<nsIDOMNode> node;
-        contentList->Item(i, getter_AddRefs(node));
-        nsCOMPtr<nsIContent> child = do_QueryInterface(node);
-        child->List(out, aIndent + 1);
+
+    for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
+    fputs(">\n", out);
+
+    bool outHeader = false;
+    ExplicitChildIterator iter(nonConstThis);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+      if (!outHeader) {
+        outHeader = true;
+
+        for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
+        fputs("content-list<\n", out);
       }
 
+      child->List(out, aIndent + 1);
+    }
+
+    if (outHeader) {
       for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
       fputs(">\n", out);
     }
   }
 }
 
 void
 Element::DumpContent(FILE* out, int32_t aIndent,
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -89,17 +89,17 @@
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIControllers.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
-#include "nsXBLInsertionPoint.h"
+#include "ChildIterator.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/NodeListBinding.h"
 #include "mozilla/dom/UndoManager.h"
 
 #ifdef MOZ_XUL
@@ -144,26 +144,25 @@ nsIContent::FindFirstNonChromeOnlyAccess
     }
   }
   return nullptr;
 }
 
 nsIContent*
 nsIContent::GetFlattenedTreeParent() const
 {
-  nsIContent *parent = GetParent();
-  if (parent && parent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    nsIDocument *doc = parent->OwnerDoc();
-    nsIContent* insertionElement =
-      doc->BindingManager()->GetNestedInsertionPoint(parent, this);
-    if (insertionElement) {
-      parent = insertionElement;
+  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
+    nsIContent* parent = OwnerDoc()->BindingManager()->
+      GetInsertionParent(const_cast<nsIContent*>(this));
+    if (parent) {
+      return parent;
     }
   }
-  return parent;
+
+  return GetParent();
 }
 
 nsIContent::IMEState
 nsIContent::GetDesiredIMEState()
 {
   if (!IsEditableInternal()) {
     // Check for the special case where we're dealing with elements which don't
     // have the editable flag set, but are readwrite (such as text controls).
@@ -648,34 +647,24 @@ FragmentOrElement::GetChildren(uint32_t 
       list->AppendElement(beforeFrame->GetContent());
     }
   }
 
   // If XBL is bound to this node then append XBL anonymous content including
   // explict content altered by insertion point if we were requested for XBL
   // anonymous content, otherwise append explicit content with respect to
   // insertion point if any.
-  nsINodeList *childList = nullptr;
-
-  nsIDocument* document = OwnerDoc();
   if (!(aFilter & eAllButXBL)) {
-    childList = document->BindingManager()->GetXBLChildNodesFor(this);
-    if (!childList) {
-      childList = ChildNodes();
+    FlattenedChildIterator iter(this);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+      list->AppendElement(child);
     }
-
   } else {
-    childList = document->BindingManager()->GetContentListFor(this);
-  }
-
-  if (childList) {
-    uint32_t length = 0;
-    childList->GetLength(&length);
-    for (uint32_t idx = 0; idx < length; idx++) {
-      nsIContent* child = childList->Item(idx);
+    ExplicitChildIterator iter(this);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       list->AppendElement(child);
     }
   }
 
   if (frame) {
     // Append native anonymous content to the end.
     nsIAnonymousContentCreator* creator = do_QueryFrame(frame);
     if (creator) {
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -82,16 +82,17 @@ CPP_SOURCES += [
     'WebSocket.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
     'nsCSPService.cpp',
     'nsChannelPolicy.cpp',
+    'ChildIterator.cpp',
     'nsContentAreaDragDrop.cpp',
     'nsContentIterator.cpp',
     'nsContentList.cpp',
     'nsContentPolicy.cpp',
     'nsContentSink.cpp',
     'nsContentUtils.cpp',
     'nsCopySupport.cpp',
     'nsCrossSiteListenerProxy.cpp',
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6164,28 +6164,16 @@ nsDocument::ClearBoxObjectFor(nsIContent
     nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
     if (boxObject) {
       boxObject->Clear();
       mBoxObjectTable->Remove(aContent);
     }
   }
 }
 
-nsresult
-nsDocument::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
-{
-  return BindingManager()->GetXBLChildNodesFor(aContent, aResult);
-}
-
-nsresult
-nsDocument::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
-{
-  return BindingManager()->GetContentListFor(aContent, aResult);
-}
-
 void
 nsDocument::FlushSkinBindings()
 {
   BindingManager()->FlushSkinBindings();
 }
 
 nsresult
 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -813,20 +813,16 @@ public:
 
   virtual NS_HIDDEN_(void) AddStyleRelevantLink(mozilla::dom::Link* aLink);
   virtual NS_HIDDEN_(void) ForgetLink(mozilla::dom::Link* aLink);
 
   NS_HIDDEN_(void) ClearBoxObjectFor(nsIContent* aContent);
   already_AddRefed<nsIBoxObject> GetBoxObjectFor(mozilla::dom::Element* aElement,
                                                  mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
 
-  virtual NS_HIDDEN_(nsresult) GetXBLChildNodesFor(nsIContent* aContent,
-                                                   nsIDOMNodeList** aResult);
-  virtual NS_HIDDEN_(nsresult) GetContentListFor(nsIContent* aContent,
-                                                 nsIDOMNodeList** aResult);
   virtual NS_HIDDEN_(Element*)
     GetAnonymousElementByAttribute(nsIContent* aElement,
                                    nsIAtom* aAttrName,
                                    const nsAString& aAttrValue) const;
 
   virtual NS_HIDDEN_(Element*) ElementFromPointHelper(float aX, float aY,
                                                       bool aIgnoreRootScrollFrame,
                                                       bool aFlushLayout);
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -88,17 +88,16 @@
 #include "nsRuleProcessorData.h"
 #include "nsString.h"
 #include "nsStyleConsts.h"
 #include "nsSVGFeatures.h"
 #include "nsSVGUtils.h"
 #include "nsTextNode.h"
 #include "nsUnicharUtils.h"
 #include "nsXBLBinding.h"
-#include "nsXBLInsertionPoint.h"
 #include "nsXBLPrototypeBinding.h"
 #include "prprf.h"
 #include "xpcpublic.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsCSSParser.h"
 #include "HTMLLegendElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "WrapperFactory.h"
@@ -1189,18 +1188,17 @@ nsINode::GetOwnerGlobal()
 }
 
 bool
 nsINode::UnoptimizableCCNode() const
 {
   const uintptr_t problematicFlags = (NODE_IS_ANONYMOUS |
                                       NODE_IS_IN_ANONYMOUS_SUBTREE |
                                       NODE_IS_NATIVE_ANONYMOUS_ROOT |
-                                      NODE_MAY_BE_IN_BINDING_MNGR |
-                                      NODE_IS_INSERTION_PARENT);
+                                      NODE_MAY_BE_IN_BINDING_MNGR);
   return HasFlag(problematicFlags) ||
          NodeType() == nsIDOMNode::ATTRIBUTE_NODE ||
          // For strange cases like xbl:content/xbl:children
          (IsElement() &&
           AsElement()->IsInNamespace(kNameSpaceID_XBL));
 }
 
 /* static */
--- a/content/base/src/nsNameSpaceManager.cpp
+++ b/content/base/src/nsNameSpaceManager.cpp
@@ -12,16 +12,18 @@
 #include "nsINameSpaceManager.h"
 #include "nsAutoPtr.h"
 #include "nsINodeInfo.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsDataHashtable.h"
 #include "nsString.h"
+#include "nsINodeInfo.h"
+#include "nsXBLChildrenElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define kXMLNSNameSpaceURI "http://www.w3.org/2000/xmlns/"
 #define kXMLNameSpaceURI "http://www.w3.org/XML/1998/namespace"
 #define kXHTMLNameSpaceURI "http://www.w3.org/1999/xhtml"
 #define kXLinkNameSpaceURI "http://www.w3.org/1999/xlink"
@@ -197,16 +199,20 @@ NS_NewElement(nsIContent** aResult,
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, aNodeInfo);
   }
   if (ns == kNameSpaceID_SVG) {
     return NS_NewSVGElement(aResult, aNodeInfo, aFromParser);
   }
+  if (ns == kNameSpaceID_XBL && aNodeInfo.get()->Equals(nsGkAtoms::children)) {
+    NS_ADDREF(*aResult = new nsXBLChildrenElement(aNodeInfo));
+    return NS_OK;
+  }
   return NS_NewXMLElement(aResult, aNodeInfo);
 }
 
 bool
 NameSpaceManagerImpl::HasElementCreator(int32_t aNameSpaceID)
 {
   return aNameSpaceID == kNameSpaceID_XHTML ||
 #ifdef MOZ_XUL
--- a/content/xbl/src/moz.build
+++ b/content/xbl/src/moz.build
@@ -13,17 +13,17 @@ EXPORTS += [
 ]
 
 CPP_SOURCES += [
     'nsBindingManager.cpp',
     'nsXBLBinding.cpp',
     'nsXBLContentSink.cpp',
     'nsXBLDocumentInfo.cpp',
     'nsXBLEventHandler.cpp',
-    'nsXBLInsertionPoint.cpp',
+    'nsXBLChildrenElement.cpp',
     'nsXBLProtoImpl.cpp',
     'nsXBLProtoImplField.cpp',
     'nsXBLProtoImplMethod.cpp',
     'nsXBLProtoImplProperty.cpp',
     'nsXBLPrototypeBinding.cpp',
     'nsXBLPrototypeHandler.cpp',
     'nsXBLPrototypeResources.cpp',
     'nsXBLResourceLoader.cpp',
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -17,21 +17,22 @@
 #include "nsIDOMElement.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsIPresShell.h"
 #include "nsIXMLContentSink.h"
 #include "nsContentCID.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "nsIStreamListener.h"
+#include "ChildIterator.h"
 
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsXBLDocumentInfo.h"
-#include "nsXBLInsertionPoint.h"
+#include "nsXBLChildrenElement.h"
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsIWeakReference.h"
 
 #include "jsapi.h"
 #include "nsIXPConnect.h"
 #include "nsDOMCID.h"
@@ -43,178 +44,16 @@
 #include "nsBindingManager.h"
 #include "nsCxPusher.h"
 
 #include "nsThreadUtils.h"
 #include "mozilla/dom/NodeListBinding.h"
 
 using namespace mozilla;
 
-// ==================================================================
-// = nsAnonymousContentList 
-// ==================================================================
-
-#define NS_ANONYMOUS_CONTENT_LIST_IID \
-  { 0xbfb5d8e7, 0xf718, 0x4a46, \
-    { 0xb2, 0x2b, 0x22, 0x4a, 0x44, 0x4c, 0xb9, 0x77 } }
-
-class nsAnonymousContentList : public nsINodeList
-{
-public:
-  nsAnonymousContentList(nsIContent *aContent, nsInsertionPointList* aElements);
-  virtual ~nsAnonymousContentList();
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsAnonymousContentList)
-  // nsIDOMNodeList interface
-  NS_DECL_NSIDOMNODELIST
-
-  // nsINodeList interface
-  virtual int32_t IndexOf(nsIContent* aContent);
-  virtual nsINode *GetParentObject()
-  {
-    return mContent;
-  }
-  virtual nsIContent* Item(uint32_t aIndex);
-
-  int32_t GetInsertionPointCount() { return mElements->Length(); }
-
-  nsXBLInsertionPoint* GetInsertionPointAt(int32_t i) { return static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i)); }
-  void RemoveInsertionPointAt(int32_t i) { mElements->RemoveElementAt(i); }
-
-  virtual JSObject* WrapObject(JSContext *cx,
-                               JS::Handle<JSObject*> scope) MOZ_OVERRIDE
-  {
-    return mozilla::dom::NodeListBinding::Wrap(cx, scope, this);
-  }
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID)
-private:
-  nsCOMPtr<nsIContent> mContent;
-  nsInsertionPointList* mElements;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsAnonymousContentList,
-                              NS_ANONYMOUS_CONTENT_LIST_IID)
-
-nsAnonymousContentList::nsAnonymousContentList(nsIContent *aContent,
-                                               nsInsertionPointList* aElements)
-  : mContent(aContent),
-    mElements(aElements)
-{
-  MOZ_COUNT_CTOR(nsAnonymousContentList);
-
-  // We don't reference count our Anonymous reference (to avoid circular
-  // references). We'll be told when the Anonymous goes away.
-  SetIsDOMBinding();
-}
-
-nsAnonymousContentList::~nsAnonymousContentList()
-{
-  MOZ_COUNT_DTOR(nsAnonymousContentList);
-  delete mElements;
-}
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList)
-
-NS_INTERFACE_TABLE_HEAD(nsAnonymousContentList)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_TABLE3(nsAnonymousContentList, nsINodeList, nsIDOMNodeList,
-                      nsAnonymousContentList)
-  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAnonymousContentList)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAnonymousContentList)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
-  tmp->mElements->Clear();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
-  {
-    int32_t i, count = tmp->mElements->Length();
-    for (i = 0; i < count; ++i) {
-      NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements->ElementAt(i));
-    }
-  }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsAnonymousContentList)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMETHODIMP
-nsAnonymousContentList::GetLength(uint32_t* aLength)
-{
-  NS_ASSERTION(aLength != nullptr, "null ptr");
-  if (! aLength)
-      return NS_ERROR_NULL_POINTER;
-
-  int32_t cnt = mElements->Length();
-
-  *aLength = 0;
-  for (int32_t i = 0; i < cnt; i++)
-    *aLength += static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i))->ChildCount();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP    
-nsAnonymousContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
-{
-  nsINode* item = Item(aIndex);
-  if (!item)
-    return NS_ERROR_FAILURE;
-
-  return CallQueryInterface(item, aReturn);    
-}
-
-nsIContent*
-nsAnonymousContentList::Item(uint32_t aIndex)
-{
-  int32_t cnt = mElements->Length();
-  uint32_t pointCount = 0;
-
-  for (int32_t i = 0; i < cnt; i++) {
-    aIndex -= pointCount;
-    
-    nsXBLInsertionPoint* point = static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i));
-    pointCount = point->ChildCount();
-
-    if (aIndex < pointCount) {
-      return point->ChildAt(aIndex);
-    }
-  }
-
-  return nullptr;
-}
-
-int32_t
-nsAnonymousContentList::IndexOf(nsIContent* aContent)
-{
-  int32_t cnt = mElements->Length();
-  int32_t lengthSoFar = 0;
-
-  for (int32_t i = 0; i < cnt; ++i) {
-    nsXBLInsertionPoint* point =
-      static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i));
-    int32_t idx = point->IndexOf(aContent);
-    if (idx != -1) {
-      return idx + lengthSoFar;
-    }
-
-    lengthSoFar += point->ChildCount();
-  }
-
-  // Didn't find it anywhere
-  return -1;
-}
-
 //
 // Generic pldhash table stuff for mapping one nsISupports to another
 //
 // These values are never null - a null value implies that this
 // whole key should be removed (See SetOrRemoveObject)
 class ObjectEntry : public PLDHashEntryHdr
 {
 public:
@@ -352,24 +191,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mBindingTable.Clear();
 
   if (tmp->mDocumentTable.IsInitialized())
     tmp->mDocumentTable.Clear();
 
   if (tmp->mLoadingDocTable.IsInitialized())
     tmp->mLoadingDocTable.Clear();
 
-  if (tmp->mContentListTable.ops)
-    PL_DHashTableFinish(&(tmp->mContentListTable));
-  tmp->mContentListTable.ops = nullptr;
-
-  if (tmp->mAnonymousNodesTable.ops)
-    PL_DHashTableFinish(&(tmp->mAnonymousNodesTable));
-  tmp->mAnonymousNodesTable.ops = nullptr;
-
   if (tmp->mInsertionParentTable.ops)
     PL_DHashTableFinish(&(tmp->mInsertionParentTable));
   tmp->mInsertionParentTable.ops = nullptr;
 
   if (tmp->mWrapperTable.ops)
     PL_DHashTableFinish(&(tmp->mWrapperTable));
   tmp->mWrapperTable.ops = nullptr;
 
@@ -426,137 +257,81 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindi
 
 // Constructors/Destructors
 nsBindingManager::nsBindingManager(nsIDocument* aDocument)
   : mProcessingAttachedStack(false),
     mDestroyed(false),
     mAttachedStackSizeOnOutermost(0),
     mDocument(aDocument)
 {
-  mContentListTable.ops = nullptr;
-  mAnonymousNodesTable.ops = nullptr;
   mInsertionParentTable.ops = nullptr;
   mWrapperTable.ops = nullptr;
 }
 
 nsBindingManager::~nsBindingManager(void)
 {
   mDestroyed = true;
 
-  if (mContentListTable.ops)
-    PL_DHashTableFinish(&mContentListTable);
-  if (mAnonymousNodesTable.ops)
-    PL_DHashTableFinish(&mAnonymousNodesTable);
   NS_ASSERTION(!mInsertionParentTable.ops || !mInsertionParentTable.entryCount,
                "Insertion parent table isn't empty!");
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&mInsertionParentTable);
   if (mWrapperTable.ops)
     PL_DHashTableFinish(&mWrapperTable);
 }
 
-PLDHashOperator
-RemoveInsertionParentCB(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
-                        uint32_t aNumber, void* aArg)
-{
-  return (static_cast<ObjectEntry*>(aEntry)->GetValue() ==
-          static_cast<nsISupports*>(aArg)) ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
-}
-
-static void
-RemoveInsertionParentForNodeList(nsIDOMNodeList* aList, nsIContent* aParent)
-{
-  nsAnonymousContentList* list = nullptr;
-  if (aList) {
-    CallQueryInterface(aList, &list);
-  }
-  if (list) {
-    int32_t count = list->GetInsertionPointCount();
-    for (int32_t i = 0; i < count; ++i) {
-      nsRefPtr<nsXBLInsertionPoint> currPoint = list->GetInsertionPointAt(i);
-      currPoint->UnbindDefaultContent();
-#ifdef DEBUG
-      nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
-      NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
-#endif
-      currPoint->ClearInsertionParent();
-    }
-    NS_RELEASE(list);
-  }
-}
-
-void
-nsBindingManager::RemoveInsertionParent(nsIContent* aParent)
-{
-  RemoveInsertionParentForNodeList(GetContentListFor(aParent), aParent);
-
-  RemoveInsertionParentForNodeList(GetAnonymousNodesFor(aParent), aParent);
-
-  if (mInsertionParentTable.ops) {
-    PL_DHashTableEnumerate(&mInsertionParentTable, RemoveInsertionParentCB,
-                           static_cast<nsISupports*>(aParent));
-  }
-}
-
 nsXBLBinding*
 nsBindingManager::GetBinding(nsIContent* aContent)
 {
   if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
       mBindingTable.IsInitialized()) {
     return mBindingTable.GetWeak(aContent);
   }
 
   return nullptr;
 }
 
+nsXBLBinding*
+nsBindingManager::GetBindingWithContent(nsIContent* aContent)
+{
+  nsXBLBinding* binding = GetBinding(aContent);
+  return binding ? binding->GetBindingWithContent() : nullptr;
+}
+
 nsresult
 nsBindingManager::SetBinding(nsIContent* aContent, nsXBLBinding* aBinding)
 {
   if (!mBindingTable.IsInitialized()) {
     mBindingTable.Init();
   }
 
   // After this point, aBinding will be the most-derived binding for aContent.
   // If we already have a binding for aContent in our table, make sure to
   // remove it from the attached stack.  Otherwise we might end up firing its
   // constructor twice (if aBinding inherits from it) or firing its constructor
   // after aContent has been deleted (if aBinding is null and the content node
   // dies before we process mAttachedStack).
   nsRefPtr<nsXBLBinding> oldBinding = GetBinding(aContent);
   if (oldBinding) {
-    if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
-      nsRefPtr<nsXBLBinding> parentBinding =
-        GetBinding(aContent->GetBindingParent());
-      // Clear insertion parent only if we don't have a parent binding which
-      // marked content to be an insertion parent. See also ChangeDocumentFor().
-      if (!parentBinding || !parentBinding->HasInsertionParent(aContent)) {
-        RemoveInsertionParent(aContent);
-        aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
-      }
-    }
     // Don't remove items here as that could mess up an executing
     // ProcessAttachedQueue
     uint32_t index = mAttachedStack.IndexOf(oldBinding);
     if (index != mAttachedStack.NoIndex) {
       mAttachedStack[index] = nullptr;
     }
   }
-  
+
   if (aBinding) {
     aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
     mBindingTable.Put(aContent, aBinding);
   } else {
     mBindingTable.Remove(aContent);
 
-    // The death of the bindings means the death of the JS wrapper,
-    // and the flushing of our explicit and anonymous insertion point
-    // lists.
+    // The death of the bindings means the death of the JS wrapper.
     SetWrappedJS(aContent, nullptr);
-    SetContentListFor(aContent, nullptr);
-    SetAnonymousNodesFor(aContent, nullptr);
     if (oldBinding) {
       oldBinding->SetBoundElement(nullptr);
     }
   }
 
   return NS_OK;
 }
 
@@ -569,19 +344,16 @@ nsBindingManager::GetInsertionParent(nsI
   }
 
   return nullptr;
 }
 
 nsresult
 nsBindingManager::SetInsertionParent(nsIContent* aContent, nsIContent* aParent)
 {
-  NS_ASSERTION(!aParent || aParent->HasFlag(NODE_IS_INSERTION_PARENT),
-               "Insertion parent should have NODE_IS_INSERTION_PARENT flag!");
-
   if (mDestroyed) {
     return NS_OK;
   }
 
   return SetOrRemoveObject(mInsertionParentTable, aContent, aParent);
 }
 
 nsIXPConnectWrappedJS*
@@ -601,50 +373,32 @@ nsBindingManager::SetWrappedJS(nsIConten
     return NS_OK;
   }
 
   return SetOrRemoveObject(mWrapperTable, aContent, aWrappedJS);
 }
 
 void
 nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
-                                              nsIDocument* aOldDocument,
-                                              nsIContent* aContentBindingParent)
+                                              nsIDocument* aOldDocument)
 {
   NS_PRECONDITION(aOldDocument != nullptr, "no old document");
 
   if (mDestroyed)
     return;
 
-  // Hold a ref to the binding so it won't die when we remove it from our
-  // table.
   nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
-  if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
-    nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContentBindingParent);
-    if (parentBinding) {
-      parentBinding->RemoveInsertionParent(aContent);
-      // Clear insertion parent only if we don't have a binding which
-      // marked content to be an insertion parent. See also SetBinding().
-      if (!binding || !binding->HasInsertionParent(aContent)) {
-        RemoveInsertionParent(aContent);
-        aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
-      }
-    }
-  }
-
   if (binding) {
     binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
     binding->ChangeDocument(aOldDocument, nullptr);
     SetBinding(aContent, nullptr);
   }
 
   // Clear out insertion parents and content lists.
   SetInsertionParent(aContent, nullptr);
-  SetContentListFor(aContent, nullptr);
-  SetAnonymousNodesFor(aContent, nullptr);
 }
 
 nsIAtom*
 nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
 {
   nsXBLBinding *binding = GetBinding(aContent);
   
   if (binding) {
@@ -655,186 +409,28 @@ nsBindingManager::ResolveTag(nsIContent*
     }
   }
 
   *aNameSpaceID = aContent->GetNameSpaceID();
   return aContent->Tag();
 }
 
 nsresult
-nsBindingManager::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
-{
-  NS_IF_ADDREF(*aResult = GetContentListFor(aContent));
-  return NS_OK;
-}
-
-nsINodeList*
-nsBindingManager::GetContentListFor(nsIContent* aContent)
-{ 
-  nsINodeList* result = nullptr;
-
-  if (mContentListTable.ops) {
-    result = static_cast<nsAnonymousContentList*>
-      (LookupObject(mContentListTable, aContent));
-  }
-
-  if (!result) {
-    result = aContent->ChildNodes();
-  }
-
-  return result;
-}
-
-nsresult
-nsBindingManager::SetContentListFor(nsIContent* aContent,
-                                    nsInsertionPointList* aList)
-{
-  if (mDestroyed) {
-    return NS_OK;
-  }
-
-  nsAnonymousContentList* contentList = nullptr;
-  if (aList) {
-    contentList = new nsAnonymousContentList(aContent, aList);
-    if (!contentList) {
-      delete aList;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  return SetOrRemoveObject(mContentListTable, aContent, contentList);
-}
-
-bool
-nsBindingManager::HasContentListFor(nsIContent* aContent)
-{
-  return mContentListTable.ops && LookupObject(mContentListTable, aContent);
-}
-
-nsINodeList*
-nsBindingManager::GetAnonymousNodesInternal(nsIContent* aContent,
-                                            bool* aIsAnonymousContentList)
-{ 
-  nsINodeList* result = nullptr;
-  if (mAnonymousNodesTable.ops) {
-    result = static_cast<nsAnonymousContentList*>
-                        (LookupObject(mAnonymousNodesTable, aContent));
-  }
-
-  if (!result) {
-    *aIsAnonymousContentList = false;
-    nsXBLBinding *binding = GetBinding(aContent);
-    if (binding) {
-      result = binding->GetAnonymousNodes();
-    }
-  } else
-    *aIsAnonymousContentList = true;
-
-  return result;
-}
-
-nsresult
 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
                                        nsIDOMNodeList** aResult)
 {
-  bool dummy;
-  NS_IF_ADDREF(*aResult = GetAnonymousNodesInternal(aContent, &dummy));
+  NS_IF_ADDREF(*aResult = GetAnonymousNodesFor(aContent));
   return NS_OK;
 }
 
 nsINodeList*
 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
 {
-  bool dummy;
-  return GetAnonymousNodesInternal(aContent, &dummy);
-}
-
-nsresult
-nsBindingManager::SetAnonymousNodesFor(nsIContent* aContent,
-                                       nsInsertionPointList* aList)
-{
-  if (mDestroyed) {
-    return NS_OK;
-  }
-
-  nsAnonymousContentList* contentList = nullptr;
-  if (aList) {
-    contentList = new nsAnonymousContentList(aContent, aList);
-    if (!contentList) {
-      delete aList;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  return SetOrRemoveObject(mAnonymousNodesTable, aContent, contentList);
-}
-
-nsINodeList*
-nsBindingManager::GetXBLChildNodesInternal(nsIContent* aContent,
-                                           bool* aIsAnonymousContentList)
-{
-  uint32_t length;
-
-  // Retrieve the anonymous content that we should build.
-  nsINodeList* result = GetAnonymousNodesInternal(aContent,
-                                                  aIsAnonymousContentList);
-  if (result) {
-    result->GetLength(&length);
-    if (length == 0)
-      result = nullptr;
-  }
-    
-  // We may have an altered list of children from XBL insertion points.
-  // If we don't have any anonymous kids, we next check to see if we have 
-  // insertion points.
-  if (!result) {
-    if (mContentListTable.ops) {
-      result = static_cast<nsAnonymousContentList*>
-                          (LookupObject(mContentListTable, aContent));
-      *aIsAnonymousContentList = true;
-    }
-  }
-
-  return result;
-}
-
-nsresult
-nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
-{
-  NS_IF_ADDREF(*aResult = GetXBLChildNodesFor(aContent));
-  return NS_OK;
-}
-
-nsINodeList*
-nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent)
-{
-  bool dummy;
-  return GetXBLChildNodesInternal(aContent, &dummy);
-}
-
-nsIContent*
-nsBindingManager::GetInsertionPoint(nsIContent* aParent,
-                                    const nsIContent* aChild,
-                                    uint32_t* aIndex)
-{
-  nsXBLBinding *binding = GetBinding(aParent);
-  return binding ? binding->GetInsertionPoint(aChild, aIndex) : nullptr;
-}
-
-nsIContent*
-nsBindingManager::GetSingleInsertionPoint(nsIContent* aParent,
-                                          uint32_t* aIndex,
-                                          bool* aMultipleInsertionPoints)
-{
-  nsXBLBinding *binding = GetBinding(aParent);
-  if (binding)
-    return binding->GetSingleInsertionPoint(aIndex, aMultipleInsertionPoints);
-
-  *aMultipleInsertionPoints = false;
-  return nullptr;
+  nsXBLBinding* binding = GetBindingWithContent(aContent);
+  return binding ? binding->GetAnonymousNodeList() : nullptr;
 }
 
 nsresult
 nsBindingManager::ClearBinding(nsIContent* aContent)
 {
   // Hold a ref to the binding so it won't die when we remove it from our table
   nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
   
@@ -1367,282 +963,186 @@ void
 nsBindingManager::AppendAllSheets(nsTArray<nsCSSStyleSheet*>& aArray)
 {
   if (!mBindingTable.IsInitialized())
     return;
 
   mBindingTable.EnumerateRead(EnumAppendAllSheets, &aArray);
 }
 
-nsIContent*
-nsBindingManager::GetNestedInsertionPoint(nsIContent* aParent,
-                                          const nsIContent* aChild)
+static void
+InsertAppendedContent(nsBindingManager* aManager,
+                      nsXBLChildrenElement* aPoint,
+                      nsIContent* aFirstNewContent)
 {
-  // Check to see if the content is anonymous.
-  if (aChild->GetBindingParent() == aParent)
-    return nullptr; // It is anonymous. Don't use the insertion point, since that's only
-                   // for the explicit kids.
-
-  uint32_t index;
-  nsIContent *insertionElement = GetInsertionPoint(aParent, aChild, &index);
-  if (insertionElement && insertionElement != aParent) {
-    // See if we nest even further in.
-    nsIContent* nestedPoint = GetNestedInsertionPoint(insertionElement, aChild);
-    if (nestedPoint)
-      insertionElement = nestedPoint;
-  }
+  uint32_t insertionIndex;
+  if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) {
+    // If we have a previous sibling, then it must already be in aPoint. Find
+    // it and insert after it.
+    insertionIndex = aPoint->IndexOfInsertedChild(prevSibling);
+    MOZ_ASSERT(insertionIndex != aPoint->NoIndex);
 
-  return insertionElement;
-}
-
-nsIContent*
-nsBindingManager::GetNestedSingleInsertionPoint(nsIContent* aParent,
-                                                bool* aMultipleInsertionPoints)
-{
-  *aMultipleInsertionPoints = false;
-  
-  uint32_t index;
-  nsIContent *insertionElement =
-    GetSingleInsertionPoint(aParent, &index, aMultipleInsertionPoints);
-  if (*aMultipleInsertionPoints) {
-    return nullptr;
-  }
-  if (insertionElement && insertionElement != aParent) {
-    // See if we nest even further in.
-    nsIContent* nestedPoint =
-      GetNestedSingleInsertionPoint(insertionElement,
-                                    aMultipleInsertionPoints);
-    if (nestedPoint)
-      insertionElement = nestedPoint;
+    // Our insertion index is one after our previous sibling's index.
+    ++insertionIndex;
+  } else {
+    // Otherwise, we append.
+    // TODO This is wrong for nested insertion points. In that case, we need to
+    // keep track of the right index to insert into.
+    insertionIndex = aPoint->mInsertedChildren.Length();
   }
 
-  return insertionElement;
-}
-
-nsXBLInsertionPoint*
-nsBindingManager::FindInsertionPointAndIndex(nsIContent* aContainer,
-                                             nsIContent* aInsertionParent,
-                                             uint32_t aIndexInContainer,
-                                             int32_t aAppend,
-                                             int32_t* aInsertionIndex)
-{
-  bool isAnonymousContentList;
-  nsINodeList* nodeList =
-    GetXBLChildNodesInternal(aInsertionParent, &isAnonymousContentList);
-  if (!nodeList || !isAnonymousContentList) {
-    return nullptr;
+  // Do the inserting.
+  for (nsIContent* currentChild = aFirstNewContent;
+       currentChild;
+       currentChild = currentChild->GetNextSibling()) {
+    aPoint->InsertInsertedChildAt(currentChild, insertionIndex++, aManager);
   }
-
-  // Find a non-pseudo-insertion point and just jam ourselves in.  This is
-  // not 100% correct, since there might be multiple insertion points under
-  // this insertion parent, and we should really be using the one that
-  // matches our content...  Hack city, baby.
-  nsAnonymousContentList* contentList =
-    static_cast<nsAnonymousContentList*>(nodeList);
-
-  int32_t count = contentList->GetInsertionPointCount();
-  for (int32_t i = 0; i < count; i++) {
-    nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
-    if (point->GetInsertionIndex() != -1) {
-      // We're real. Jam the kid in.
-
-      // Find the right insertion spot.  Can't just insert in the insertion
-      // point at aIndexInContainer since the point may contain anonymous
-      // content, not all of aContainer's kids, etc.  So find the last
-      // child of aContainer that comes before aIndexInContainer and is in
-      // the insertion point and insert right after it.
-      int32_t pointSize = point->ChildCount();
-      for (int32_t parentIndex = aIndexInContainer - 1; parentIndex >= 0;
-           --parentIndex) {
-        nsIContent* currentSibling = aContainer->GetChildAt(parentIndex);
-        for (int32_t pointIndex = pointSize - 1; pointIndex >= 0;
-             --pointIndex) {
-          if (point->ChildAt(pointIndex) == currentSibling) {
-            *aInsertionIndex = pointIndex + 1;
-            return point;
-          }
-        }
-      }
-
-      // None of our previous siblings are in here... just stick
-      // ourselves in at the end of the insertion point if we're
-      // appending, and at the beginning otherwise.            
-      // XXXbz if we ever start doing the filter thing right, this may be no
-      // good, since we may _still_ have anonymous kids in there and may need
-      // to get the ordering with those right.  In fact, this is even wrong
-      // without the filter thing for nested insertion points, since they might
-      // contain anonymous content that needs to come after all explicit
-      // kids... but we have no way to know that here easily.
-      if (aAppend) {
-        *aInsertionIndex = pointSize;
-      } else {
-        *aInsertionIndex = 0;
-      }
-      return point;
-    }
-  }
-
-  return nullptr;  
 }
 
 void
 nsBindingManager::ContentAppended(nsIDocument* aDocument,
                                   nsIContent* aContainer,
                                   nsIContent* aFirstNewContent,
                                   int32_t     aNewIndexInContainer)
 {
-  if (aNewIndexInContainer != -1 &&
-      (mContentListTable.ops || mAnonymousNodesTable.ops)) {
-    // It's not anonymous.
-    NS_ASSERTION(aNewIndexInContainer >= 0, "Bogus index");
-
-    bool multiple;
-    nsIContent* ins = GetNestedSingleInsertionPoint(aContainer, &multiple);
+  if (aNewIndexInContainer == -1) {
+    return;
+  }
 
-    if (multiple) {
-      // Do each kid individually
-      int32_t childCount = aContainer->GetChildCount();
-      for (int32_t idx = aNewIndexInContainer; idx < childCount; ++idx) {
-        HandleChildInsertion(aContainer, aContainer->GetChildAt(idx),
-                             idx, true);
-      }
+  // Try to find insertion points for all the new kids.
+  nsXBLChildrenElement* point = nullptr;
+  nsIContent* parent = aContainer;
+  bool first = true;
+  do {
+    nsXBLBinding* binding = GetBindingWithContent(parent);
+    if (!binding) {
+      break;
     }
-    else if (ins) {
-      int32_t insertionIndex;
-      nsXBLInsertionPoint* point =
-        FindInsertionPointAndIndex(aContainer, ins, aNewIndexInContainer,
-                                   true, &insertionIndex);
-      if (point) {
-        int32_t childCount = aContainer->GetChildCount();
-        for (int32_t j = aNewIndexInContainer; j < childCount;
-             j++, insertionIndex++) {
-          nsIContent* child = aContainer->GetChildAt(j);
-          point->InsertChildAt(insertionIndex, child);
-          SetInsertionParent(child, ins);
-        }
+
+    if (binding->HasFilteredInsertionPoints()) {
+      // There are filtered insertion points involved, handle each child
+      // separately.
+      // We could optimize this in the case when we've nested a few levels
+      // deep already, without hitting bindings that have filtered insertion
+      // points.
+      int32_t currentIndex = aNewIndexInContainer;
+      for (nsIContent* currentChild = aFirstNewContent; currentChild;
+           currentChild = currentChild->GetNextSibling()) {
+        HandleChildInsertion(aContainer, currentChild,
+                             currentIndex++, true);
       }
+
+      return;
     }
-  }
+
+    point = binding->GetDefaultInsertionPoint();
+    if (!point) {
+      break;
+    }
+
+    // Even though we're in ContentAppended, nested insertion points force us
+    // to deal with this append as an insertion except in the outermost
+    // binding.
+    if (first) {
+      first = false;
+      for (nsIContent* child = aFirstNewContent; child;
+           child = child->GetNextSibling()) {
+        point->AppendInsertedChild(child, this);
+      }
+    } else {
+      InsertAppendedContent(this, point, aFirstNewContent);
+    }
+
+    nsIContent* newParent = point->GetParent();
+    if (newParent == parent) {
+      break;
+    }
+    parent = newParent;
+  } while (parent);
 }
 
 void
 nsBindingManager::ContentInserted(nsIDocument* aDocument,
                                   nsIContent* aContainer,
                                   nsIContent* aChild,
                                   int32_t aIndexInContainer)
 {
-  if (aIndexInContainer != -1 &&
-      (mContentListTable.ops || mAnonymousNodesTable.ops)) {
-    // It's not anonymous.
-    NS_ASSERTION(aIndexInContainer >= 0, "Bogus index");
-    HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
+  if (aIndexInContainer == -1) {
+    return;
   }
-}
 
-static void
-RemoveChildFromInsertionPoint(nsAnonymousContentList* aInsertionPointList,
-                              nsIContent* aChild,
-                              bool aRemoveFromPseudoPoints)
-{
-  // We need to find the insertion point that contains aChild and remove it
-  // from that insertion point.  Sadly, we don't know which point it is, or
-  // when we've hit it, but just trying to remove from all the pseudo or
-  // non-pseudo insertion points, depending on the value of
-  // aRemoveFromPseudoPoints, should work.
-
-  // XXXbz nsXBLInsertionPoint::RemoveChild could return whether it
-  // removed something.  Wouldn't that let us short-circuit the walk?
-  // Or can a child be in multiple insertion points?  I wouldn't think
-  // so...
-  int32_t count = aInsertionPointList->GetInsertionPointCount();
-  for (int32_t i = 0; i < count; i++) {
-    nsXBLInsertionPoint* point =
-      aInsertionPointList->GetInsertionPointAt(i);
-    if ((point->GetInsertionIndex() == -1) == aRemoveFromPseudoPoints) {
-      point->RemoveChild(aChild);
-    }
-  }
+  HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
 }
 
 void
 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
                                  nsIContent* aContainer,
                                  nsIContent* aChild,
                                  int32_t aIndexInContainer,
                                  nsIContent* aPreviousSibling)
 {
-  if (aContainer && aIndexInContainer != -1 &&
-      (mContentListTable.ops || mAnonymousNodesTable.ops)) {
-    // It's not anonymous
-    nsCOMPtr<nsIContent> point = GetNestedInsertionPoint(aContainer, aChild);
+  SetInsertionParent(aChild, nullptr);
 
-    if (point) {
-      bool isAnonymousContentList;
-      nsCOMPtr<nsIDOMNodeList> nodeList =
-        GetXBLChildNodesInternal(point, &isAnonymousContentList);
-      
-      if (nodeList && isAnonymousContentList) {
-        // Find a non-pseudo-insertion point and remove ourselves.
-        RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList*>
-                                        (static_cast<nsIDOMNodeList*>
-                                                    (nodeList)),
-                                      aChild,
-                                      false);
-        SetInsertionParent(aChild, nullptr);
+  nsXBLChildrenElement* point = nullptr;
+  nsIContent* parent = aContainer;
+  do {
+    nsXBLBinding* binding = GetBindingWithContent(parent);
+    if (!binding) {
+      // If aChild is XBL content, it might have <xbl:children> elements
+      // somewhere under it. We need to inform those elements that they're no
+      // longer in the tree so they can tell their distributed children that
+      // they're no longer distributed under them.
+      // XXX This is wrong. We need to do far more work to update the parent
+      // binding's list of insertion points and to get the new insertion parent
+      // for the newly-distributed children correct.
+      if (aChild->GetBindingParent()) {
+        ClearInsertionPointsRecursively(aChild);
       }
-
-      // Also remove from the list in mContentListTable, if any.
-      if (mContentListTable.ops) {
-        nsCOMPtr<nsIDOMNodeList> otherNodeList =
-          static_cast<nsAnonymousContentList*>
-                     (LookupObject(mContentListTable, point));
-        if (otherNodeList && otherNodeList != nodeList) {
-          // otherNodeList is always anonymous
-          RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList*>
-                                        (static_cast<nsIDOMNodeList*>
-                                                    (otherNodeList)),
-                                        aChild,
-                                        false);
-        }
-      }
+      return;
     }
 
-    // Whether the child has a nested insertion point or not, aContainer might
-    // have insertion points under it.  If that's the case, we need to remove
-    // aChild from the pseudo insertion point it's in.
-    if (mContentListTable.ops) {
-      nsAnonymousContentList* insertionPointList =
-        static_cast<nsAnonymousContentList*>(LookupObject(mContentListTable,
-                                                          aContainer));
-      if (insertionPointList) {
-        RemoveChildFromInsertionPoint(insertionPointList, aChild, true);
-      }
+    point = binding->FindInsertionPointFor(aChild);
+    if (!point) {
+      break;
+    }
+
+    point->RemoveInsertedChild(aChild);
+
+    nsIContent* newParent = point->GetParent();
+    if (newParent == parent) {
+      break;
     }
+    parent = newParent;
+  } while (parent);
+}
+
+void
+nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent)
+{
+  if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    static_cast<nsXBLChildrenElement*>(aContent)->ClearInsertedChildrenAndInsertionParents(this);
+  }
+
+  uint32_t childCount = aContent->GetChildCount();
+  for (uint32_t c = 0; c < childCount; c++) {
+    ClearInsertionPointsRecursively(aContent->GetChildAt(c));
   }
 }
 
 void
 nsBindingManager::DropDocumentReference()
 {
   mDestroyed = true;
 
   // Make sure to not run any more XBL constructors
   mProcessingAttachedStack = true;
   if (mProcessAttachedQueueEvent) {
     mProcessAttachedQueueEvent->Revoke();
   }
 
-  if (mContentListTable.ops)
-    PL_DHashTableFinish(&(mContentListTable));
-  mContentListTable.ops = nullptr;
-
-  if (mAnonymousNodesTable.ops)
-    PL_DHashTableFinish(&(mAnonymousNodesTable));
-  mAnonymousNodesTable.ops = nullptr;
-
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&(mInsertionParentTable));
   mInsertionParentTable.ops = nullptr;
 
   if (mBindingTable.IsInitialized())
     mBindingTable.Clear();
 
   mDocument = nullptr;
@@ -1671,30 +1171,16 @@ nsBindingManager::Traverse(nsIContent *a
   }
 
   nsXBLBinding *binding = GetBinding(aContent);
   if (binding) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBindingTable key");
     cb.NoteXPCOMChild(aContent);
     CycleCollectionNoteChild(cb, binding, "[via binding manager] mBindingTable value");
   }
-  if (mContentListTable.ops &&
-      (value = LookupObject(mContentListTable, aContent))) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mContentListTable key");
-    cb.NoteXPCOMChild(aContent);
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mContentListTable value");
-    cb.NoteXPCOMChild(value);
-  }
-  if (mAnonymousNodesTable.ops &&
-      (value = LookupObject(mAnonymousNodesTable, aContent))) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mAnonymousNodesTable key");
-    cb.NoteXPCOMChild(aContent);
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mAnonymousNodesTable value");
-    cb.NoteXPCOMChild(value);
-  }
   if (mWrapperTable.ops &&
       (value = LookupObject(mWrapperTable, aContent))) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
     cb.NoteXPCOMChild(aContent);
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
     cb.NoteXPCOMChild(value);
   }
 }
@@ -1720,21 +1206,128 @@ nsBindingManager::HandleChildInsertion(n
                                        uint32_t aIndexInContainer,
                                        bool aAppend)
 {
   NS_PRECONDITION(aChild, "Must have child");
   NS_PRECONDITION(!aContainer ||
                   uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
                   "Child not at the right index?");
 
-  nsIContent* ins = GetNestedInsertionPoint(aContainer, aChild);
+  nsXBLChildrenElement* point = nullptr;
+  nsIContent* parent = aContainer;
+  while (parent) {
+    nsXBLBinding* binding = GetBindingWithContent(parent);
+    if (!binding) {
+      break;
+    }
+
+    point = binding->FindInsertionPointFor(aChild);
+    if (!point) {
+      break;
+    }
 
-  if (ins) {
-    int32_t insertionIndex;
-    nsXBLInsertionPoint* point =
-      FindInsertionPointAndIndex(aContainer, ins, aIndexInContainer, aAppend,
-                                 &insertionIndex);
-    if (point) {
-      point->InsertChildAt(insertionIndex, aChild);
-      SetInsertionParent(aChild, ins);
+    // Insert the child into the proper insertion point.
+    // TODO If there were multiple insertion points, this approximation can be
+    // wrong. We need to re-run the distribution algorithm. In the meantime,
+    // this should work well enough.
+    uint32_t index = aAppend ? point->mInsertedChildren.Length() : 0;
+    for (nsIContent* currentSibling = aChild->GetPreviousSibling();
+         currentSibling;
+         currentSibling = currentSibling->GetPreviousSibling()) {
+      // If we find one of our previous siblings in the insertion point, the
+      // index following it is the correct insertion point. Otherwise, we guess
+      // based on whether we're appending or inserting.
+      uint32_t pointIndex = point->IndexOfInsertedChild(currentSibling);
+      if (pointIndex != point->NoIndex) {
+        index = pointIndex + 1;
+        break;
+      }
     }
+
+    point->InsertInsertedChildAt(aChild, index, this);
+
+    nsIContent* newParent = point->GetParent();
+    if (newParent == parent) {
+      break;
+    }
+
+    parent = newParent;
   }
 }
+
+
+nsIContent*
+nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
+                                           nsIContent* aChild)
+{
+  NS_PRECONDITION(aChild->GetParent() == aContainer,
+                  "Wrong container");
+
+  nsIContent* parent = aContainer;
+  if (aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    if (static_cast<nsXBLChildrenElement*>(aContainer)->
+          HasInsertedChildren()) {
+      return nullptr;
+    }
+    parent = aContainer->GetParent();
+  }
+
+  while (parent) {
+    nsXBLBinding* binding = GetBindingWithContent(parent);
+    if (!binding) {
+      break;
+    }
+  
+    nsXBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
+    if (!point) {
+      return nullptr;
+    }
+
+    nsIContent* newParent = point->GetParent();
+    if (newParent == parent) {
+      break;
+    }
+    parent = newParent;
+  }
+
+  return parent;
+}
+
+nsIContent*
+nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
+                                                 bool* aMulti)
+{
+  *aMulti = false;
+
+  nsIContent* parent = aContainer;
+  if (aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    if (static_cast<nsXBLChildrenElement*>(aContainer)->
+          HasInsertedChildren()) {
+      return nullptr;
+    }
+    parent = aContainer->GetParent();
+  }
+
+  while(parent) {
+    nsXBLBinding* binding = GetBindingWithContent(parent);
+    if (!binding) {
+      break;
+    }
+
+    if (binding->HasFilteredInsertionPoints()) {
+      *aMulti = true;
+      return nullptr;
+    }
+
+    nsXBLChildrenElement* point = binding->GetDefaultInsertionPoint();
+    if (!point) {
+      return nullptr;
+    }
+
+    nsIContent* newParent = point->GetParent();
+    if (newParent == parent) {
+      break;
+    }
+    parent = newParent;
+  }
+
+  return parent;
+}
--- a/content/xbl/src/nsBindingManager.h
+++ b/content/xbl/src/nsBindingManager.h
@@ -39,16 +39,17 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   nsBindingManager(nsIDocument* aDocument);
   ~nsBindingManager();
 
   nsXBLBinding* GetBinding(nsIContent* aContent);
+  nsXBLBinding* GetBindingWithContent(nsIContent* aContent);
   nsresult SetBinding(nsIContent* aContent, nsXBLBinding* aBinding);
 
   nsIContent* GetInsertionParent(nsIContent* aContent);
   nsresult SetInsertionParent(nsIContent* aContent, nsIContent* aResult);
 
   /**
    * Notify the binding manager that an element
    * has been removed from its document,
@@ -57,111 +58,33 @@ public:
    * content that may depend on the document.
    * @param aContent the element that's being moved
    * @param aOldDocument the old document in which the
    *   content resided.
    */
   void RemovedFromDocument(nsIContent* aContent, nsIDocument* aOldDocument)
   {
     if (aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-      RemovedFromDocumentInternal(aContent, aOldDocument,
-                                  aContent->GetBindingParent());
+      RemovedFromDocumentInternal(aContent, aOldDocument);
     }
   }
   void RemovedFromDocumentInternal(nsIContent* aContent,
-                                   nsIDocument* aOldDocument,
-                                   nsIContent* aContentBindingParent);
+                                   nsIDocument* aOldDocument);
 
   nsIAtom* ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID);
 
   /**
-   * Return a list of all explicit children, including any children
-   * that may have been inserted via XBL insertion points.
-   */
-  nsresult GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult);
-
-  /**
-   * Non-COMy version of GetContentListFor.
-   */
-  nsINodeList* GetContentListFor(nsIContent* aContent);
-
-  /**
-   * Set the insertion point children for the specified element.
-   * The binding manager assumes ownership of aList.
-   */
-  nsresult SetContentListFor(nsIContent* aContent,
-                             nsInsertionPointList* aList);
-
-  /**
-   * Determine whether or not the explicit child list has been altered
-   * by XBL insertion points.
-   */
-  bool HasContentListFor(nsIContent* aContent);
-
-  /**
    * Return the nodelist of "anonymous" kids for this node.  This might
    * actually include some of the nodes actual DOM kids, if there are
    * <children> tags directly as kids of <content>.  This will only end up
    * returning a non-null list for nodes which have a binding attached.
    */
   nsresult GetAnonymousNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult);
-
-  /**
-   * Same as above, but without the XPCOM goop
-   */
   nsINodeList* GetAnonymousNodesFor(nsIContent* aContent);
 
-  /**
-   * Set the anonymous child content for the specified element.
-   * The binding manager assumes ownership of aList.
-   */
-  nsresult SetAnonymousNodesFor(nsIContent* aContent,
-                                nsInsertionPointList* aList);
-
-  /**
-   * Retrieves the anonymous list of children if the element has one;
-   * otherwise, retrieves the list of explicit children. N.B. that if
-   * the explicit child list has not been altered by XBL insertion
-   * points, then aResult will be null.
-   */
-  nsresult GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult);
-
-  /**
-   * Non-COMy version of GetXBLChildNodesFor
-   */
-  nsINodeList* GetXBLChildNodesFor(nsIContent* aContent);
-
-  /**
-   * Given a parent element and a child content, determine where the
-   * child content should be inserted in the parent element's
-   * anonymous content tree. Specifically, aChild should be inserted
-   * beneath aResult at the index specified by aIndex.
-   */
-  // XXXbz That's false.  The aIndex doesn't seem to accurately reflect
-  // anything resembling reality in terms of inserting content.  It's really
-  // only used to tell apart two different insertion points with the same
-  // insertion parent when managing our internal data structures.  We really
-  // shouldn't be handing it out in our public API, since it's not useful to
-  // anyone.
-  nsIContent* GetInsertionPoint(nsIContent* aParent,
-                                const nsIContent* aChild, uint32_t* aIndex);
-
-  /**
-   * Return the unfiltered insertion point for the specified parent
-   * element. If other filtered insertion points exist,
-   * aMultipleInsertionPoints will be set to true.
-   */
-  nsIContent* GetSingleInsertionPoint(nsIContent* aParent, uint32_t* aIndex,
-                                      bool* aMultipleInsertionPoints);
-
-  nsIContent* GetNestedInsertionPoint(nsIContent* aParent,
-                                      const nsIContent* aChild);
-  nsIContent* GetNestedSingleInsertionPoint(nsIContent* aParent,
-                                            bool* aMultipleInsertionPoints);
-
   nsresult ClearBinding(nsIContent* aContent);
   nsresult LoadBindingDocument(nsIDocument* aBoundDoc, nsIURI* aURL,
                                nsIPrincipal* aOriginPrincipal);
 
   nsresult AddToAttachedQueue(nsXBLBinding* aBinding);
   void ProcessAttachedQueue(uint32_t aSkipSize = 0);
 
   void ExecuteDetachedHandlers();
@@ -200,80 +123,52 @@ public:
 
   NS_DECL_CYCLE_COLLECTION_CLASS(nsBindingManager)
 
   // Notify the binding manager when an outermost update begins and
   // ends.  The end method can execute script.
   void BeginOutermostUpdate();
   void EndOutermostUpdate();
 
+  // When removing an insertion point or a parent of one, clear the insertion
+  // points and their insertion parents.
+  void ClearInsertionPointsRecursively(nsIContent* aContent);
+
   // Called when the document is going away
   void DropDocumentReference();
 
+  nsIContent* FindNestedInsertionPoint(nsIContent* aContainer,
+                                       nsIContent* aChild);
+
+  nsIContent* FindNestedSingleInsertionPoint(nsIContent* aContainer, bool* aMulti);
+
 protected:
   nsIXPConnectWrappedJS* GetWrappedJS(nsIContent* aContent);
   nsresult SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aResult);
 
-  nsINodeList* GetXBLChildNodesInternal(nsIContent* aContent,
-                                        bool* aIsAnonymousContentList);
-  nsINodeList* GetAnonymousNodesInternal(nsIContent* aContent,
-                                         bool* aIsAnonymousContentList);
-
   // Called by ContentAppended and ContentInserted to handle a single child
   // insertion.  aChild must not be null.  aContainer may be null.
   // aIndexInContainer is the index of the child in the parent.  aAppend is
   // true if this child is being appended, not inserted.
   void HandleChildInsertion(nsIContent* aContainer, nsIContent* aChild,
                             uint32_t aIndexInContainer, bool aAppend);
 
-  // For the given container under which a child is being added, given
-  // insertion parent and given index of the child being inserted, find the
-  // right nsXBLInsertionPoint and the right index in that insertion point to
-  // insert it at.  If null is returned, aInsertionIndex might be garbage.
-  // aAppend controls what should be returned as the aInsertionIndex if the
-  // right index can't be found.  If true, the length of the insertion point
-  // will be returned; otherwise 0 will be returned.
-  nsXBLInsertionPoint* FindInsertionPointAndIndex(nsIContent* aContainer,
-                                                  nsIContent* aInsertionParent,
-                                                  uint32_t aIndexInContainer,
-                                                  int32_t aAppend,
-                                                  int32_t* aInsertionIndex);
-
   // Same as ProcessAttachedQueue, but also nulls out
   // mProcessAttachedQueueEvent
   void DoProcessAttachedQueue();
 
   // Post an event to process the attached queue.
   void PostProcessAttachedQueueEvent();
 
 // MEMBER VARIABLES
 protected: 
-  void RemoveInsertionParent(nsIContent* aParent);
   // A mapping from nsIContent* to the nsXBLBinding* that is
   // installed on that element.
   nsRefPtrHashtable<nsISupportsHashKey,nsXBLBinding> mBindingTable;
 
-  // A mapping from nsIContent* to an nsAnonymousContentList*.  This
-  // list contains an accurate reflection of our *explicit* children
-  // (once intermingled with insertion points) in the altered DOM.
-  // There is an entry for a content node in this table only if that
-  // content node has some <children> kids.
-  PLDHashTable mContentListTable;
-
-  // A mapping from nsIContent* to an nsAnonymousContentList*.  This
-  // list contains an accurate reflection of our *anonymous* children
-  // (if and only if they are intermingled with insertion points) in
-  // the altered DOM.  This table is not used if no insertion points
-  // were defined directly underneath a <content> tag in a binding.
-  // The NodeList from the <content> is used instead as a performance
-  // optimization.  There is an entry for a content node in this table
-  // only if that content node has a binding with a <content> attached
-  // and this <content> contains <children> elements directly.
-  PLDHashTable mAnonymousNodesTable;
-
   // A mapping from nsIContent* to nsIContent*.  The insertion parent
   // is our one true parent in the transformed DOM.  This gives us a
   // more-or-less O(1) way of obtaining our transformed parent.
   PLDHashTable mInsertionParentTable;
 
   // A mapping from nsIContent* to nsIXPWrappedJS* (an XPConnect
   // wrapper for JS objects).  For XBL bindings that implement XPIDL
   // interfaces, and that get referred to from C++, this table caches
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=79: */
 /* 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/. */
 
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsIInputStream.h"
@@ -14,26 +15,26 @@
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
+#include "ChildIterator.h"
 #include "nsCxPusher.h"
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif
 #include "nsIXMLContentSink.h"
 #include "nsContentCID.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "jsapi.h"
 #include "nsXBLService.h"
-#include "nsXBLInsertionPoint.h"
 #include "nsIXPConnect.h"
 #include "nsIScriptContext.h"
 #include "nsCRT.h"
 
 // Event listeners
 #include "nsEventListenerManager.h"
 #include "nsIDOMEventListener.h"
 #include "nsAttrName.h"
@@ -42,16 +43,17 @@
 
 #include "nsXBLPrototypeHandler.h"
 
 #include "nsXBLPrototypeBinding.h"
 #include "nsXBLBinding.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsGUIEvent.h"
+#include "nsXBLChildrenElement.h"
 
 #include "prprf.h"
 #include "nsNodeUtils.h"
 #include "nsJSUtils.h"
 
 // Nasty hack.  Maybe we could move some of the classinfo utility methods
 // (e.g. WrapNative) over to nsContentUtils?
 #include "nsDOMClassInfo.h"
@@ -137,85 +139,81 @@ nsXBLJSClass::Destroy()
 }
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mIsStyleBinding(true),
     mMarkedForDeath(false),
-    mPrototypeBinding(aBinding),
-    mInsertionPointTable(nullptr)
+    mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
 
 nsXBLBinding::~nsXBLBinding(void)
 {
   if (mContent) {
     nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
   }
-  delete mInsertionPointTable;
   nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
   NS_RELEASE(info);
 }
 
-static PLDHashOperator
-TraverseKey(nsISupports* aKey, nsInsertionPointList* aData, void* aClosure)
-{
-  nsCycleCollectionTraversalCallback &cb = 
-    *static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mInsertionPointTable key");
-  cb.NoteXPCOMChild(aKey);
-  if (aData) {
-    ImplCycleCollectionTraverse(cb, *aData, "mInsertionPointTable value");
-  }
-  return PL_DHASH_NEXT;
-}
-
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding)
   // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
   //     mPrototypeBinding is weak.
   if (tmp->mContent) {
     nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(),
                                             tmp->mContent);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding)
-  delete tmp->mInsertionPointTable;
-  tmp->mInsertionPointTable = nullptr;
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
                                      "mPrototypeBinding->XBLDocumentInfo()");
   cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
                       tmp->mPrototypeBinding->XBLDocumentInfo()));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding)
-  if (tmp->mInsertionPointTable)
-    tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
 
 void
 nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
 {
   if (mNextBinding) {
     NS_ERROR("Base XBL binding is already defined!");
     return;
   }
 
   mNextBinding = aBinding; // Comptr handles rel/add
 }
 
+nsXBLBinding*
+nsXBLBinding::GetBindingWithContent()
+{
+  if (mContent) {
+    return this;
+  }
+
+  return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr;
+}
+
 void
 nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement,
                                       bool aChromeOnlyContent)
 {
   // We need to ensure two things.
   // (1) The anonymous content should be fooled into thinking it's in the bound
   // element's document, assuming that the bound element is in a document
   // Note that we don't change the current doc of aAnonParent here, since that
@@ -295,197 +293,16 @@ nsXBLBinding::HasStyleSheets() const
   // Find out if we need to re-resolve style.  We'll need to do this
   // if we have additional stylesheets in our binding document.
   if (mPrototypeBinding->HasStyleSheets())
     return true;
 
   return mNextBinding ? mNextBinding->HasStyleSheets() : false;
 }
 
-struct EnumData {
-  nsXBLBinding* mBinding;
- 
-  EnumData(nsXBLBinding* aBinding)
-    :mBinding(aBinding)
-  {}
-};
-
-struct ContentListData : public EnumData {
-  nsBindingManager* mBindingManager;
-  nsresult          mRv;
-
-  ContentListData(nsXBLBinding* aBinding, nsBindingManager* aManager)
-    :EnumData(aBinding), mBindingManager(aManager), mRv(NS_OK)
-  {}
-};
-
-static PLDHashOperator
-BuildContentLists(nsISupports* aKey,
-                  nsAutoPtr<nsInsertionPointList>& aData,
-                  void* aClosure)
-{
-  ContentListData* data = (ContentListData*)aClosure;
-  nsBindingManager* bm = data->mBindingManager;
-  nsXBLBinding* binding = data->mBinding;
-
-  nsIContent *boundElement = binding->GetBoundElement();
-
-  int32_t count = aData->Length();
-  
-  if (count == 0)
-    return PL_DHASH_NEXT;
-
-  // Figure out the relevant content node.
-  nsXBLInsertionPoint* currPoint = aData->ElementAt(0);
-  nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
-  if (!parent) {
-    data->mRv = NS_ERROR_FAILURE;
-    return PL_DHASH_STOP;
-  }
-  int32_t currIndex = currPoint->GetInsertionIndex();
-
-  // XXX Could this array just be altered in place and passed directly to
-  // SetContentListFor?  We'd save space if we could pull this off.
-  nsInsertionPointList* contentList = new nsInsertionPointList;
-  if (!contentList) {
-    data->mRv = NS_ERROR_OUT_OF_MEMORY;
-    return PL_DHASH_STOP;
-  }
-
-  nsCOMPtr<nsIDOMNodeList> nodeList;
-  if (parent == boundElement) {
-    // We are altering anonymous nodes to accommodate insertion points.
-    nodeList = binding->GetAnonymousNodes();
-  }
-  else {
-    // We are altering the explicit content list of a node to accommodate insertion points.
-    nsCOMPtr<nsIDOMNode> node(do_QueryInterface(parent));
-    node->GetChildNodes(getter_AddRefs(nodeList));
-  }
-
-  nsXBLInsertionPoint* pseudoPoint = nullptr;
-  uint32_t childCount;
-  nodeList->GetLength(&childCount);
-  int32_t j = 0;
-
-  for (uint32_t i = 0; i < childCount; i++) {
-    nsCOMPtr<nsIDOMNode> node;
-    nodeList->Item(i, getter_AddRefs(node));
-    nsCOMPtr<nsIContent> child(do_QueryInterface(node));
-    if (((int32_t)i) == currIndex) {
-      // Add the currPoint to the insertion point list.
-      contentList->AppendElement(currPoint);
-
-      // Get the next real insertion point and update our currIndex.
-      j++;
-      if (j < count) {
-        currPoint = aData->ElementAt(j);
-        currIndex = currPoint->GetInsertionIndex();
-      }
-
-      // Null out our current pseudo-point.
-      pseudoPoint = nullptr;
-    }
-    
-    if (!pseudoPoint) {
-      pseudoPoint = new nsXBLInsertionPoint(parent, (uint32_t) -1, nullptr);
-      if (pseudoPoint) {
-        contentList->AppendElement(pseudoPoint);
-      }
-    }
-    if (pseudoPoint) {
-      pseudoPoint->AddChild(child);
-    }
-  }
-
-  // Add in all the remaining insertion points.
-  contentList->AppendElements(aData->Elements() + j, count - j);
-  
-  // Now set the content list using the binding manager,
-  // If the bound element is the parent, then we alter the anonymous node list
-  // instead.  This allows us to always maintain two distinct lists should
-  // insertion points be nested into an inner binding.
-  if (parent == boundElement)
-    bm->SetAnonymousNodesFor(parent, contentList);
-  else 
-    bm->SetContentListFor(parent, contentList);
-  return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator
-RealizeDefaultContent(nsISupports* aKey,
-                      nsAutoPtr<nsInsertionPointList>& aData,
-                      void* aClosure)
-{
-  ContentListData* data = (ContentListData*)aClosure;
-  nsBindingManager* bm = data->mBindingManager;
-  nsXBLBinding* binding = data->mBinding;
-
-  int32_t count = aData->Length();
- 
-  for (int32_t i = 0; i < count; i++) {
-    nsXBLInsertionPoint* currPoint = aData->ElementAt(i);
-    int32_t insCount = currPoint->ChildCount();
-    
-    if (insCount == 0) {
-      nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
-      if (defContent) {
-        // We need to take this template and use it to realize the
-        // actual default content (through cloning).
-        // Clone this insertion point element.
-        nsCOMPtr<nsIContent> insParent = currPoint->GetInsertionParent();
-        if (!insParent) {
-          data->mRv = NS_ERROR_FAILURE;
-          return PL_DHASH_STOP;
-        }
-        nsIDocument *document = insParent->OwnerDoc();
-        nsCOMPtr<nsINode> clonedNode;
-        nsCOMArray<nsINode> nodesWithProperties;
-        nsNodeUtils::Clone(defContent, true, document->NodeInfoManager(),
-                           nodesWithProperties, getter_AddRefs(clonedNode));
-
-        // Now that we have the cloned content, install the default content as
-        // if it were additional anonymous content.
-        nsCOMPtr<nsIContent> clonedContent(do_QueryInterface(clonedNode));
-        binding->InstallAnonymousContent(clonedContent, insParent,
-                                         binding->PrototypeBinding()->
-                                           ChromeOnlyContent());
-
-        // Cache the clone so that it can be properly destroyed if/when our
-        // other anonymous content is destroyed.
-        currPoint->SetDefaultContent(clonedContent);
-
-        // Now make sure the kids of the clone are added to the insertion point as
-        // children.
-        for (nsIContent* child = clonedContent->GetFirstChild();
-             child;
-             child = child->GetNextSibling()) {
-          bm->SetInsertionParent(child, insParent);
-          currPoint->AddChild(child);
-        }
-      }
-    }
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator
-ChangeDocumentForDefaultContent(nsISupports* aKey,
-                                nsAutoPtr<nsInsertionPointList>& aData,
-                                void* aClosure)
-{
-  int32_t count = aData->Length();
-  for (int32_t i = 0; i < count; i++) {
-    aData->ElementAt(i)->UnbindDefaultContent();
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 void
 nsXBLBinding::GenerateAnonymousContent()
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot a script blocker");
 
   // Fetch the content element for this binding.
   nsIContent* content =
@@ -500,179 +317,70 @@ nsXBLBinding::GenerateAnonymousContent()
   }
      
   // Find out if we're really building kids or if we're just
   // using the attribute-setting shorthand hack.
   uint32_t contentCount = content->GetChildCount();
 
   // Plan to build the content by default.
   bool hasContent = (contentCount > 0);
-  bool hasInsertionPoints = mPrototypeBinding->HasInsertionPoints();
-
-#ifdef DEBUG
-  // See if there's an includes attribute.
-  if (nsContentUtils::HasNonEmptyAttr(content, kNameSpaceID_None,
-                                      nsGkAtoms::includes)) {
-    nsAutoCString message("An XBL Binding with URI ");
-    nsAutoCString uri;
-    mPrototypeBinding->BindingURI()->GetSpec(uri);
-    message += uri;
-    message += " is still using the deprecated\n<content includes=\"\"> syntax! Use <children> instead!\n"; 
-    NS_WARNING(message.get());
-  }
-#endif
-
-  if (hasContent || hasInsertionPoints) {
+  if (hasContent) {
     nsIDocument* doc = mBoundElement->OwnerDoc();
     
     nsBindingManager *bindingManager = doc->BindingManager();
 
-    nsCOMPtr<nsIDOMNodeList> children;
-    bindingManager->GetContentListFor(mBoundElement, getter_AddRefs(children));
- 
-    nsCOMPtr<nsIDOMNode> node;
-    nsCOMPtr<nsIContent> childContent;
-    uint32_t length;
-    children->GetLength(&length);
-    if (length > 0 && !hasInsertionPoints) {
-      // There are children being placed underneath us, but we have no specified
-      // insertion points, and therefore no place to put the kids.  Don't generate
-      // anonymous content.
-      // Special case template and observes.
-      for (uint32_t i = 0; i < length; i++) {
-        children->Item(i, getter_AddRefs(node));
-        childContent = do_QueryInterface(node);
+    nsCOMPtr<nsINode> clonedNode;
+    nsCOMArray<nsINode> nodesWithProperties;
+    nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
+                       nodesWithProperties, getter_AddRefs(clonedNode));
+    mContent = clonedNode->AsElement();
 
-        nsINodeInfo *ni = childContent->NodeInfo();
-        nsIAtom *localName = ni->NameAtom();
-        if (ni->NamespaceID() != kNameSpaceID_XUL ||
-            (localName != nsGkAtoms::observes &&
-             localName != nsGkAtoms::_template)) {
-          hasContent = false;
-          break;
+    // Search for <xbl:children> elements in the XBL content. In the presence
+    // of multiple default insertion points, we use the last one in document
+    // order.
+    for (nsIContent* child = mContent; child; child = child->GetNextNode(mContent)) {
+      if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+        nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(child);
+        if (point->IsDefaultInsertion()) {
+          mDefaultInsertionPoint = point;
+        } else {
+          mInsertionPoints.AppendElement(point);
         }
       }
     }
 
-    if (hasContent || hasInsertionPoints) {
-      nsCOMPtr<nsINode> clonedNode;
-      nsCOMArray<nsINode> nodesWithProperties;
-      nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
-                         nodesWithProperties, getter_AddRefs(clonedNode));
-
-      mContent = do_QueryInterface(clonedNode);
-      InstallAnonymousContent(mContent, mBoundElement,
-                              mPrototypeBinding->ChromeOnlyContent());
-
-      if (hasInsertionPoints) {
-        // Now check and see if we have a single insertion point 
-        // or multiple insertion points.
-      
-        // Enumerate the prototype binding's insertion table to build
-        // our table of instantiated insertion points.
-        mPrototypeBinding->InstantiateInsertionPoints(this);
-
-        // We now have our insertion point table constructed.  We
-        // enumerate this table.  For each array of insertion points
-        // bundled under the same content node, we generate a content
-        // list.  In the case of the bound element, we generate a new
-        // anonymous node list that will be used in place of the binding's
-        // cached anonymous node list.
-        ContentListData data(this, bindingManager);
-        mInsertionPointTable->Enumerate(BuildContentLists, &data);
-        if (NS_FAILED(data.mRv)) {
-          return;
-        }
-
-        // We need to place the children
-        // at their respective insertion points.
-        uint32_t index = 0;
-        bool multiplePoints = false;
-        nsIContent *singlePoint = GetSingleInsertionPoint(&index,
-                                                          &multiplePoints);
-      
-        if (children) {
-          if (multiplePoints) {
-            // We must walk the entire content list in order to determine where
-            // each child belongs.
-            children->GetLength(&length);
-            for (uint32_t i = 0; i < length; i++) {
-              children->Item(i, getter_AddRefs(node));
-              childContent = do_QueryInterface(node);
-
-              // Now determine the insertion point in the prototype table.
-              uint32_t index;
-              nsIContent *point = GetInsertionPoint(childContent, &index);
-              bindingManager->SetInsertionParent(childContent, point);
+    // Do this after looking for <children> as this messes up the parent
+    // pointer which would make the GetNextNode call above fail
+    InstallAnonymousContent(mContent, mBoundElement,
+                            mPrototypeBinding->ChromeOnlyContent());
 
-              // Find the correct nsIXBLInsertion point in our table.
-              nsInsertionPointList* arr = nullptr;
-              GetInsertionPointsFor(point, &arr);
-              nsXBLInsertionPoint* insertionPoint = nullptr;
-              int32_t arrCount = arr->Length();
-              for (int32_t j = 0; j < arrCount; j++) {
-                insertionPoint = arr->ElementAt(j);
-                if (insertionPoint->Matches(point, index))
-                  break;
-                insertionPoint = nullptr;
-              }
-
-              if (insertionPoint) 
-                insertionPoint->AddChild(childContent);
-              else {
-                // We were unable to place this child.  All anonymous content
-                // should be thrown out.  Special-case template and observes.
-
-                nsINodeInfo *ni = childContent->NodeInfo();
-                nsIAtom *localName = ni->NameAtom();
-                if (ni->NamespaceID() != kNameSpaceID_XUL ||
-                    (localName != nsGkAtoms::observes &&
-                     localName != nsGkAtoms::_template)) {
-                  // Undo InstallAnonymousContent
-                  UninstallAnonymousContent(doc, mContent);
-
-                  // Kill all anonymous content.
-                  mContent = nullptr;
-                  bindingManager->SetContentListFor(mBoundElement, nullptr);
-                  bindingManager->SetAnonymousNodesFor(mBoundElement, nullptr);
-                  return;
-                }
-              }
-            }
-          }
-          else {
-            // All of our children are shunted to this single insertion point.
-            nsInsertionPointList* arr = nullptr;
-            GetInsertionPointsFor(singlePoint, &arr);
-            nsXBLInsertionPoint* insertionPoint = arr->ElementAt(0);
-        
-            nsCOMPtr<nsIDOMNode> node;
-            nsCOMPtr<nsIContent> content;
-            uint32_t length;
-            children->GetLength(&length);
-          
-            for (uint32_t i = 0; i < length; i++) {
-              children->Item(i, getter_AddRefs(node));
-              content = do_QueryInterface(node);
-              bindingManager->SetInsertionParent(content, singlePoint);
-              insertionPoint->AddChild(content);
-            }
-          }
-        }
-
-        // Now that all of our children have been added, we need to walk all of our
-        // nsIXBLInsertion points to see if any of them have default content that
-        // needs to be built.
-        mInsertionPointTable->Enumerate(RealizeDefaultContent, &data);
-        if (NS_FAILED(data.mRv)) {
-          return;
+    // Insert explicit children into insertion points
+    if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
+      ExplicitChildIterator iter(mBoundElement);
+      for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+        mDefaultInsertionPoint->AppendInsertedChild(child, bindingManager);
+      }
+    } else if (!mInsertionPoints.IsEmpty()) {
+      ExplicitChildIterator iter(mBoundElement);
+      for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+        nsXBLChildrenElement* point = FindInsertionPointForInternal(child);
+        if (point) {
+          point->AppendInsertedChild(child, bindingManager);
         }
       }
     }
 
+    // Set binding parent on default content if need
+    if (mDefaultInsertionPoint) {
+      mDefaultInsertionPoint->MaybeSetupDefaultContent(bindingManager);
+    }
+    for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
+      mInsertionPoints[i]->MaybeSetupDefaultContent(bindingManager);
+    }
+
     mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
   }
 
   // Always check the content element for potential attributes.
   // This shorthand hack always happens, even when we didn't
   // build anonymous content.
   const nsAttrName* attrName;
   for (uint32_t i = 0; (attrName = content->GetAttrNameAt(i)); ++i) {
@@ -691,16 +399,68 @@ nsXBLBinding::GenerateAnonymousContent()
     }
 
     // Conserve space by wiping the attributes off the clone.
     if (mContent)
       mContent->UnsetAttr(namespaceID, name, false);
   }
 }
 
+nsXBLChildrenElement*
+nsXBLBinding::FindInsertionPointFor(nsIContent* aChild)
+{
+  // XXX We should get rid of this function as it causes us to traverse the
+  // binding chain multiple times
+  if (mContent) {
+    return FindInsertionPointForInternal(aChild);
+  }
+  
+  return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
+                      : nullptr;
+}
+
+nsXBLChildrenElement*
+nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild)
+{
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
+    nsXBLChildrenElement* point = mInsertionPoints[i];
+    if (point->Includes(aChild)) {
+      return point;
+    }
+  }
+  
+  return mDefaultInsertionPoint;
+}
+
+void
+nsXBLBinding::ClearInsertionPoints()
+{
+  if (mDefaultInsertionPoint) {
+    mDefaultInsertionPoint->ClearInsertedChildren();
+  }
+
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
+    mInsertionPoints[i]->ClearInsertedChildren();
+  }
+}
+
+nsAnonymousContentList*
+nsXBLBinding::GetAnonymousNodeList()
+{
+  if (!mContent) {
+    return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr;
+  }
+
+  if (!mAnonymousContentList) {
+    mAnonymousContentList = new nsAnonymousContentList(mContent);
+  }
+
+  return mAnonymousContentList;
+}
+
 void
 nsXBLBinding::InstallEventHandlers()
 {
   // Don't install handlers if scripts aren't allowed.
   if (AllowScripts()) {
     // Fetch the handlers prototypes for this binding.
     nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
 
@@ -914,16 +674,45 @@ nsXBLBinding::UnhookEventHandlers()
         flags.mInSystemGroup = true;
       }
 
       manager->RemoveEventListenerByType(handler, type, flags);
     }
   }
 }
 
+static void
+UpdateInsertionParent(nsBindingManager* aBindingManager,
+                      nsXBLChildrenElement* aPoint,
+                      nsIContent* aOldBoundElement)
+{
+  if (aPoint->IsDefaultInsertion()) {
+    return;
+  }
+
+  for (size_t i = 0; i < aPoint->InsertedChildrenLength(); ++i) {
+    nsIContent* child = aPoint->mInsertedChildren[i];
+
+    MOZ_ASSERT(child->GetParentNode());
+
+    // Here, we're iterating children that we inserted. There are two cases:
+    // either |child| is an explicit child of |aOldBoundElement| and is no
+    // longer inserted anywhere or it's a child of a <children> element
+    // parented to |aOldBoundElement|. In the former case, the child is no
+    // longer inserted anywhere, so we set its insertion parent to null. In the
+    // latter case, the child is now inserted into |aOldBoundElement| from some
+    // binding above us, so we set its insertion parent to aOldBoundElement.
+    if (child->GetParentNode() == aOldBoundElement) {
+      aBindingManager->SetInsertionParent(child, nullptr);
+    } else {
+      aBindingManager->SetInsertionParent(child, aOldBoundElement);
+    }
+  }
+}
+
 void
 nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
 {
   if (aOldDocument == aNewDocument)
     return;
 
   // Only style bindings get their prototypes unhooked.  First do ourselves.
   if (mIsStyleBinding) {
@@ -1020,34 +809,38 @@ nsXBLBinding::ChangeDocument(nsIDocument
     // Then do our ancestors.  This reverses the construction order, so that at
     // all times things are consistent as far as everyone is concerned.
     if (mNextBinding) {
       mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
     }
 
     // Update the anonymous content.
     // XXXbz why not only for style bindings?
-    nsIContent *anonymous = mContent;
-    if (anonymous) {
-      // Also kill the default content within all our insertion points.
-      if (mInsertionPointTable)
-        mInsertionPointTable->Enumerate(ChangeDocumentForDefaultContent,
-                                        nullptr);
-
-      nsXBLBinding::UninstallAnonymousContent(aOldDocument, anonymous);
+    if (mContent) {
+      nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent);
     }
 
-    // Make sure that henceforth we don't claim that mBoundElement's children
-    // have insertion parents in the old document.
     nsBindingManager* bindingManager = aOldDocument->BindingManager();
-    for (nsIContent* child = mBoundElement->GetLastChild();
-         child;
-         child = child->GetPreviousSibling()) {
-      bindingManager->SetInsertionParent(child, nullptr);
+
+    // Now that we've unbound our anonymous content from the tree and updated
+    // its binding parent, update the insertion parent for content inserted
+    // into our <children> elements.
+    if (mDefaultInsertionPoint) {
+      UpdateInsertionParent(bindingManager, mDefaultInsertionPoint,
+                            mBoundElement);
     }
+
+    for (size_t i = 0; i < mInsertionPoints.Length(); ++i) {
+      UpdateInsertionParent(bindingManager, mInsertionPoints[i],
+                            mBoundElement);
+    }
+
+    // Now that our inserted children no longer think they're inserted
+    // anywhere, make sure our internal state reflects that as well.
+    ClearInsertionPoints();
   }
 }
 
 bool
 nsXBLBinding::InheritsStyle() const
 {
   // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
   // Most derived binding with anonymous content determines style inheritance for now.
@@ -1269,119 +1062,16 @@ nsXBLBinding::AllowScripts()
   nsCOMPtr<nsIDocument> ourDocument =
     mPrototypeBinding->XBLDocumentInfo()->GetDocument();
   bool canExecute;
   nsresult rv =
     mgr->CanExecuteScripts(cx, ourDocument->NodePrincipal(), &canExecute);
   return NS_SUCCEEDED(rv) && canExecute;
 }
 
-void
-nsXBLBinding::RemoveInsertionParent(nsIContent* aParent)
-{
-  if (mNextBinding) {
-    mNextBinding->RemoveInsertionParent(aParent);
-  }
-  if (mInsertionPointTable) {
-    nsInsertionPointList* list = nullptr;
-    mInsertionPointTable->Get(aParent, &list);
-    if (list) {
-      int32_t count = list->Length();
-      for (int32_t i = 0; i < count; ++i) {
-        nsRefPtr<nsXBLInsertionPoint> currPoint = list->ElementAt(i);
-        currPoint->UnbindDefaultContent();
-#ifdef DEBUG
-        nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
-        NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
-#endif
-        currPoint->ClearInsertionParent();
-      }
-      mInsertionPointTable->Remove(aParent);
-    }
-  }
-}
-
-bool
-nsXBLBinding::HasInsertionParent(nsIContent* aParent)
-{
-  if (mInsertionPointTable) {
-    nsInsertionPointList* list = nullptr;
-    mInsertionPointTable->Get(aParent, &list);
-    if (list) {
-      return true;
-    }
-  }
-  return mNextBinding ? mNextBinding->HasInsertionParent(aParent) : false;
-}
-
-void
-nsXBLBinding::GetInsertionPointsFor(nsIContent* aParent,
-                                    nsInsertionPointList** aResult)
-{
-  if (!mInsertionPointTable) {
-    mInsertionPointTable =
-      new nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>;
-    mInsertionPointTable->Init(4);
-  }
-
-  mInsertionPointTable->Get(aParent, aResult);
-
-  if (!*aResult) {
-    *aResult = new nsInsertionPointList;
-    mInsertionPointTable->Put(aParent, *aResult);
-    if (aParent) {
-      aParent->SetFlags(NODE_IS_INSERTION_PARENT);
-    }
-  }
-}
-
-nsInsertionPointList*
-nsXBLBinding::GetExistingInsertionPointsFor(nsIContent* aParent)
-{
-  if (!mInsertionPointTable) {
-    return nullptr;
-  }
-
-  nsInsertionPointList* result = nullptr;
-  mInsertionPointTable->Get(aParent, &result);
-  return result;
-}
-
-nsIContent*
-nsXBLBinding::GetInsertionPoint(const nsIContent* aChild, uint32_t* aIndex)
-{
-  if (mContent) {
-    return mPrototypeBinding->GetInsertionPoint(mBoundElement, mContent,
-                                                aChild, aIndex);
-  }
-
-  if (mNextBinding)
-    return mNextBinding->GetInsertionPoint(aChild, aIndex);
-
-  return nullptr;
-}
-
-nsIContent*
-nsXBLBinding::GetSingleInsertionPoint(uint32_t* aIndex,
-                                      bool* aMultipleInsertionPoints)
-{
-  *aMultipleInsertionPoints = false;
-  if (mContent) {
-    return mPrototypeBinding->GetSingleInsertionPoint(mBoundElement, mContent, 
-                                                      aIndex, 
-                                                      aMultipleInsertionPoints);
-  }
-
-  if (mNextBinding)
-    return mNextBinding->GetSingleInsertionPoint(aIndex,
-                                                 aMultipleInsertionPoints);
-
-  return nullptr;
-}
-
 nsXBLBinding*
 nsXBLBinding::RootBinding()
 {
   if (mNextBinding)
     return mNextBinding->RootBinding();
 
   return this;
 }
@@ -1507,21 +1197,8 @@ nsXBLBinding::MarkForDeath()
 }
 
 bool
 nsXBLBinding::ImplementsInterface(REFNSIID aIID) const
 {
   return mPrototypeBinding->ImplementsInterface(aIID) ||
     (mNextBinding && mNextBinding->ImplementsInterface(aIID));
 }
-
-nsINodeList*
-nsXBLBinding::GetAnonymousNodes()
-{
-  if (mContent) {
-    return mContent->ChildNodes();
-  }
-
-  if (mNextBinding)
-    return mNextBinding->GetAnonymousNodes();
-
-  return nullptr;
-}
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -17,19 +17,18 @@
 #include "nsISupportsImpl.h"
 #include "jsapi.h"
 
 class nsXBLPrototypeBinding;
 class nsIContent;
 class nsIAtom;
 class nsIDocument;
 class nsIScriptContext;
-class nsObjectHashtable;
-class nsXBLInsertionPoint;
-typedef nsTArray<nsRefPtr<nsXBLInsertionPoint> > nsInsertionPointList;
+class nsXBLChildrenElement;
+class nsAnonymousContentList;
 struct JSContext;
 class JSObject;
 
 // *********************************************************************/
 // The XBLBinding class
 
 class nsXBLBinding
 {
@@ -48,16 +47,17 @@ public:
    */
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXBLBinding)
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding)
 
   nsXBLPrototypeBinding* PrototypeBinding() { return mPrototypeBinding; }
   nsIContent* GetAnonymousContent() { return mContent.get(); }
+  nsXBLBinding* GetBindingWithContent();
 
   nsXBLBinding* GetBaseBinding() { return mNextBinding; }
   void SetBaseBinding(nsXBLBinding *aBinding);
 
   nsIContent* GetBoundElement() { return mBoundElement; }
   void SetBoundElement(nsIContent *aElement);
 
   void SetJSClass(nsXBLJSClass *aClass) {
@@ -115,63 +115,72 @@ public:
   nsIAtom* GetBaseTag(int32_t* aNameSpaceID);
   nsXBLBinding* RootBinding();
   nsXBLBinding* GetFirstStyleBinding();
 
   // Resolve all the fields for this binding and all ancestor bindings on the
   // object |obj|.  False return means a JS exception was set.
   bool ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const;
 
-  // Get the list of insertion points for aParent. The nsInsertionPointList
-  // is owned by the binding, you should not delete it.
-  void GetInsertionPointsFor(nsIContent* aParent,
-                             nsInsertionPointList** aResult);
-
-  nsInsertionPointList* GetExistingInsertionPointsFor(nsIContent* aParent);
-
-  // XXXbz this aIndex has nothing to do with an index into the child
-  // list of the insertion parent or anything.
-  nsIContent* GetInsertionPoint(const nsIContent* aChild, uint32_t* aIndex);
-
-  nsIContent* GetSingleInsertionPoint(uint32_t* aIndex,
-                                      bool* aMultipleInsertionPoints);
-
   void AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID,
                         bool aRemoveFlag, bool aNotify);
 
   void ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument);
 
   void WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData);
 
-  nsINodeList* GetAnonymousNodes();
-
   static nsresult DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
                                 JS::Handle<JSObject*> obj,
                                 const nsAFlatCString& aClassName,
                                 nsXBLPrototypeBinding* aProtoBinding,
                                 JS::MutableHandle<JSObject*> aClassObject,
                                 bool* aNew);
 
   bool AllowScripts();  // XXX make const
 
-  void RemoveInsertionParent(nsIContent* aParent);
-  bool HasInsertionParent(nsIContent* aParent);
+  nsXBLChildrenElement* FindInsertionPointFor(nsIContent* aChild);
+
+  bool HasFilteredInsertionPoints()
+  {
+    return !mInsertionPoints.IsEmpty();
+  }
+
+  nsXBLChildrenElement* GetDefaultInsertionPoint()
+  {
+    return mDefaultInsertionPoint;
+  }
+
+  // Removes all inserted node from <xbl:children> insertion points under us.
+  void ClearInsertionPoints();
+
+  // Returns a live node list that iterates over the anonymous nodes generated
+  // by this binding.
+  nsAnonymousContentList* GetAnonymousNodeList();
 
 // MEMBER VARIABLES
 protected:
 
   bool mIsStyleBinding;
   bool mMarkedForDeath;
 
   nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
   nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
   nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
   nsRefPtr<nsXBLJSClass> mJSClass; // Strong. The class object also holds a strong reference,
                                    // which might be somewhat redundant, but be safe to avoid
                                    // worrying about edge cases.
-  
+
   nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
-  
-  // A hash from nsIContent* -> (a sorted array of nsXBLInsertionPoint)
-  nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>* mInsertionPointTable;
+
+  // The <xbl:children> elements that we found in our <xbl:content> when we
+  // processed this binding. The default insertion point has no includes
+  // attribute and all other insertion points must have at least one includes
+  // attribute. These points must be up-to-date with respect to their parent's
+  // children, even if their parent has another binding attached to it,
+  // preventing us from rendering their contents directly.
+  nsRefPtr<nsXBLChildrenElement> mDefaultInsertionPoint;
+  nsTArray<nsRefPtr<nsXBLChildrenElement> > mInsertionPoints;
+  nsRefPtr<nsAnonymousContentList> mAnonymousContentList;
+
+  nsXBLChildrenElement* FindInsertionPointForInternal(nsIContent* aChild);
 };
 
 #endif // nsXBLBinding_h_
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLChildrenElement.cpp
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=79: */
+/* 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/. */
+
+#include "nsXBLChildrenElement.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "mozilla/dom/NodeListBinding.h"
+
+nsXBLChildrenElement::~nsXBLChildrenElement()
+{
+}
+
+NS_IMPL_ADDREF_INHERITED(nsXBLChildrenElement, Element)
+NS_IMPL_RELEASE_INHERITED(nsXBLChildrenElement, Element)
+
+NS_INTERFACE_TABLE_HEAD(nsXBLChildrenElement)
+  NS_INTERFACE_TABLE_INHERITED2(nsXBLChildrenElement, nsIDOMNode,
+                                                      nsIDOMElement)
+  NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
+NS_INTERFACE_MAP_END_INHERITING(Element)
+
+NS_IMPL_ELEMENT_CLONE(nsXBLChildrenElement)
+
+nsIAtom*
+nsXBLChildrenElement::GetIDAttributeName() const
+{
+  return nullptr;
+}
+
+nsIAtom*
+nsXBLChildrenElement::DoGetID() const
+{
+  return nullptr;
+}
+
+nsresult
+nsXBLChildrenElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                                bool aNotify)
+{
+  if (aAttribute == nsGkAtoms::includes &&
+      aNameSpaceID == kNameSpaceID_None) {
+    mIncludes.Clear();
+  }
+
+  return Element::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
+}
+
+bool
+nsXBLChildrenElement::ParseAttribute(int32_t aNamespaceID,
+                                     nsIAtom* aAttribute,
+                                     const nsAString& aValue,
+                                     nsAttrValue& aResult)
+{
+  if (aAttribute == nsGkAtoms::includes &&
+      aNamespaceID == kNameSpaceID_None) {
+    mIncludes.Clear();
+    nsCharSeparatedTokenizer tok(aValue, '|',
+                                 nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
+    while (tok.hasMoreTokens()) {
+      mIncludes.AppendElement(do_GetAtom(tok.nextToken()));
+    }
+  }
+
+  return false;
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsAnonymousContentList, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList)
+
+NS_INTERFACE_TABLE_HEAD(nsAnonymousContentList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_TABLE_INHERITED3(nsAnonymousContentList, nsINodeList,
+                                                        nsIDOMNodeList,
+                                                        nsAnonymousContentList)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+nsAnonymousContentList::GetLength(uint32_t* aLength)
+{
+  if (!mParent) {
+    *aLength = 0;
+    return NS_OK;
+  }
+
+  uint32_t count = 0;
+  for (nsIContent* child = mParent->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+      nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(child);
+      if (!point->mInsertedChildren.IsEmpty()) {
+        count += point->mInsertedChildren.Length();
+      }
+      else {
+        count += point->GetChildCount();
+      }
+    }
+    else {
+      ++count;
+    }
+  }
+
+  *aLength = count;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAnonymousContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
+{
+  nsIContent* item = Item(aIndex);
+  if (!item) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return CallQueryInterface(item, aReturn);
+}
+
+nsIContent*
+nsAnonymousContentList::Item(uint32_t aIndex)
+{
+  if (!mParent) {
+    return nullptr;
+  }
+
+  uint32_t remIndex = aIndex;
+  for (nsIContent* child = mParent->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+      nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(child);
+      if (!point->mInsertedChildren.IsEmpty()) {
+        if (remIndex < point->mInsertedChildren.Length()) {
+          return point->mInsertedChildren[remIndex];
+        }
+        remIndex -= point->mInsertedChildren.Length();
+      }
+      else {
+        if (remIndex < point->GetChildCount()) {
+          return point->GetChildAt(remIndex);
+        }
+        remIndex -= point->GetChildCount();
+      }
+    }
+    else {
+      if (remIndex == 0) {
+        return child;
+      }
+      --remIndex;
+    }
+  }
+
+  return nullptr;
+}
+
+int32_t
+nsAnonymousContentList::IndexOf(nsIContent* aContent)
+{
+  NS_ASSERTION(!aContent->NodeInfo()->Equals(nsGkAtoms::children,
+                                             kNameSpaceID_XBL),
+               "Looking for insertion point");
+
+  if (!mParent) {
+    return -1;
+  }
+
+  uint32_t index = 0;
+  for (nsIContent* child = mParent->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+      nsXBLChildrenElement* point = static_cast<nsXBLChildrenElement*>(child);
+      if (!point->mInsertedChildren.IsEmpty()) {
+        uint32_t insIndex = point->mInsertedChildren.IndexOf(aContent);
+        if (insIndex != point->mInsertedChildren.NoIndex) {
+          return index + insIndex;
+        }
+        index += point->mInsertedChildren.Length();
+      }
+      else {
+        int32_t insIndex = point->IndexOf(aContent);
+        if (insIndex != -1) {
+          return index + (uint32_t)insIndex;
+        }
+        index += point->GetChildCount();
+      }
+    }
+    else {
+      if (child == aContent) {
+        return index;
+      }
+      ++index;
+    }
+  }
+
+  return -1;
+}
+
+JSObject*
+nsAnonymousContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
+{
+  return mozilla::dom::NodeListBinding::Wrap(cx, scope, this);
+}
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLChildrenElement.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=79: */
+/* 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/. */
+
+#ifndef nsXBLChildrenElement_h___
+#define nsXBLChildrenElement_h___
+
+#include "nsIDOMElement.h"
+#include "nsINodeList.h"
+#include "nsBindingManager.h"
+#include "mozilla/dom/nsXMLElement.h"
+
+namespace mozilla {
+namespace dom {
+
+class ExplicitChildIterator;
+
+}
+}
+
+class nsAnonymousContentList;
+
+class nsXBLChildrenElement : public nsXMLElement
+{
+public:
+  friend class mozilla::dom::ExplicitChildIterator;
+  friend class nsAnonymousContentList;
+  nsXBLChildrenElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+    : nsXMLElement(aNodeInfo)
+  {
+  }
+  ~nsXBLChildrenElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsINode interface methods
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsXPCClassInfo* GetClassInfo() { return nullptr; }
+
+  virtual nsIDOMNode* AsDOMNode() { return this; }
+
+  // nsIContent interface methods
+  virtual nsIAtom *GetIDAttributeName() const;
+  virtual nsIAtom* DoGetID() const;
+  virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                             bool aNotify);
+  virtual bool ParseAttribute(int32_t aNamespaceID,
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
+
+  void AppendInsertedChild(nsIContent* aChild,
+                           nsBindingManager* aBindingManager)
+  {
+    mInsertedChildren.AppendElement(aChild);
+    aBindingManager->SetInsertionParent(aChild, GetParent());
+  }
+
+  void InsertInsertedChildAt(nsIContent* aChild,
+                             uint32_t aIndex,
+                             nsBindingManager* aBindingManager)
+  {
+    mInsertedChildren.InsertElementAt(aIndex, aChild);
+    aBindingManager->SetInsertionParent(aChild, GetParent());
+  }
+
+  void RemoveInsertedChild(nsIContent* aChild)
+  {
+    // Can't use this assertion as we cheat for dynamic insertions and
+    // only insert in the innermost insertion point.
+    //NS_ASSERTION(mInsertedChildren.Contains(aChild),
+    //             "Removing child that's not there");
+    mInsertedChildren.RemoveElement(aChild);
+  }
+
+  void ClearInsertedChildren()
+  {
+    mInsertedChildren.Clear();
+  }
+
+  void ClearInsertedChildrenAndInsertionParents(nsBindingManager* aBindingManager)
+  {
+    for (uint32_t c = 0; c < mInsertedChildren.Length(); ++c) {
+      aBindingManager->SetInsertionParent(mInsertedChildren[c], nullptr);
+    }
+    mInsertedChildren.Clear();
+  }
+
+  void MaybeSetupDefaultContent(nsBindingManager* aBindingManager)
+  {
+    if (!HasInsertedChildren()) {
+      for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
+           child;
+           child = child->GetNextSibling()) {
+        aBindingManager->SetInsertionParent(child, GetParent());
+      }
+    }
+  }
+
+  void MaybeRemoveDefaultContent(nsBindingManager* aBindingManager)
+  {
+    if (!HasInsertedChildren()) {
+      for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
+           child;
+           child = child->GetNextSibling()) {
+        aBindingManager->SetInsertionParent(child, nullptr);
+      }
+    }
+  }
+
+  uint32_t InsertedChildrenLength()
+  {
+    return mInsertedChildren.Length();
+  }
+
+  bool HasInsertedChildren()
+  {
+    return !mInsertedChildren.IsEmpty();
+  }
+
+  enum {
+    NoIndex = uint32_t(-1)
+  };
+  uint32_t IndexOfInsertedChild(nsIContent* aChild)
+  {
+    return mInsertedChildren.IndexOf(aChild);
+  }
+
+  bool Includes(nsIContent* aChild)
+  {
+    NS_ASSERTION(!mIncludes.IsEmpty(),
+                 "Shouldn't check for includes on default insertion point");
+    return mIncludes.Contains(aChild->Tag());
+  }
+
+  bool IsDefaultInsertion()
+  {
+    return mIncludes.IsEmpty();
+  }
+
+  nsTArray<nsIContent*> mInsertedChildren;
+
+private:
+  nsTArray<nsCOMPtr<nsIAtom> > mIncludes;
+};
+
+class nsAnonymousContentList : public nsINodeList
+{
+public:
+  nsAnonymousContentList(nsIContent* aParent)
+    : mParent(aParent)
+  {
+    MOZ_COUNT_CTOR(nsAnonymousContentList);
+    SetIsDOMBinding();
+  }
+
+  virtual ~nsAnonymousContentList()
+  {
+    MOZ_COUNT_DTOR(nsAnonymousContentList);
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsAnonymousContentList)
+  // nsIDOMNodeList interface
+  NS_DECL_NSIDOMNODELIST
+
+  // nsINodeList interface
+  virtual int32_t IndexOf(nsIContent* aContent);
+  virtual nsINode* GetParentObject() { return mParent; }
+  virtual nsIContent* Item(uint32_t aIndex);
+
+  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
+
+  bool IsListFor(nsIContent* aContent) {
+    return mParent == aContent;
+  }
+
+private:
+  nsCOMPtr<nsIContent> mParent;
+};
+
+#endif // nsXBLChildrenElement_h___
deleted file mode 100644
--- a/content/xbl/src/nsXBLInsertionPoint.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "nsXBLInsertionPoint.h"
-#include "nsXBLBinding.h"
-
-nsXBLInsertionPoint::nsXBLInsertionPoint(nsIContent* aParentElement,
-                                         uint32_t aIndex,
-                                         nsIContent* aDefaultContent)
-  : mParentElement(aParentElement),
-    mIndex(aIndex),
-    mDefaultContentTemplate(aDefaultContent)
-{
-}
-
-nsXBLInsertionPoint::~nsXBLInsertionPoint()
-{
-  if (mDefaultContent) {
-    nsXBLBinding::UninstallAnonymousContent(mDefaultContent->OwnerDoc(),
-                                            mDefaultContent);
-  }
-}
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLInsertionPoint)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultContentTemplate)
-  if (tmp->mDefaultContent) {
-    nsXBLBinding::UninstallAnonymousContent(tmp->mDefaultContent->OwnerDoc(),
-                                            tmp->mDefaultContent);
-  }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultContent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLInsertionPoint)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultContentTemplate)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultContent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLInsertionPoint, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLInsertionPoint, Release)
-
-nsIContent*
-nsXBLInsertionPoint::GetInsertionParent()
-{
-  return mParentElement;
-}
-
-nsIContent*
-nsXBLInsertionPoint::GetDefaultContent()
-{
-  return mDefaultContent;
-}
-
-nsIContent*
-nsXBLInsertionPoint::GetDefaultContentTemplate()
-{
-  return mDefaultContentTemplate;
-}
-
-nsIContent*
-nsXBLInsertionPoint::ChildAt(uint32_t aIndex)
-{
-  return mElements.ObjectAt(aIndex);
-}
-
-bool
-nsXBLInsertionPoint::Matches(nsIContent* aContent, uint32_t aIndex)
-{
-  return (aContent == mParentElement && mIndex != -1 && ((int32_t)aIndex) == mIndex);
-}
-
-void
-nsXBLInsertionPoint::UnbindDefaultContent()
-{
-  if (!mDefaultContent) {
-    return;
-  }
-
-  // Undo InstallAnonymousContent.
-  nsXBLBinding::UninstallAnonymousContent(mDefaultContent->OwnerDoc(),
-                                          mDefaultContent);
-}
deleted file mode 100644
--- a/content/xbl/src/nsXBLInsertionPoint.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef nsXBLInsertionPoint_h__
-#define nsXBLInsertionPoint_h__
-
-#include "nsCOMArray.h"
-#include "nsIContent.h"
-#include "nsCOMPtr.h"
-#include "nsCycleCollectionParticipant.h"
-
-class nsXBLInsertionPoint
-{
-public:
-  nsXBLInsertionPoint(nsIContent* aParentElement, uint32_t aIndex, nsIContent* aDefContent);
-  ~nsXBLInsertionPoint();
-
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXBLInsertionPoint)
-
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPoint)
-
-  nsIContent* GetInsertionParent();
-  void ClearInsertionParent() { mParentElement = nullptr; }
-
-  int32_t GetInsertionIndex() { return mIndex; }
-
-  void SetDefaultContent(nsIContent* aDefaultContent) { mDefaultContent = aDefaultContent; }
-  nsIContent* GetDefaultContent();
-
-  void SetDefaultContentTemplate(nsIContent* aDefaultContent) { mDefaultContentTemplate = aDefaultContent; }
-  nsIContent* GetDefaultContentTemplate();
-
-  void AddChild(nsIContent* aChildElement) { mElements.AppendObject(aChildElement); }
-  void InsertChildAt(int32_t aIndex, nsIContent* aChildElement) { mElements.InsertObjectAt(aChildElement, aIndex); }
-  void RemoveChild(nsIContent* aChildElement) { mElements.RemoveObject(aChildElement); }
-  
-  int32_t ChildCount() { return mElements.Count(); }
-
-  nsIContent* ChildAt(uint32_t aIndex);
-
-  int32_t IndexOf(nsIContent* aContent) { return mElements.IndexOf(aContent); }
-
-  bool Matches(nsIContent* aContent, uint32_t aIndex);
-
-  // Unbind all the default content in this insertion point.  Used
-  // when the insertion parent is going away.
-  void UnbindDefaultContent();
-
-protected:
-  nsIContent* mParentElement;            // This ref is weak.  The parent of the <children> element.
-  int32_t mIndex;                        // The index of this insertion point. -1 is a pseudo-point.
-  nsCOMArray<nsIContent> mElements;      // An array of elements present at the insertion point.
-  nsCOMPtr<nsIContent> mDefaultContentTemplate ;           // The template default content that will be cloned if
-                                                           // the insertion point is empty.
-  nsCOMPtr<nsIContent> mDefaultContent;  // The cloned default content obtained by cloning mDefaultContentTemplate.
-};
-
-#endif
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -18,17 +18,16 @@
 #include "plstr.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIDocument.h"
 #include "nsIXMLContentSink.h"
 #include "nsContentCID.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "nsXBLService.h"
 #include "nsXBLBinding.h"
-#include "nsXBLInsertionPoint.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsXBLContentSink.h"
 #include "xptinfo.h"
 #include "nsIInterfaceInfoManager.h"
 #include "nsIDocumentObserver.h"
 #include "nsGkAtoms.h"
 #include "nsXBLProtoImpl.h"
 #include "nsCRT.h"
@@ -107,88 +106,30 @@ protected:
   nsIContent* mElement;
 
   nsCOMPtr<nsIAtom> mSrcAttribute;
   nsCOMPtr<nsIAtom> mDstAttribute;
   int32_t mDstNameSpace;
   nsXBLAttributeEntry* mNext;
 };
 
-// nsXBLInsertionPointEntry and helpers.  This class stores all the necessary
-// info to figure out the position of an insertion point.
-// The same insertion point may be in the insertion point table for multiple
-// keys, so we refcount the entries.
-
-class nsXBLInsertionPointEntry {
-public:
-  nsXBLInsertionPointEntry(nsIContent* aParent)
-    : mInsertionParent(aParent),
-      mInsertionIndex(0)
-  {}
-
-  ~nsXBLInsertionPointEntry() {
-    if (mDefaultContent) {
-      nsAutoScriptBlocker scriptBlocker;
-      // mDefaultContent is a sort of anonymous content within the XBL
-      // document, and we own and manage it.  Unhook it here, since we're going
-      // away.
-      mDefaultContent->UnbindFromTree();
-    }
-  }
-
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPointEntry)
-
-  nsIContent* GetInsertionParent() { return mInsertionParent; }
-  uint32_t GetInsertionIndex() { return mInsertionIndex; }
-  void SetInsertionIndex(uint32_t aIndex) { mInsertionIndex = aIndex; }
-
-  nsIContent* GetDefaultContent() { return mDefaultContent; }
-  void SetDefaultContent(nsIContent* aChildren) { mDefaultContent = aChildren; }
-
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXBLInsertionPointEntry)
-
-protected:
-  nsCOMPtr<nsIContent> mInsertionParent;
-  nsCOMPtr<nsIContent> mDefaultContent;
-  uint32_t mInsertionIndex;
-};
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLInsertionPointEntry)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionParent)
-  if (tmp->mDefaultContent) {
-    nsAutoScriptBlocker scriptBlocker;
-    // mDefaultContent is a sort of anonymous content within the XBL
-    // document, and we own and manage it.  Unhook it here, since we're going
-    // away.
-    tmp->mDefaultContent->UnbindFromTree();
-    tmp->mDefaultContent = nullptr;
-  }      
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLInsertionPointEntry)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultContent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLInsertionPointEntry, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLInsertionPointEntry, Release)
-
 // =============================================================================
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
 : mImplementation(nullptr),
   mBaseBinding(nullptr),
   mInheritStyle(true),
   mCheckedBaseProto(false),
   mKeyHandlersRegistered(false),
   mChromeOnlyContent(false),
   mResources(nullptr),
   mAttributeTable(nullptr),
-  mInsertionPointTable(nullptr),
   mInterfaceTable(nullptr),
   mBaseNameSpaceID(kNameSpaceID_None)
 {
   MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
 }
 
 nsresult
 nsXBLPrototypeBinding::Init(const nsACString& aID,
@@ -223,27 +164,16 @@ bool nsXBLPrototypeBinding::CompareBindi
   mBindingURI->Equals(aURI, &equal);
   if (!equal && mAlternateBindingURI) {
     mAlternateBindingURI->Equals(aURI, &equal);
   }
   return equal;
 }
 
 static bool
-TraverseInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
-{
-  nsCycleCollectionTraversalCallback &cb = 
-    *static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
-  nsXBLInsertionPointEntry* entry =
-    static_cast<nsXBLInsertionPointEntry*>(aData);
-  CycleCollectionNoteChild(cb, entry, "[insertion point table] value");
-  return kHashEnumerateNext;
-}
-
-static bool
 TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
 {
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME((*cb), "proto mInterfaceTable data");
   cb->NoteXPCOMChild(static_cast<nsISupports*>(aData));
   return kHashEnumerateNext;
 }
@@ -252,18 +182,16 @@ void
 nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
   cb.NoteXPCOMChild(mBinding);
   if (mResources) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mResources mLoader");
     cb.NoteXPCOMChild(mResources->mLoader);
   }
-  if (mInsertionPointTable)
-    mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
   if (mInterfaceTable)
     mInterfaceTable->Enumerate(TraverseBinding, &cb);
 }
 
 void
 nsXBLPrototypeBinding::UnlinkJSObjects()
 {
   if (mImplementation)
@@ -277,29 +205,24 @@ nsXBLPrototypeBinding::Trace(const Trace
     mImplementation->Trace(aCallbacks, aClosure);
 }
 
 void
 nsXBLPrototypeBinding::Initialize()
 {
   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
   if (content) {
-    // Make sure to construct the attribute table first, since constructing the
-    // insertion point table removes some of the subtrees, which makes them
-    // unreachable by walking our DOM.
     ConstructAttributeTable(content);
-    ConstructInsertionTable(content);
   }
 }
 
 nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void)
 {
   delete mResources;
   delete mAttributeTable;
-  delete mInsertionPointTable;
   delete mInterfaceTable;
   delete mImplementation;
   MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
 }
 
 void
 nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
 {
@@ -524,161 +447,16 @@ nsXBLPrototypeBinding::AttributeChanged(
         }
       }
     }
 
     xblAttr = xblAttr->GetNext();
   }
 }
 
-struct InsertionData {
-  nsXBLBinding* mBinding;
-  nsXBLPrototypeBinding* mPrototype;
-
-  InsertionData(nsXBLBinding* aBinding,
-                nsXBLPrototypeBinding* aPrototype) 
-    :mBinding(aBinding), mPrototype(aPrototype) {}
-};
-
-bool InstantiateInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
-{
-  nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry*>(aData);
-  InsertionData* data = static_cast<InsertionData*>(aClosure);
-  nsXBLBinding* binding = data->mBinding;
-  nsXBLPrototypeBinding* proto = data->mPrototype;
-
-  // Get the insertion parent.
-  nsIContent* content = entry->GetInsertionParent();
-  uint32_t index = entry->GetInsertionIndex();
-  nsIContent* defContent = entry->GetDefaultContent();
-
-  // Locate the real content.
-  nsIContent *instanceRoot = binding->GetAnonymousContent();
-  nsIContent *templRoot = proto->GetImmediateChild(nsGkAtoms::content);
-  nsIContent *realContent = proto->LocateInstance(nullptr, templRoot,
-                                                  instanceRoot, content);
-  if (!realContent)
-    realContent = binding->GetBoundElement();
-
-  // Now that we have the real content, look it up in our table.
-  nsInsertionPointList* points = nullptr;
-  binding->GetInsertionPointsFor(realContent, &points);
-  nsXBLInsertionPoint* insertionPoint = nullptr;
-  int32_t count = points->Length();
-  int32_t i = 0;
-  int32_t currIndex = 0;  
-  
-  for ( ; i < count; i++) {
-    nsXBLInsertionPoint* currPoint = points->ElementAt(i);
-    currIndex = currPoint->GetInsertionIndex();
-    if (currIndex == (int32_t)index) {
-      // This is a match. Break out of the loop and set our variable.
-      insertionPoint = currPoint;
-      break;
-    }
-    
-    if (currIndex > (int32_t)index)
-      // There was no match. Break.
-      break;
-  }
-
-  if (!insertionPoint) {
-    // We need to make a new insertion point.
-    insertionPoint = new nsXBLInsertionPoint(realContent, index, defContent);
-    if (insertionPoint) {
-      points->InsertElementAt(i, insertionPoint);
-    }
-  }
-
-  return true;
-}
-
-void
-nsXBLPrototypeBinding::InstantiateInsertionPoints(nsXBLBinding* aBinding)
-{
-  InsertionData data(aBinding, this);
-  if (mInsertionPointTable)
-    mInsertionPointTable->Enumerate(InstantiateInsertionPoint, &data);
-}
-
-nsIContent*
-nsXBLPrototypeBinding::GetInsertionPoint(nsIContent* aBoundElement,
-                                         nsIContent* aCopyRoot,
-                                         const nsIContent* aChild,
-                                         uint32_t* aIndex)
-{
-  if (!mInsertionPointTable)
-    return nullptr;
-
-  nsISupportsKey key(aChild->Tag());
-  nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key));
-  if (!entry) {
-    nsISupportsKey key2(nsGkAtoms::children);
-    entry = static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key2));
-  }
-
-  nsIContent *realContent = nullptr;
-  if (entry) {
-    nsIContent* content = entry->GetInsertionParent();
-    *aIndex = entry->GetInsertionIndex();
-    nsIContent* templContent = GetImmediateChild(nsGkAtoms::content);
-    realContent = LocateInstance(nullptr, templContent, aCopyRoot, content);
-  }
-  else {
-    // We got nothin'.  Bail.
-    return nullptr;
-  }
-
-  return realContent ? realContent : aBoundElement;
-}
-
-nsIContent*
-nsXBLPrototypeBinding::GetSingleInsertionPoint(nsIContent* aBoundElement,
-                                               nsIContent* aCopyRoot,
-                                               uint32_t* aIndex,
-                                               bool* aMultipleInsertionPoints)
-{ 
-  *aMultipleInsertionPoints = false;
-  *aIndex = 0;
-
-  if (!mInsertionPointTable)
-    return nullptr;
-
-  if (mInsertionPointTable->Count() != 1) {
-    *aMultipleInsertionPoints = true;
-    return nullptr;
-  }
-
-  nsISupportsKey key(nsGkAtoms::children);
-  nsXBLInsertionPointEntry* entry =
-    static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key));
-
-  if (!entry) {
-    // The only insertion point specified was actually a filtered insertion
-    // point. This means (strictly speaking) that we actually have multiple
-    // insertion points: the filtered one and a generic insertion point
-    // (content that doesn't match the filter will just go right underneath the
-    // bound element).
-
-    *aMultipleInsertionPoints = true;
-    *aIndex = 0;
-    return nullptr;
-  }
-
-  *aMultipleInsertionPoints = false;
-  *aIndex = entry->GetInsertionIndex();
-
-  nsIContent* templContent = GetImmediateChild(nsGkAtoms::content);
-  nsIContent* content = entry->GetInsertionParent();
-  nsIContent *realContent = LocateInstance(nullptr, templContent, aCopyRoot,
-                                           content);
-
-  return realContent ? realContent : aBoundElement;
-}
-
 void
 nsXBLPrototypeBinding::SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag)
 {
   mBaseNameSpaceID = aNamespaceID;
   mBaseTag = aTag;
 }
 
 nsIAtom*
@@ -739,96 +517,30 @@ nsXBLPrototypeBinding::LocateInstance(ns
                                       nsIContent* aCopyRoot, 
                                       nsIContent* aTemplChild)
 {
   // XXX We will get in trouble if the binding instantiation deviates from the template
   // in the prototype.
   if (aTemplChild == aTemplRoot || !aTemplChild)
     return nullptr;
 
-  nsCOMPtr<nsIContent> templParent = aTemplChild->GetParent();
-  nsCOMPtr<nsIContent> childPoint;
+  nsIContent* templParent = aTemplChild->GetParent();
 
   // We may be disconnected from our parent during cycle collection.
   if (!templParent)
     return nullptr;
-  
-  if (aBoundElement) {
-    if (templParent->NodeInfo()->Equals(nsGkAtoms::children,
-                                        kNameSpaceID_XBL)) {
-      childPoint = templParent;
-      templParent = childPoint->GetParent();
-    }
-  }
 
-  if (!templParent)
+  nsIContent *copyParent =
+    templParent == aTemplRoot ? aCopyRoot :
+                   LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
+
+  if (!copyParent)
     return nullptr;
 
-  nsIContent* result = nullptr;
-  nsIContent *copyParent;
-
-  if (templParent == aTemplRoot)
-    copyParent = aCopyRoot;
-  else
-    copyParent = LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
-  
-  if (childPoint && aBoundElement) {
-    // First we have to locate this insertion point and use its index and its
-    // count to detemine our precise position within the template.
-    nsIDocument* doc = aBoundElement->OwnerDoc();
-    nsXBLBinding *binding = doc->BindingManager()->GetBinding(aBoundElement);
-    nsIContent *anonContent = nullptr;
-
-    while (binding) {
-      anonContent = binding->GetAnonymousContent();
-      if (anonContent)
-        break;
-
-      binding = binding->GetBaseBinding();
-    }
-
-    NS_ABORT_IF_FALSE(binding, "Bug 620181 this is unexpected");
-    if (!binding)
-      return nullptr;
-
-    nsInsertionPointList* points = nullptr;
-    if (anonContent == copyParent)
-      binding->GetInsertionPointsFor(aBoundElement, &points);
-    else
-      binding->GetInsertionPointsFor(copyParent, &points);
-    int32_t count = points->Length();
-    for (int32_t i = 0; i < count; i++) {
-      // Next we have to find the real insertion point for this proto insertion
-      // point.  If it does not contain any default content, then we should 
-      // return null, since the content is not in the clone.
-      nsXBLInsertionPoint* currPoint = static_cast<nsXBLInsertionPoint*>(points->ElementAt(i));
-      nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
-      if (defContent == childPoint) {
-        // Now check to see if we even built default content at this
-        // insertion point.
-        defContent = currPoint->GetDefaultContent();
-        if (defContent) {
-          // Find out the index of the template element within the <children> elt.
-          int32_t index = childPoint->IndexOf(aTemplChild);
-          
-          // Now we just have to find the corresponding elt underneath the cloned
-          // default content.
-          result = defContent->GetChildAt(index);
-        } 
-        break;
-      }
-    }
-  }
-  else if (copyParent)
-  {
-    int32_t index = templParent->IndexOf(aTemplChild);
-    result = copyParent->GetChildAt(index);
-  }
-
-  return result;
+  return copyParent->GetChildAt(templParent->IndexOf(aTemplChild));
 }
 
 struct nsXBLAttrChangeData
 {
   nsXBLPrototypeBinding* mProto;
   nsIContent* mBoundElement;
   nsIContent* mContent;
   int32_t mSrcNamespace;
@@ -1075,121 +787,16 @@ nsXBLPrototypeBinding::ConstructAttribut
   // Recur into our children.
   for (nsIContent* child = aElement->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     ConstructAttributeTable(child);
   }
 }
 
-static bool
-DeleteInsertionPointEntry(nsHashKey* aKey, void* aData, void* aClosure)
-{
-  static_cast<nsXBLInsertionPointEntry*>(aData)->Release();
-  return true;
-}
-
-void 
-nsXBLPrototypeBinding::ConstructInsertionTable(nsIContent* aContent)
-{
-  nsCOMArray<nsIContent> childrenElements;
-  GetNestedChildren(nsGkAtoms::children, kNameSpaceID_XBL, aContent,
-                    childrenElements);
-
-  int32_t count = childrenElements.Count();
-  if (count == 0)
-    return;
-
-  mInsertionPointTable = new nsObjectHashtable(nullptr, nullptr,
-                                               DeleteInsertionPointEntry,
-                                               nullptr, 4);
-  if (!mInsertionPointTable)
-    return;
-
-  int32_t i;
-  for (i = 0; i < count; i++) {
-    nsIContent* child = childrenElements[i];
-    nsCOMPtr<nsIContent> parent = child->GetParent();
-
-    // Create an XBL insertion point entry.
-    nsXBLInsertionPointEntry* xblIns = new nsXBLInsertionPointEntry(parent);
-
-    nsAutoString includes;
-    child->GetAttr(kNameSpaceID_None, nsGkAtoms::includes, includes);
-    if (includes.IsEmpty()) {
-      nsISupportsKey key(nsGkAtoms::children);
-      xblIns->AddRef();
-      mInsertionPointTable->Put(&key, xblIns);
-    }
-    else {
-      // The user specified at least one attribute.
-      char* str = ToNewCString(includes);
-      char* newStr;
-      // XXX We should use a strtok function that tokenizes PRUnichar's
-      // so that we don't have to convert from Unicode to ASCII and then back
-
-      char* token = nsCRT::strtok( str, "| ", &newStr );
-      while( token != nullptr ) {
-        nsAutoString tok;
-        tok.AssignWithConversion(token);
-
-        // Build an atom out of this string.
-        nsCOMPtr<nsIAtom> atom = do_GetAtom(tok);
-
-        nsISupportsKey key(atom);
-        xblIns->AddRef();
-        mInsertionPointTable->Put(&key, xblIns);
-          
-        token = nsCRT::strtok( newStr, "| ", &newStr );
-      }
-
-      nsMemory::Free(str);
-    }
-
-    // Compute the index of the <children> element.  This index is
-    // equal to the index of the <children> in the template minus the #
-    // of previous insertion point siblings removed.  Because our childrenElements
-    // array was built in a DFS that went from left-to-right through siblings,
-    // if we dynamically obtain our index each time, then the removals of previous
-    // siblings will cause the index to adjust (and we won't have to take that into
-    // account explicitly).
-    int32_t index = parent->IndexOf(child);
-    xblIns->SetInsertionIndex((uint32_t)index);
-
-    // Now remove the <children> element from the template.  This ensures that the
-    // binding instantiation will not contain a clone of the <children> element when
-    // it clones the binding template.
-    parent->RemoveChildAt(index, false);
-
-    // See if the insertion point contains default content.  Default content must
-    // be cached in our insertion point entry, since it will need to be cloned
-    // in situations where no content ends up being placed at the insertion point.
-    uint32_t defaultCount = child->GetChildCount();
-    if (defaultCount > 0) {
-      nsAutoScriptBlocker scriptBlocker;
-      // Annotate the insertion point with our default content.
-      xblIns->SetDefaultContent(child);
-
-      // Reconnect back to our parent for access later.  This makes "inherits" easier
-      // to work with on default content.
-      // XXXbz this is somewhat screwed up, since it's sort of like anonymous
-      // content... but not.
-      nsresult rv =
-        child->BindToTree(parent->GetCurrentDoc(), parent,
-                          parent->GetBindingParent(), false);
-      if (NS_FAILED(rv)) {
-        // Well... now what?  Just unbind and bail out, I guess...
-        // XXXbz This really shouldn't be a void method!
-        child->UnbindFromTree();
-        return;
-      }
-    }
-  }
-}
-
 nsresult
 nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAString& aImpls)
 {
   if (!aImpls.IsEmpty()) {
     // Obtain the interface info manager that can tell us the IID
     // for a given interface name.
     nsCOMPtr<nsIInterfaceInfoManager>
       infoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
@@ -1247,33 +854,16 @@ nsXBLPrototypeBinding::ConstructInterfac
 
       token = nsCRT::strtok( newStr, ", ", &newStr );
     }
   }
 
   return NS_OK;
 }
 
-void
-nsXBLPrototypeBinding::GetNestedChildren(nsIAtom* aTag, int32_t aNamespace,
-                                         nsIContent* aContent,
-                                         nsCOMArray<nsIContent> & aList)
-{
-  for (nsIContent* child = aContent->GetFirstChild();
-       child;
-       child = child->GetNextSibling()) {
-
-    if (child->NodeInfo()->Equals(aTag, aNamespace)) {
-      aList.AppendObject(child);
-    }
-    else
-      GetNestedChildren(aTag, aNamespace, child, aList);
-  }
-}
-
 nsresult
 nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement)
 {
   if (!mResources)
     return NS_ERROR_FAILURE; // Makes no sense to add a listener when the binding
                              // has no resources.
 
   mResources->AddResourceListener(aBoundElement);
@@ -1499,41 +1089,16 @@ nsXBLPrototypeBinding::Read(nsIObjectInp
   }
 
   cleanup.Disconnect();
   return NS_OK;
 }
 
 static
 bool
-GatherInsertionPoints(nsHashKey *aKey, void *aData, void* aClosure)
-{
-  ArrayOfInsertionPointsByContent* insertionPointsByContent =
-    static_cast<ArrayOfInsertionPointsByContent *>(aClosure);
-
-  nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry *>(aData);
-
-  // Add a new blank array if it isn't already there.
-  nsAutoTArray<InsertionItem, 1>* list;
-  if (!insertionPointsByContent->Get(entry->GetInsertionParent(), &list)) {
-    list = new nsAutoTArray<InsertionItem, 1>;
-    insertionPointsByContent->Put(entry->GetInsertionParent(), list);
-  }
-
-  // Add the item to the array and ensure it stays sorted by insertion index.
-  nsIAtom* atom = static_cast<nsIAtom *>(
-                    static_cast<nsISupportsKey *>(aKey)->GetValue());
-  InsertionItem newitem(entry->GetInsertionIndex(), atom, entry->GetDefaultContent());
-  list->InsertElementSorted(newitem);
-
-  return kHashEnumerateNext;
-}
-
-static
-bool
 WriteInterfaceID(nsHashKey *aKey, void *aData, void* aClosure)
 {
   // We can just write out the ids. The cache will be invalidated when a
   // different build is used, so we don't need to worry about ids changing.
   nsID iid = ((nsIIDKey *)aKey)->mKey;
   static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(iid);
   return kHashEnumerateNext;
 }
@@ -1585,30 +1150,19 @@ nsXBLPrototypeBinding::Write(nsIObjectOu
 
   nsAutoString baseTag;
   if (mBaseTag) {
     mBaseTag->ToString(baseTag);
   }
   rv = aStream->WriteWStringZ(baseTag.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // The binding holds insertions points keyed by the tag that is going to be
-  // inserted, however, the cache would prefer to know insertion points keyed
-  // by where they are in the content hierarchy. GatherInsertionPoints is used
-  // to iterate over the insertion points and store them temporarily in this
-  // latter hashtable.
-  ArrayOfInsertionPointsByContent insertionPointsByContent;
-  insertionPointsByContent.Init();
-  if (mInsertionPointTable) {
-    mInsertionPointTable->Enumerate(GatherInsertionPoints, &insertionPointsByContent);
-  }
-
   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
   if (content) {
-    rv = WriteContentNode(aStream, content, insertionPointsByContent);
+    rv = WriteContentNode(aStream, content);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     // Write a marker to indicate that there is no content.
     rv = aStream->Write8(XBLBinding_Serialize_NoContent);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -1827,67 +1381,16 @@ nsXBLPrototypeBinding::ReadContentNode(n
 
     EnsureAttributeTable();
     AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
 
     rv = ReadNamespace(aStream, srcNamespaceID);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // Next, read the insertion points.
-  uint32_t insertionPointIndex;
-  rv = aStream->Read32(&insertionPointIndex);
-  NS_ENSURE_SUCCESS(rv, rv);
-  while (insertionPointIndex != XBLBinding_Serialize_NoMoreInsertionPoints) {
-    nsRefPtr<nsXBLInsertionPointEntry> xblIns =
-      new nsXBLInsertionPointEntry(content);
-    xblIns->SetInsertionIndex(insertionPointIndex);
-
-    // If the insertion point has default content, read it.
-    nsCOMPtr<nsIContent> defaultContent;
-    rv = ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(defaultContent));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (defaultContent) {
-      xblIns->SetDefaultContent(defaultContent);
-
-      rv = defaultContent->BindToTree(nullptr, content,
-                                      content->GetBindingParent(), false);
-      if (NS_FAILED(rv)) {
-        defaultContent->UnbindFromTree();
-        return rv;
-      }
-    }
-
-    if (!mInsertionPointTable) {
-      mInsertionPointTable = new nsObjectHashtable(nullptr, nullptr,
-                                                   DeleteInsertionPointEntry,
-                                                   nullptr, 4);
-    }
-
-    // Now read in the tags that can be inserted at this point, which is
-    // specified by the syntax includes="menupopup|panel", and add them to
-    // the hash.
-    uint32_t count;
-    rv = aStream->Read32(&count);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (; count > 0; count --) {
-      aStream->ReadString(tag);
-      nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
-
-      nsISupportsKey key(tagAtom);
-      NS_ADDREF(xblIns.get());
-      mInsertionPointTable->Put(&key, xblIns);
-    }
-
-    rv = aStream->Read32(&insertionPointIndex);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   // Finally, read in the child nodes.
   uint32_t childCount;
   rv = aStream->Read32(&childCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (uint32_t i = 0; i < childCount; i++) {
     nsCOMPtr<nsIContent> child;
     ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
@@ -1956,18 +1459,17 @@ WriteAttributeNS(nsHashKey *aKey, void *
   nsObjectHashtable* attributes = static_cast<nsObjectHashtable*>(aData);
   attributes->Enumerate(WriteAttribute, data);
 
   return kHashEnumerateNext;
 }
 
 nsresult
 nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
-                                        nsIContent* aNode,
-                                        ArrayOfInsertionPointsByContent& aInsertionPointsByContent)
+                                        nsIContent* aNode)
 {
   nsresult rv;
 
   if (!aNode->IsElement()) {
     // Text is writen out as a single byte for the type, followed by the text.
     uint8_t type = XBLBinding_Serialize_NoContent;
     switch (aNode->NodeType()) {
       case nsIDOMNode::TEXT_NODE:
@@ -2041,72 +1543,23 @@ nsXBLPrototypeBinding::WriteContentNode(
   // Write out the attribute fowarding information
   if (mAttributeTable) {
     WriteAttributeData data(this, aStream, aNode);
     mAttributeTable->Enumerate(WriteAttributeNS, &data);
   }
   rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Write out the insertion points.
-  nsAutoTArray<InsertionItem, 1>* list;
-  if (aInsertionPointsByContent.Get(aNode, &list)) {
-    uint32_t lastInsertionIndex = 0xFFFFFFFF;
-
-    // Iterate over the insertion points and see if they match this point
-    // in the content tree by comparing insertion indices.
-    for (uint32_t l = 0; l < list->Length(); l++) {
-      InsertionItem item = list->ElementAt(l);
-      // The list is sorted by insertionIndex so all items pertaining to
-      // this point will be in the list in order. We only need to write out the
-      // default content and the number of tags once for each index.
-      if (item.insertionIndex != lastInsertionIndex) {
-        lastInsertionIndex = item.insertionIndex;
-        aStream->Write32(item.insertionIndex);
-        // If the insertion point has default content, write that out, or
-        // write out XBLBinding_Serialize_NoContent if there isn't any.
-        if (item.defaultContent) {
-          rv = WriteContentNode(aStream, item.defaultContent,
-                                aInsertionPointsByContent);
-        }
-        else {
-          rv = aStream->Write8(XBLBinding_Serialize_NoContent);
-        }
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        // Determine how many insertion points share the same index, so that
-        // the total count can be written out.
-        uint32_t icount = 1;
-        for (uint32_t i = l + 1; i < list->Length(); i++) {
-          if (list->ElementAt(i).insertionIndex != lastInsertionIndex)
-            break;
-          icount++;
-        }
-
-        rv = aStream->Write32(icount);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-
-      rv = aStream->WriteWStringZ(nsDependentAtomString(list->ElementAt(l).tag).get());
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
-  // Write out an end marker after the insertion points. If there weren't
-  // any, this will be all that is written out.
-  rv = aStream->Write32(XBLBinding_Serialize_NoMoreInsertionPoints);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Finally, write out the child nodes.
   count = aNode->GetChildCount();
   rv = aStream->Write32(count);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (i = 0; i < count; i++) {
-    rv = WriteContentNode(aStream, aNode->GetChildAt(i), aInsertionPointsByContent);
+    rv = WriteContentNode(aStream, aNode->GetChildAt(i));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -22,44 +22,16 @@ class nsIAtom;
 class nsIContent;
 class nsIDocument;
 class nsIScriptContext;
 class nsSupportsHashtable;
 class nsXBLProtoImplField;
 class nsXBLBinding;
 class nsCSSStyleSheet;
 
-// This structure represents an insertion point, and is used when writing out
-// insertion points. It contains comparison operators so that it can be stored
-// in an array sorted by index.
-struct InsertionItem {
-  uint32_t insertionIndex;
-  nsIAtom* tag;
-  nsIContent* defaultContent;
-
-  InsertionItem(uint32_t aInsertionIndex, nsIAtom* aTag, nsIContent* aDefaultContent)
-    : insertionIndex(aInsertionIndex), tag(aTag), defaultContent(aDefaultContent) { }
-
-  bool operator<(const InsertionItem& item) const
-  {
-    NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
-                 "default content is different for same index");
-    return insertionIndex < item.insertionIndex;
-  }
-
-  bool operator==(const InsertionItem& item) const
-  {
-    NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
-                 "default content is different for same index");
-    return insertionIndex == item.insertionIndex;
-  }
-};
-
-typedef nsClassHashtable<nsISupportsHashKey, nsAutoTArray<InsertionItem, 1> > ArrayOfInsertionPointsByContent;
-
 // *********************************************************************/
 // The XBLPrototypeBinding class
 
 // Instances of this class are owned by the nsXBLDocumentInfo object returned
 // by XBLDocumentInfo().  Consumers who want to refcount things should refcount
 // that.
 class nsXBLPrototypeBinding
 {
@@ -140,38 +112,23 @@ public:
 
   nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
   bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }
   
   void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
 
   nsIStyleRuleProcessor* GetRuleProcessor();
   nsXBLPrototypeResources::sheet_array_type* GetStyleSheets();
-
-  bool HasInsertionPoints() { return mInsertionPointTable != nullptr; }
   
   bool HasStyleSheets() {
     return mResources && mResources->mStyleSheetList.Length() > 0;
   }
 
   nsresult FlushSkinSheets();
 
-  void InstantiateInsertionPoints(nsXBLBinding* aBinding);
-
-  // XXXbz this aIndex has nothing to do with an index into the child
-  // list of the insertion parent or anything.
-  nsIContent* GetInsertionPoint(nsIContent* aBoundElement,
-                                nsIContent* aCopyRoot,
-                                const nsIContent *aChild,
-                                uint32_t* aIndex);
-
-  nsIContent* GetSingleInsertionPoint(nsIContent* aBoundElement,
-                                      nsIContent* aCopyRoot,
-                                      uint32_t* aIndex, bool* aMultiple);
-
   nsIAtom* GetBaseTag(int32_t* aNamespaceID);
   void SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag);
 
   bool ImplementsInterface(REFNSIID aIID) const;
 
   nsresult AddResourceListener(nsIContent* aBoundElement);
 
   void Initialize();
@@ -214,18 +171,16 @@ public:
    */
   nsresult ReadContentNode(nsIObjectInputStream* aStream,
                            nsIDocument* aDocument,
                            nsNodeInfoManager* aNim,
                            nsIContent** aChild);
 
   /**
    * Write the content node aNode to aStream.
-   * aInsertionPointsByContent is a hash of the insertion points in the binding,
-   * keyed by where there are in the content hierarchy.
    *
    * This method is called recursively for each child descendant. For the topmost
    * call, aNode must be an element.
    *
    * Text, CDATA and comment nodes are serialized as:
    *   the constant XBLBinding_Serialize_TextNode, XBLBinding_Serialize_CDATANode
    *     or XBLBinding_Serialize_CommentNode
    *   the text for the node
@@ -240,29 +195,21 @@ public:
    *     attribute's tag
    *     attribute's value
    *   attribute forwarding table:
    *     source namespace
    *     source attribute
    *     destination namespace
    *     destination attribute
    *   the constant XBLBinding_Serialize_NoMoreAttributes
-   *   insertion points within this node:
-   *     child index to insert within node
-   *     default content serialized in the same manner or XBLBinding_Serialize_NoContent
-   *     count of insertion points at that index
-   *       that number of string tags (those in the <children>'s includes attribute)
-   *   the constant XBLBinding_Serialize_NoMoreInsertionPoints
    *   32-bit count of the number of child nodes
    *     each child node is serialized in the same manner in sequence
    *   the constant XBLBinding_Serialize_NoContent
    */
-  nsresult WriteContentNode(nsIObjectOutputStream* aStream,
-                            nsIContent* aNode,
-                            ArrayOfInsertionPointsByContent& aInsertionPointsByContent);
+  nsresult WriteContentNode(nsIObjectOutputStream* aStream, nsIContent* aNode);
 
   /**
    * Read or write a namespace id from or to aStream. If the namespace matches
    * one of the built-in ones defined in nsINameSpaceManager.h, it will be written as
    * a single byte with that value. Otherwise, XBLBinding_Serialize_CustomNamespace is
    * written out, followed by a string written with writeWStringZ.
    */
   nsresult ReadNamespace(nsIObjectInputStream* aStream, int32_t& aNameSpaceID);
@@ -281,19 +228,16 @@ public:
                 nsIContent* aElement,
                 bool aFirstBinding = false);
 
   void Traverse(nsCycleCollectionTraversalCallback &cb) const;
   void UnlinkJSObjects();
   void Trace(const TraceCallbacks& aCallbacks, void *aClosure) const;
 
 // Internal member functions.
-// XXXbz GetImmediateChild needs to be public to be called by SetAttrs,
-// InstantiateInsertionPoints, etc; those should probably be a class static
-// method instead of a global (non-static!) ones.
 public:
   /**
    * GetImmediateChild locates the immediate child of our binding element which
    * has the localname given by aTag and is in the XBL namespace.
    */
   nsIContent* GetImmediateChild(nsIAtom* aTag);
   nsIContent* LocateInstance(nsIContent* aBoundElt,
                              nsIContent* aTemplRoot,
@@ -304,20 +248,16 @@ public:
 protected:
   // Ensure that mAttributeTable has been created.
   void EnsureAttributeTable();
   // Ad an entry to the attribute table
   void AddToAttributeTable(int32_t aSourceNamespaceID, nsIAtom* aSourceTag,
                            int32_t aDestNamespaceID, nsIAtom* aDestTag,
                            nsIContent* aContent);
   void ConstructAttributeTable(nsIContent* aElement);
-  void ConstructInsertionTable(nsIContent* aElement);
-  void GetNestedChildren(nsIAtom* aTag, int32_t aNamespace,
-                         nsIContent* aContent,
-                         nsCOMArray<nsIContent> & aList);
   void CreateKeyHandlers();
 
 // MEMBER VARIABLES
 protected:
   nsCOMPtr<nsIURI> mBindingURI;
   nsCOMPtr<nsIURI> mAlternateBindingURI; // Alternate id-less URI that is only non-null on the first binding.
   nsCOMPtr<nsIContent> mBinding; // Strong. We own a ref to our content element in the binding doc.
   nsAutoPtr<nsXBLPrototypeHandler> mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers.
@@ -337,19 +277,16 @@ protected:
   nsXBLPrototypeResources* mResources; // If we have any resources, this will be non-null.
                                       
   nsXBLDocumentInfo* mXBLDocInfoWeak; // A pointer back to our doc info.  Weak, since it owns us.
 
   nsObjectHashtable* mAttributeTable; // A table for attribute containers. Namespace IDs are used as
                                       // keys in the table. Containers are nsObjectHashtables.
                                       // This table is used to efficiently handle attribute changes.
 
-  nsObjectHashtable* mInsertionPointTable; // A table of insertion points for placing explicit content
-                                           // underneath anonymous content.
-
   nsSupportsHashtable* mInterfaceTable; // A table of cached interfaces that we support.
 
   int32_t mBaseNameSpaceID;    // If we extend a tagname/namespace, then that information will
   nsCOMPtr<nsIAtom> mBaseTag;  // be stored in here.
 
   nsCOMArray<nsXBLKeyEventHandler> mKeyHandlers;
 };
 
--- a/content/xml/content/src/moz.build
+++ b/content/xml/content/src/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'content'
 
 EXPORTS.mozilla.dom += [
     'CDATASection.h',
     'ProcessingInstruction.h',
     'XMLStylesheetProcessingInstruction.h',
+    'nsXMLElement.h',
 ]
 
 CPP_SOURCES += [
     'CDATASection.cpp',
     'ProcessingInstruction.cpp',
     'XMLStylesheetProcessingInstruction.cpp',
     'nsXMLElement.cpp',
 ]
--- a/content/xul/templates/src/nsXULTemplateBuilder.cpp
+++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp
@@ -62,16 +62,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsIConsoleService.h" 
 #include "nsNetUtil.h"
 #include "nsXULTemplateBuilder.h"
 #include "nsXULTemplateQueryProcessorRDF.h"
 #include "nsXULTemplateQueryProcessorXML.h"
 #include "nsXULTemplateQueryProcessorStorage.h"
 #include "nsContentUtils.h"
+#include "ChildIterator.h"
 #include "nsCxPusher.h"
 
 using namespace mozilla::dom;
 using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 static NS_DEFINE_CID(kRDFContainerUtilsCID,      NS_RDFCONTAINERUTILS_CID);
@@ -1649,57 +1650,38 @@ nsXULTemplateBuilder::GetTemplateRoot(ns
             NS_ENSURE_STATE(content &&
                             !nsContentUtils::ContentIsDescendantOf(mRoot,
                                                                    content));
             content.forget(aResult);
             return NS_OK;
         }
     }
 
-#if 1 // XXX hack to workaround bug with XBL insertion/removal?
-    {
-        // If root node has no template attribute, then look for a child
-        // node which is a template tag
-        for (nsIContent* child = mRoot->GetFirstChild();
-             child;
-             child = child->GetNextSibling()) {
-
-            if (IsTemplateElement(child)) {
-                NS_ADDREF(*aResult = child);
-                return NS_OK;
-            }
+    // If root node has no template attribute, then look for a child
+    // node which is a template tag.
+    for (nsIContent* child = mRoot->GetFirstChild();
+         child;
+         child = child->GetNextSibling()) {
+
+        if (IsTemplateElement(child)) {
+            NS_ADDREF(*aResult = child);
+            return NS_OK;
         }
     }
-#endif
-
-    // If we couldn't find a real child, look through the anonymous
-    // kids, too.
-    nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
-    if (! doc)
-        return NS_OK;
-
-    nsCOMPtr<nsIDOMNodeList> kids;
-    doc->BindingManager()->GetXBLChildNodesFor(mRoot, getter_AddRefs(kids));
-
-    if (kids) {
-        uint32_t length;
-        kids->GetLength(&length);
-
-        for (uint32_t i = 0; i < length; ++i) {
-            nsCOMPtr<nsIDOMNode> node;
-            kids->Item(i, getter_AddRefs(node));
-            if (! node)
-                continue;
-
-            nsCOMPtr<nsIContent> child = do_QueryInterface(node);
-
-            if (IsTemplateElement(child)) {
-                NS_ADDREF(*aResult = child.get());
-                return NS_OK;
-            }
+
+    // Look through the anonymous children as well. Although FlattenedChildIterator
+    // will find a template element that has been placed in an insertion point, many
+    // bindings do not have a specific insertion point for the template element, which
+    // would cause it to not be part of the flattened content tree. The check above to
+    // check the explicit children as well handles this case.
+    FlattenedChildIterator iter(mRoot);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+        if (IsTemplateElement(child)) {
+            NS_ADDREF(*aResult = child);
+            return NS_OK;
         }
     }
 
     *aResult = nullptr;
     return NS_OK;
 }
 
 nsresult
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -306,17 +306,17 @@ nsFocusManager::GetRedirectedFocus(nsICo
       if (menulist) {
         menulist->GetInputField(getter_AddRefs(inputField));
       }
       else if (aContent->Tag() == nsGkAtoms::scale) {
         nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
         if (!doc)
           return nullptr;
 
-        nsINodeList* children = doc->BindingManager()->GetXBLChildNodesFor(aContent);
+        nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent);
         if (children) {
           nsIContent* child = children->Item(0);
           if (child && child->Tag() == nsGkAtoms::slider)
             return child;
         }
       }
     }
 
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -86,17 +86,16 @@ CPP_SOURCES += [
     'PositionedEventTargeting.cpp',
     'RestyleTracker.cpp',
     'StackArena.cpp',
     'nsCSSColorUtils.cpp',
     'nsCSSFrameConstructor.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
     'nsCaret.cpp',
-    'nsChildIterator.cpp',
     'nsCounterManager.cpp',
     'nsDisplayList.cpp',
     'nsDisplayListInvalidation.cpp',
     'nsDocumentViewer.cpp',
     'nsFrameManager.cpp',
     'nsFrameTraversal.cpp',
     'nsGenConList.cpp',
     'nsLayoutDebugger.cpp',
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -64,17 +64,18 @@
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsObjectFrame.h"
 #include "nsRuleNode.h"
 #include "nsIDOMMutationEvent.h"
-#include "nsChildIterator.h"
+#include "ChildIterator.h"
+#include "nsXBLChildrenElement.h"
 #include "nsCSSRendering.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 #include "nsAutoPtr.h"
 #include "nsBoxFrame.h"
 #include "nsBoxLayout.h"
 #include "nsFlexContainerFrame.h"
 #include "nsImageFrame.h"
@@ -3767,19 +3768,18 @@ nsCSSFrameConstructor::CreateAnonymousFr
 }
 
 static void
 SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet)
 {
 #ifdef DEBUG
   // Make sure that the node passed to us doesn't have any XBL children
   {
-    nsIDocument *doc = aNode->OwnerDoc();
-    NS_ASSERTION(doc, "The node must be in a document");
-    NS_ASSERTION(!doc->BindingManager()->GetXBLChildNodesFor(aNode),
+    FlattenedChildIterator iter(aNode);
+    NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(),
                  "The node should not have any XBL children");
   }
 #endif
 
   // Set the flag on the node itself
   aNode->SetFlags(aFlagsToSet);
 
   // Set the flag on all of its children recursively
@@ -5936,54 +5936,45 @@ nsCSSFrameConstructor::FindFrameForConte
       !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
     sibling = nullptr;
   }
 
   return sibling;
 }
 
 nsIFrame*
-nsCSSFrameConstructor::FindPreviousSibling(const ChildIterator& aFirst,
-                                           ChildIterator aIter,
+nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
                                            uint8_t& aTargetContentDisplay)
 {
-  nsIContent* child = *aIter;
+  nsIContent* child = aIter.Get();
 
   // Note: not all content objects are associated with a frame (e.g., if it's
   // `display: none') so keep looking until we find a previous frame
-  while (aIter != aFirst) {
-    --aIter;
+  while (nsIContent* sibling = aIter.GetPreviousChild()) {
     nsIFrame* prevSibling =
-      FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, true);
+      FindFrameForContentSibling(sibling, child, aTargetContentDisplay, true);
 
     if (prevSibling) {
       // Found a previous sibling, we're done!
       return prevSibling;
     }
   }
 
   return nullptr;
 }
 
 nsIFrame*
-nsCSSFrameConstructor::FindNextSibling(ChildIterator aIter,
-                                       const ChildIterator& aLast,
+nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
                                        uint8_t& aTargetContentDisplay)
 {
-  if (aIter == aLast) {
-    // XXXbz Can happen when XBL lies to us about insertion points.  This check
-    // might be able to go away once bug 474324 is fixed.
-    return nullptr;
-  }
-
-  nsIContent* child = *aIter;
-
-  while (++aIter != aLast) {
+  nsIContent* child = aIter.Get();
+
+  while (nsIContent* sibling = aIter.GetNextChild()) {
     nsIFrame* nextSibling =
-      FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, false);
+      FindFrameForContentSibling(sibling, child, aTargetContentDisplay, false);
 
     if (nextSibling) {
       // We found a next sibling, we're done!
       return nextSibling;
     }
   }
 
   return nullptr;
@@ -6025,53 +6016,52 @@ nsCSSFrameConstructor::GetInsertionPrevS
   // Find the frame that precedes the insertion point. Walk backwards
   // from the parent frame to get the parent content, because if an
   // XBL insertion point is involved, we'll need to use _that_ to find
   // the preceding frame.
 
   NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
   nsIContent* container = aParentFrame->GetContent();
 
-  ChildIterator first, last;
-  ChildIterator::Init(container, &first, &last);
-  ChildIterator iter(first);
+  FlattenedChildIterator iter(container);
   bool xblCase = iter.XBLInvolved() || container != aContainer;
   if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
     // The check for IsRootOfAnonymousSubtree() is because editor is
     // severely broken and calls us directly for native anonymous
     // nodes that it creates.
     if (aStartSkipChild) {
-      iter.seek(aStartSkipChild);
+      iter.Seek(aStartSkipChild);
     } else {
-      iter.seek(aChild);
-    }
-  }
-#ifdef DEBUG
-  else {
+      iter.Seek(aChild);
+    }
+  } else {
+    // Prime the iterator for the call to FindPreviousSibling.
+    iter.GetNextChild();
     NS_WARNING("Someone passed native anonymous content directly into frame "
                "construction.  Stop doing that!");
   }
-#endif
-
+
+  // Note that FindPreviousSibling is passed the iterator by value, so that
+  // the later usage of the iterator starts from the same place.
   uint8_t childDisplay = UNSET_DISPLAY;
-  nsIFrame* prevSibling = FindPreviousSibling(first, iter, childDisplay);
+  nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
 
   // Now, find the geometric parent so that we can handle
   // continuations properly. Use the prev sibling if we have it;
   // otherwise use the next sibling.
   if (prevSibling) {
     aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
   }
   else {
     // If there is no previous sibling, then find the frame that follows
     if (aEndSkipChild) {
-      iter.seek(aEndSkipChild);
-      iter--;
-    }
-    nsIFrame* nextSibling = FindNextSibling(iter, last, childDisplay);
+      iter.Seek(aEndSkipChild);
+      iter.GetPreviousChild();
+    }
+    nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
 
     if (nextSibling) {
       aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
     }
     else {
       // No previous or next sibling, so treat this like an appended frame.
       *aIsAppend = true;
       if (IsFrameSpecial(aParentFrame)) {
@@ -6320,21 +6310,18 @@ nsCSSFrameConstructor::CreateNeededFrame
       }
     }
   }
   if (inRun) {
     ContentAppended(aContent, firstChildInRun, false);
   }
 
   // Now descend.
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aContent, &iter, &last);
-       iter != last;
-       ++iter) {
-    nsIContent* child = *iter;
+  FlattenedChildIterator iter(aContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
       CreateNeededFrames(child);
     }
   }
 }
 
 void nsCSSFrameConstructor::CreateNeededFrames()
 {
@@ -6376,28 +6363,26 @@ nsCSSFrameConstructor::IssueSingleInsert
     // Call ContentInserted with this node.
     ContentInserted(aContainer, child, mTempFrameTreeState,
                     aAllowLazyConstruction);
   }
 }
 
 nsIFrame*
 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
-                                              nsIFrame* aParentFrame,
                                               nsIContent* aStartChild,
                                               nsIContent* aEndChild,
                                               bool aAllowLazyConstruction)
 {
   // See if we have an XBL insertion point. If so, then that's our
   // real parent frame; if not, then the frame hasn't been built yet
   // and we just bail.
-  nsIFrame* insertionPoint;
   bool multiple = false;
-  GetInsertionPoint(aParentFrame, nullptr, &insertionPoint, &multiple);
-  if (! insertionPoint)
+  nsIFrame* insertionPoint = GetInsertionPoint(aContainer, nullptr, &multiple);
+  if (!insertionPoint && !multiple)
     return nullptr; // Don't build the frames.
  
   bool hasInsertion = false;
   if (!multiple) {
     // XXXbz XBL2/sXBL issue
     nsIDocument* document = aStartChild->GetDocument();
     // XXXbz how would |document| be null here?
     if (document &&
@@ -6417,16 +6402,18 @@ nsCSSFrameConstructor::GetRangeInsertion
       // is to find out if the insertion point's content node contains any
       // explicit children.  If it does not, then it is highly likely that 
       // an append is occurring.  (Note it is not definite, and there are insane
       // cases we will not deal with by employing this heuristic, but it beats
       // always falling back to multiple ContentInserted calls).
       //
       // In the multiple insertion point case, we know we're going to need to do
       // multiple ContentInserted calls anyway.
+      // XXXndeakin This test doesn't work in the new world. Or rather, it works, but
+      // it's slow
       childCount = insertionPoint->GetContent()->GetChildCount();
     }
  
     // If we have multiple insertion points or if we have an insertion point
     // and the operation is not a true append or if the insertion point already
     // has explicit children, then we must fall back.
     if (multiple || aEndChild != nullptr || childCount > 0) {
       // Now comes the fun part.  For each inserted child, make a
@@ -6506,26 +6493,29 @@ nsCSSFrameConstructor::ContentAppended(n
         tag == nsGkAtoms::treerow)
       return NS_OK;
 
   }
 #endif // MOZ_XUL
 
   // Get the frame associated with the content
   nsIFrame* parentFrame = GetFrameFor(aContainer);
-  if (! parentFrame)
+
+  // See comment in ContentRangeInserted for why this is necessary.
+  if (!parentFrame && !aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     return NS_OK;
+  }
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
     return NS_OK;
   }
 
   LAYOUT_PHASE_TEMP_EXIT();
-  parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
+  parentFrame = GetRangeInsertionPoint(aContainer,
                                        aFirstNewContent, nullptr,
                                        aAllowLazyConstruction);
   LAYOUT_PHASE_TEMP_REENTER();
   if (!parentFrame) {
     return NS_OK;
   }
 
   LAYOUT_PHASE_TEMP_EXIT();
@@ -6603,18 +6593,19 @@ nsCSSFrameConstructor::ContentAppended(n
 
   if (haveFirstLetterStyle) {
     // Before we get going, remove the current letter frames
     RemoveLetterFrames(state.mPresContext, state.mPresShell,
                        containingBlock);
   }
 
   nsIAtom* frameType = parentFrame->GetType();
-  bool haveNoXBLChildren =
-    mDocument->BindingManager()->GetXBLChildNodesFor(aContainer) == nullptr;
+
+  FlattenedChildIterator iter(aContainer);
+  bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
   FrameConstructionItemList items;
   if (aFirstNewContent->GetPreviousSibling() &&
       GetParentType(frameType) == eTypeBlock &&
       haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the new
     // items, and it has no frame, make a frame construction item for it. If it
     // doesn't need a frame, ConstructFramesFromItemList below won't give it
     // one.  No need to do all this if our parent type is not block, though,
@@ -6657,17 +6648,16 @@ nsCSSFrameConstructor::ContentAppended(n
         !prevSibling->IsInlineOutside() ||
         prevSibling->GetType() == nsGkAtoms::brFrame);
     // :after content can't be <br> so no need to check it
     items.SetLineBoundaryAtEnd(!parentAfterFrame ||
         !parentAfterFrame->IsInlineOutside());
   }
   // To suppress whitespace-only text frames, we have to verify that
   // our container's DOM child list matches its flattened tree child list.
-  // This is guaranteed to be true if GetXBLChildNodesFor() returns null.
   items.SetParentHasNoXBLChildren(haveNoXBLChildren);
 
   nsFrameItems frameItems;
   ConstructFramesFromItemList(state, items, parentFrame, frameItems);
 
   for (nsIContent* child = aFirstNewContent;
        child;
        child = child->GetNextSibling()) {
@@ -6924,47 +6914,48 @@ nsCSSFrameConstructor::ContentRangeInser
       accService->ContentRangeInserted(mPresShell, aContainer,
                                        aStartChild, aEndChild);
     }
 #endif
 
     return NS_OK;
   }
 
+  nsIFrame* parentFrame = GetFrameFor(aContainer);
+  // The xbl:children element won't have a frame, but default content can have the children as
+  // a parent. While its uncommon to change the structure of the default content itself, a label,
+  // for example, can be reframed by having its value attribute set or removed.
+  if (!parentFrame && !aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    return NS_OK;
+  }
+
   // Otherwise, we've got parent content. Find its frame.
-  nsIFrame* parentFrame = GetFrameFor(aContainer);
-  if (! parentFrame)
-    return NS_OK;
+  NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!");
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
     return NS_OK;
   }
 
   if (isSingleInsert) {
     // See if we have an XBL insertion point. If so, then that's our
     // real parent frame; if not, then the frame hasn't been built yet
     // and we just bail.
-    nsIFrame* insertionPoint;
-    GetInsertionPoint(parentFrame, aStartChild, &insertionPoint);
-    if (! insertionPoint)
-      return NS_OK; // Don't build the frames.
-
-    parentFrame = insertionPoint;
+    parentFrame = GetInsertionPoint(aContainer, aStartChild);
   } else {
     // Get our insertion point. If we need to issue single ContentInserted's
     // GetRangeInsertionPoint will take care of that for us.
     LAYOUT_PHASE_TEMP_EXIT();
-    parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
-                                         aStartChild, aEndChild,
+    parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
                                          aAllowLazyConstruction);
     LAYOUT_PHASE_TEMP_REENTER();
-    if (!parentFrame) {
-      return NS_OK;
-    }
+  }
+
+  if (!parentFrame) {
+    return NS_OK;
   }
 
   bool isAppend, isRangeInsertSafe;
   nsIFrame* prevSibling =
     GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
                             &isAppend, &isRangeInsertSafe);
 
   // check if range insert is safe
@@ -7116,18 +7107,18 @@ nsCSSFrameConstructor::ContentRangeInser
       parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
       // Don't change isAppend here; we'll can call AppendFrames as needed, and
       // the change to our prevSibling doesn't affect that.
     }
   }
 
   FrameConstructionItemList items;
   ParentType parentType = GetParentType(frameType);
-  bool haveNoXBLChildren =
-    mDocument->BindingManager()->GetXBLChildNodesFor(aContainer) == nullptr;
+  FlattenedChildIterator iter(aContainer);
+  bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
   if (aStartChild->GetPreviousSibling() &&
       parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the
     // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
     AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
@@ -8931,81 +8922,60 @@ nsCSSFrameConstructor::ReplicateFixedFra
   // XXXbz this is a little screwed up, since the fixed frames will have 
   // broken auto-positioning. Oh, well.
   NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(),
                "leaking frames; doc root continuation must be empty");
   canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
   return NS_OK;
 }
 
-nsresult
-nsCSSFrameConstructor::GetInsertionPoint(nsIFrame*     aParentFrame,
+nsIFrame*
+nsCSSFrameConstructor::GetInsertionPoint(nsIContent*   aContainer,
                                          nsIContent*   aChildContent,
-                                         nsIFrame**    aInsertionPoint,
-                                         bool*       aMultiple)
-{
-  // Make the insertion point be the parent frame by default, in case
-  // we have to bail early.
-  *aInsertionPoint = aParentFrame;
-
-  nsIContent* container = aParentFrame->GetContent();
-  if (!container)
-    return NS_OK;
-
+                                         bool*         aMultiple)
+{
   nsBindingManager *bindingManager = mDocument->BindingManager();
 
   nsIContent* insertionElement;
   if (aChildContent) {
     // We've got an explicit insertion child. Check to see if it's
     // anonymous.
-    if (aChildContent->GetBindingParent() == container) {
+    if (aChildContent->GetBindingParent() == aContainer) {
       // This child content is anonymous. Don't use the insertion
       // point, since that's only for the explicit kids.
-      return NS_OK;
-    }
-
-    uint32_t index;
-    insertionElement = bindingManager->GetInsertionPoint(container,
-                                                         aChildContent,
-                                                         &index);
+      return GetFrameFor(aContainer);
+    }
+
+    insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent);
   }
   else {
     bool multiple;
-    uint32_t index;
-    insertionElement = bindingManager->GetSingleInsertionPoint(container,
-                                                               &index,
-                                                               &multiple);
-    if (multiple && aMultiple)
-      *aMultiple = multiple; // Record the fact that filters are in use.
-  }
-
-  if (insertionElement) {
-    nsIFrame* insertionPoint = insertionElement->GetPrimaryFrame();
-    if (insertionPoint) {
-      // Use the content insertion frame of the insertion point.
-      insertionPoint = insertionPoint->GetContentInsertionFrame();
-      if (insertionPoint && insertionPoint != aParentFrame) 
-        GetInsertionPoint(insertionPoint, aChildContent, aInsertionPoint, aMultiple);
-    }
-    else {
-      // There was no frame created yet for the insertion point.
-      *aInsertionPoint = nullptr;
-    }
-  }
+    insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
+
+    if (multiple) {
+      if (aMultiple) {
+        *aMultiple = true;
+      }
+      return nullptr;
+    }
+  }
+
+  if (!insertionElement) {
+    insertionElement = aContainer;
+  }
+
+  nsIFrame* insertionPoint = GetFrameFor(insertionElement);
 
   // fieldsets have multiple insertion points.  Note that we might
   // have to look at insertionElement here...
-  if (aMultiple && !*aMultiple) {
-    nsIContent* content = insertionElement ? insertionElement : container;
-    if (content->IsHTML(nsGkAtoms::fieldset)) {
-      *aMultiple = true;
-    }
-  }
-
-  return NS_OK;
+  if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) {
+    *aMultiple = true;
+  }
+
+  return insertionPoint;
 }
 
 // Capture state for the frame tree rooted at the frame associated with the
 // content object, aContent
 void
 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
                                                nsILayoutHistoryState* aHistoryState)
 {
@@ -9968,21 +9938,18 @@ nsCSSFrameConstructor::ProcessChildren(n
                                  itemsToConstruct);
     }
 
     const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
     if (!addChildItems) {
       NS_WARNING("ProcessChildren max depth exceeded");
     }
 
-    ChildIterator iter, last;
-    for (ChildIterator::Init(aContent, &iter, &last);
-         iter != last;
-         ++iter) {
-      nsIContent* child = *iter;
+    FlattenedChildIterator iter(aContent);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       // Frame construction item construction should not post
       // restyles, so removing restyle flags here is safe.
       if (child->IsElement()) {
         child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
       }
       if (addChildItems) {
         AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
                                   itemsToConstruct);
@@ -11230,24 +11197,21 @@ nsCSSFrameConstructor::BuildInlineChildI
   
   if (!aItemIsWithinSVGText) {
     // Probe for generated content before
     CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
                                nsCSSPseudoElements::ePseudo_before,
                                aParentItem.mChildItems);
   }
 
-  ChildIterator iter, last;
-  for (ChildIterator::Init(parentContent, &iter, &last);
-       iter != last;
-       ++iter) {
+  FlattenedChildIterator iter(parentContent);
+  for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
     // Manually check for comments/PIs, since we don't have a frame to pass to
     // AddFrameConstructionItems.  We know our parent is a non-replaced inline,
     // so there is no need to do the NeedFrameFor check.
-    nsIContent* content = *iter;
     content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
     if (content->IsNodeOfType(nsINode::eCOMMENT) ||
         content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
       continue;
     }
     if (content->IsElement()) {
       // See comment explaining why we need to remove the "is possible
       // restyle root" flags in AddFrameConstructionItems.  But note
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -31,25 +31,33 @@ class nsStyleContext;
 struct nsStyleContent;
 struct nsStyleDisplay;
 class nsIPresShell;
 class nsIDOMHTMLSelectElement;
 class nsPresContext;
 class nsStyleChangeList;
 class nsIFrame;
 struct nsGenConInitializer;
-class ChildIterator;
+
 class nsICSSAnonBoxPseudo;
 class nsPageContentFrame;
 struct PendingBinding;
 class nsRefreshDriver;
 
 class nsFrameConstructorState;
 class nsFrameConstructorSaveState;
 
+namespace mozilla {
+namespace dom {
+
+class FlattenedChildIterator;
+
+}
+}
+
 class nsCSSFrameConstructor : public nsFrameManager
 {
   friend class nsRefreshDriver;
 
 public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::css::RestyleTracker RestyleTracker;
   typedef mozilla::css::OverflowChangedTracker OverflowChangedTracker;
@@ -105,17 +113,16 @@ private:
                                     bool aAllowLazyConstruction);
   
   // Checks if the children of aContainer in the range [aStartChild, aEndChild)
   // can be inserted/appended to one insertion point together. If so, returns
   // that insertion point. If not, returns null and issues single
   // ContentInserted calls for each child.  aEndChild = nullptr indicates that we
   // are dealing with an append.
   nsIFrame* GetRangeInsertionPoint(nsIContent* aContainer,
-                                   nsIFrame* aParentFrame,
                                    nsIContent* aStartChild,
                                    nsIContent* aEndChild,
                                    bool aAllowLazyConstruction);
 
   // Returns true if parent was recreated due to frameset child, false otherwise.
   bool MaybeRecreateForFrameset(nsIFrame* aParentFrame,
                                   nsIContent* aStartChild,
                                   nsIContent* aEndChild);
@@ -350,20 +357,19 @@ public:
                                   nsIFrame*       aFrame,
                                   nsIFrame*       aParentFrame,
                                   bool            aIsFluid = true);
 
   // Copy over fixed frames from aParentFrame's prev-in-flow
   nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame);
 
   // Get the XBL insertion point for a child
-  nsresult GetInsertionPoint(nsIFrame*     aParentFrame,
-                             nsIContent*   aChildContent,
-                             nsIFrame**    aInsertionPoint,
-                             bool*       aMultiple = nullptr);
+  nsIFrame* GetInsertionPoint(nsIContent* aContainer,
+                              nsIContent* aChildContent,
+                              bool*       aMultiple = nullptr);
 
   nsresult CreateListBoxContent(nsPresContext* aPresContext,
                                 nsIFrame*       aParentFrame,
                                 nsIFrame*       aPrevFrame,
                                 nsIContent*     aChild,
                                 nsIFrame**      aResult,
                                 bool            aIsAppend,
                                 bool            aIsScrollbar,
@@ -1400,17 +1406,17 @@ private:
   nsIFrame* ConstructNonScrollableBlock(nsFrameConstructorState& aState,
                                         FrameConstructionItem&   aItem,
                                         nsIFrame*                aParentFrame,
                                         const nsStyleDisplay*    aDisplay,
                                         nsFrameItems&            aFrameItems);
 
   /**
    * Construct the frames for the children of aContent.  "children" is defined
-   * as "whatever ChildIterator returns for aContent".  This means we're
+   * as "whatever FlattenedChildIterator returns for aContent".  This means we're
    * basically operating on children in the "flattened tree" per sXBL/XBL2.
    * This method will also handle constructing ::before, ::after,
    * ::first-letter, and ::first-line frames, as needed and if allowed.
    *
    * If the parent is a float containing block, this method will handle pushing
    * it as the float containing block in aState (so there's no need for callers
    * to push it themselves).
    *
@@ -1779,25 +1785,23 @@ private:
   nsIFrame* FindFrameForContentSibling(nsIContent* aContent,
                                        nsIContent* aTargetContent,
                                        uint8_t& aTargetContentDisplay,
                                        bool aPrevSibling);
 
   // Find the ``rightmost'' frame for the content immediately preceding the one
   // aIter points to, following continuations if necessary.  aIter is passed by
   // value on purpose, so as not to modify the caller's iterator.
-  nsIFrame* FindPreviousSibling(const ChildIterator& aFirst,
-                                ChildIterator aIter,
+  nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter,
                                 uint8_t& aTargetContentDisplay);
 
   // Find the frame for the content node immediately following the one aIter
   // points to, following continuations if necessary.  aIter is passed by value
   // on purpose, so as not to modify the caller's iterator.
-  nsIFrame* FindNextSibling(ChildIterator aIter,
-                            const ChildIterator& aLast,
+  nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter,
                             uint8_t& aTargetContentDisplay);
 
   // Find the right previous sibling for an insertion.  This also updates the
   // parent frame to point to the correct continuation of the parent frame to
   // use, and returns whether this insertion is to be treated as an append.
   // aChild is the child being inserted.
   // aIsRangeInsertSafe returns whether it is safe to do a range insert with
   // aChild being the first child in the range. It is the callers'
deleted file mode 100644
--- a/layout/base/nsChildIterator.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-// vim:cindent:ts=2:et:sw=2:
-/* 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/. */
-
-/*
- * used by nsCSSFrameConstructor to determine and iterate the child list
- * used to construct frames (normal children or something from XBL)
- */
-
-#include "nsChildIterator.h"
-#include "nsIDocument.h"
-#include "nsBindingManager.h"
-
-nsresult
-ChildIterator::Init(nsIContent*    aContent,
-                    ChildIterator* aFirst,
-                    ChildIterator* aLast)
-{
-  // Initialize out parameters to be equal, in case of failure.
-  aFirst->mContent = aLast->mContent = nullptr;
-  aFirst->mChild   = aLast->mChild   = nullptr;
-  
-  NS_PRECONDITION(aContent != nullptr, "no content");
-  if (! aContent)
-    return NS_ERROR_NULL_POINTER;
-
-  nsIDocument* doc = aContent->OwnerDoc();
-
-  // If this node has XBL children, then use them. Otherwise, just use
-  // the vanilla content APIs.
-  nsINodeList* nodes = doc->BindingManager()->GetXBLChildNodesFor(aContent);
-
-  aFirst->mContent = aContent;
-  aLast->mContent  = aContent;
-  aFirst->mNodes   = nodes;
-  aLast->mNodes    = nodes;
-
-  if (nodes) {
-    uint32_t length;
-    nodes->GetLength(&length);
-    aFirst->mIndex = 0;
-    aLast->mIndex = length;
-  } else {
-    aFirst->mChild = aContent->GetFirstChild();
-    aLast->mChild = nullptr;
-  }
-
-  return NS_OK;
-}
deleted file mode 100644
--- a/layout/base/nsChildIterator.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-// vim:cindent:ts=2:et:sw=2:
-/* 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/. */
-
-/*
- * used by nsCSSFrameConstructor to determine and iterate the child list
- * used to construct frames (normal children or something from XBL)
- */
-
-#include "nsCOMPtr.h"
-#include "nsIContent.h"
-#include "nsINodeList.h"
-
-/**
- * Helper class for iterating children during frame construction.
- * This class should always be used in lieu of the straight content
- * node APIs, since it handles XBL-generated anonymous content as
- * well.
- */
-class MOZ_STACK_CLASS ChildIterator
-{
-protected:
-  nsIContent* mContent;
-  // If mNodes is non-null (so XBLInvolved() is true), mIndex is the
-  // index into mNodes for our current position.  Otherwise, mChild is
-  // our current child (which might be null if we're done iterating).
-  union {
-    uint32_t mIndex;
-    nsIContent* mChild;
-  };
-  nsINodeList* mNodes;
-
-public:
-  ChildIterator()
-    : mContent(nullptr), mChild(0), mNodes(nullptr) {}
-
-  ChildIterator(const ChildIterator& aOther)
-    : mContent(aOther.mContent),
-      mNodes(aOther.mNodes) {
-    if (XBLInvolved()) {
-      mIndex = aOther.mIndex;
-    } else {
-      mChild = aOther.mChild;
-    }
-  }
-
-  ChildIterator& operator=(const ChildIterator& aOther) {
-    mContent = aOther.mContent;
-    mNodes = aOther.mNodes;
-    if (XBLInvolved()) {
-      mIndex = aOther.mIndex;
-    } else {
-      mChild = aOther.mChild;
-    }
-    return *this;
-  }
-
-  ChildIterator& operator++() {
-    if (XBLInvolved()) {
-      ++mIndex;
-    } else {
-      NS_ASSERTION(mChild, "Walking off end of list?");
-      mChild = mChild->GetNextSibling();
-    }
-
-    return *this;
-  }
-
-  ChildIterator operator++(int) {
-    ChildIterator result(*this);
-    ++(*this);
-    return result;
-  }
-
-  ChildIterator& operator--() {
-    if (XBLInvolved()) {
-      --mIndex;
-    } else if (mChild) {
-      mChild = mChild->GetPreviousSibling();
-      NS_ASSERTION(mChild, "Walking off beginning of list");
-    } else {
-      mChild = mContent->GetLastChild();
-    }
-    return *this;
-  }
-
-  ChildIterator operator--(int) {
-    ChildIterator result(*this);
-    --(*this);
-    return result;
-  }
-
-  nsIContent* get() const {
-    if (XBLInvolved()) {
-      return mNodes->Item(mIndex);
-    }
-
-    return mChild;
-  }
-
-  nsIContent* operator*() const { return get(); }
-
-  bool operator==(const ChildIterator& aOther) const {
-    if (XBLInvolved()) {
-      return mContent == aOther.mContent && mIndex == aOther.mIndex;
-    }
-
-    return mContent == aOther.mContent && mChild == aOther.mChild;
-  }
-
-  bool operator!=(const ChildIterator& aOther) const {
-    return !aOther.operator==(*this);
-  }
-
-  void seek(nsIContent* aContent) {
-    if (XBLInvolved()) {
-      int32_t index = mNodes->IndexOf(aContent);
-      // XXXbz I wish we could assert that index != -1, but it seems to not be
-      // the case in some XBL cases with filtered insertion points and no
-      // default insertion point.  I will now claim that XBL's management of
-      // its insertion points is broken in those cases, since it's returning an
-      // insertion parent for a node that doesn't actually have the node in its
-      // child list according to ChildIterator.  See bug 474324.
-      if (index != -1) {
-        mIndex = index;
-      } else {
-        // If aContent isn't going to get hit by this iterator, just seek to the
-        // end of the list for lack of anything better to do.
-        mIndex = length();
-      }
-    } else if (aContent->GetParent() == mContent) {
-      mChild = aContent;
-    } else {
-      // XXXbz I wish we could assert this doesn't happen, but I think that's
-      // not necessarily the case when called from ContentInserted if
-      // first-letter frames are about.
-      mChild = nullptr;
-    }
-  }
-
-  bool XBLInvolved() const { return mNodes != nullptr; }
-
-  /**
-   * Create a pair of ChildIterators for a content node. aFirst will
-   * point to the first child of aContent; aLast will point one past
-   * the last child of aContent.
-   */
-  static nsresult Init(nsIContent*    aContent,
-                       ChildIterator* aFirst,
-                       ChildIterator* aLast);
-
-private:
-  uint32_t length() {
-    NS_PRECONDITION(XBLInvolved(), "Don't call me");
-    uint32_t l;
-    mNodes->GetLength(&l);
-    return l;
-  }
-};
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -55,16 +55,17 @@
 #include "nsUnicharUtils.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 #include "nsAutoPtr.h"
 #include "imgIRequest.h"
 #include "nsTransitionManager.h"
 #include "RestyleTracker.h"
 #include "nsAbsoluteContainingBlock.h"
+#include "ChildIterator.h"
 
 #include "nsFrameManager.h"
 #include "nsRuleProcessorData.h"
 #include "GeckoProfiler.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
@@ -393,29 +394,22 @@ nsFrameManager::ClearAllUndisplayedConte
 #endif
 
   if (mUndisplayedMap) {
     mUndisplayedMap->RemoveNodesFor(aParentContent);
   }
 
   // Need to look at aParentContent's content list due to XBL insertions.
   // Nodes in aParentContent's content list do not have aParentContent as a
-  // parent, but are treated as children of aParentContent. We get access to
-  // the content list via GetXBLChildNodesFor and just ignore any nodes we
-  // don't care about.
-  nsINodeList* list =
-    aParentContent->OwnerDoc()->BindingManager()->GetXBLChildNodesFor(aParentContent);
-  if (list) {
-    uint32_t length;
-    list->GetLength(&length);
-    for (uint32_t i = 0; i < length; ++i) {
-      nsIContent* child = list->Item(i);
-      if (child->GetParent() != aParentContent) {
-        ClearUndisplayedContentIn(child, child->GetParent());
-      }
+  // parent, but are treated as children of aParentContent. We iterate over
+  // the flattened content list and just ignore any nodes we don't care about.
+  FlattenedChildIterator iter(aParentContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+    if (child->GetParent() != aParentContent) {
+      ClearUndisplayedContentIn(child, child->GetParent());
     }
   }
 }
 
 //----------------------------------------------------------------------
 nsresult
 nsFrameManager::AppendFrames(nsIFrame*       aParentFrame,
                              ChildListID     aListID,
--- a/layout/inspector/src/Makefile.in
+++ b/layout/inspector/src/Makefile.in
@@ -12,14 +12,15 @@ include $(DEPTH)/config/autoconf.mk
 
 LIBXUL_LIBRARY = 1
 FAIL_ON_WARNINGS = 1
 
 FORCE_STATIC_LIB = 1
 
 LOCAL_INCLUDES	+= \
 		-I$(srcdir)/../../style \
+		-I$(topsrcdir)/content/base/src \
 		-I$(topsrcdir)/content/xbl/src \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/layout/inspector/src/inDOMUtils.cpp
+++ b/layout/inspector/src/inDOMUtils.cpp
@@ -17,20 +17,22 @@
 #include "nsIStyleRule.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsICSSStyleRuleDOMWrapper.h"
 #include "nsIDOMWindow.h"
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsIMutableArray.h"
 #include "nsBindingManager.h"
+#include "ChildIterator.h"
 #include "nsComputedDOMStyle.h"
 #include "nsEventStateManager.h"
 #include "nsIAtom.h"
 #include "nsRange.h"
+#include "nsContentList.h"
 #include "mozilla/dom/Element.h"
 #include "nsCSSStyleSheet.h"
 #include "nsRuleWalker.h"
 #include "nsRuleProcessorData.h"
 #include "nsCSSRuleProcessor.h"
 #include "mozilla/dom/InspectorUtilsBinding.h"
 
 using namespace mozilla;
--- a/layout/inspector/src/inDeepTreeWalker.cpp
+++ b/layout/inspector/src/inDeepTreeWalker.cpp
@@ -9,17 +9,19 @@
 
 #include "nsString.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMNodeList.h"
 #include "nsServiceManagerUtils.h"
 #include "inIDOMUtils.h"
 #include "nsIContent.h"
-#include "nsBindingManager.h"
+#include "nsContentList.h"
+#include "ChildIterator.h"
+#include "mozilla/dom/Element.h"
 
 /*****************************************************************************
  * This implementation does not currently operaate according to the W3C spec.
  * In particular it does NOT handle DOM mutations during the walk.  It also
  * ignores whatToShow and the filter.
  *****************************************************************************/
 
 ////////////////////////////////////////////////////
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -56,16 +56,17 @@
 #include "nsNetCID.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/Element.h"
 #include "nsNthIndexCache.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Util.h"
+#include "nsXBLChildrenElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
 
 static bool gSupportVisitedPseudo = true;
 
@@ -3388,16 +3389,25 @@ TreeMatchContext::InitAncestors(Element 
     nsAutoTArray<Element*, 50> ancestors;
     Element* cur = aElement;
     do {
       ancestors.AppendElement(cur);
       nsINode* parent = cur->GetParentNode();
       if (!parent->IsElement()) {
         break;
       }
+
+      if (parent->AsElement()->NodeInfo()->Equals(nsGkAtoms::children,
+                                                  kNameSpaceID_XBL)) {
+        parent = parent->GetParentNode();
+        if (!parent->IsElement()) {
+          break;
+        }
+      }
+
       cur = parent->AsElement();
     } while (true);
 
     // Now push them in reverse order.
     for (uint32_t i = ancestors.Length(); i-- != 0; ) {
       mAncestorFilter.PushAncestor(ancestors[i]);
       PushStyleScope(ancestors[i]);
     }
@@ -3456,13 +3466,24 @@ AncestorFilter::PopAncestor()
 }
 
 #ifdef DEBUG
 void
 AncestorFilter::AssertHasAllAncestors(Element *aElement) const
 {
   nsINode* cur = aElement->GetParentNode();
   while (cur && cur->IsElement()) {
+    // We build our ancestor tree from the top-down. However, because
+    // <xbl:children> elements don't have frames and don't directly
+    // participate in the style tree, they never get pushed as ancestors.
+    // Skip them on the way up as we do on the way down (see also
+    // mozilla::dom::ExplicitChildIterator).
+    if (cur->AsElement()->NodeInfo()->Equals(nsGkAtoms::children,
+                                             kNameSpaceID_XBL)) {
+      cur = cur->GetParentNode();
+      continue;
+    }
+
     MOZ_ASSERT(mElements.Contains(cur));
     cur = cur->GetParentNode();
   }
 }
 #endif
--- a/layout/xul/base/src/nsListBoxBodyFrame.cpp
+++ b/layout/xul/base/src/nsListBoxBodyFrame.cpp
@@ -27,24 +27,26 @@
 #include "nsITimer.h"
 #include "nsAutoPtr.h"
 #include "nsStyleSet.h"
 #include "nsPIBoxObject.h"
 #include "nsINodeInfo.h"
 #include "nsLayoutUtils.h"
 #include "nsPIListBoxObject.h"
 #include "nsContentUtils.h"
-#include "nsChildIterator.h"
+#include "ChildIterator.h"
 #include "nsRenderingContext.h"
 #include <algorithm>
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
+using namespace mozilla::dom;
+
 /////////////// nsListScrollSmoother //////////////////
 
 /* A mediator used to smooth out scrolling. It works by seeing if 
  * we have time to scroll the amount of rows requested. This is determined
  * by measuring how long it takes to scroll a row. If we can scroll the 
  * rows in time we do so. If not we start a timer and skip the request. We
  * do this until the timer finally first because the user has stopped moving
  * the mouse. Then do all the queued requests in on shot.
@@ -530,21 +532,18 @@ nsListBoxBodyFrame::ScrollByLines(int32_
 // walks the DOM to get the zero-based row index of the content
 nsresult
 nsListBoxBodyFrame::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
 {
   if (aItem) {
     *_retval = 0;
     nsCOMPtr<nsIContent> itemContent(do_QueryInterface(aItem));
 
-    ChildIterator iter, last;
-    for (ChildIterator::Init(mContent, &iter, &last);
-         iter != last;
-         ++iter) {
-      nsIContent *child = (*iter);
+    FlattenedChildIterator iter(mContent);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       // we hit a list row, count it
       if (child->Tag() == nsGkAtoms::listitem) {
         // is this it?
         if (child == itemContent)
           return NS_OK;
 
         ++(*_retval);
       }
@@ -557,23 +556,20 @@ nsListBoxBodyFrame::GetIndexOfItem(nsIDO
 }
 
 nsresult
 nsListBoxBodyFrame::GetItemAtIndex(int32_t aIndex, nsIDOMElement** aItem)
 {
   *aItem = nullptr;
   if (aIndex < 0)
     return NS_OK;
-  
-  int32_t itemCount = 0;
-  ChildIterator iter, last;
-  for (ChildIterator::Init(mContent, &iter, &last);
-       iter != last;
-       ++iter) {
-    nsIContent *child = (*iter);
+
+  PRInt32 itemCount = 0;
+  FlattenedChildIterator iter(mContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     // we hit a list row, check if it is the one we are looking for
     if (child->Tag() == nsGkAtoms::listitem) {
       // is this it?
       if (itemCount == aIndex) {
         return CallQueryInterface(child, aItem);
       }
       ++itemCount;
     }
@@ -665,24 +661,18 @@ nsListBoxBodyFrame::ComputeIntrinsicWidt
     nsMargin margin(0,0,0,0);
 
     if (styleContext->StylePadding()->GetPadding(margin))
       width += margin.LeftRight();
     width += styleContext->StyleBorder()->GetComputedBorder().LeftRight();
     if (styleContext->StyleMargin()->GetMargin(margin))
       width += margin.LeftRight();
 
-
-    ChildIterator iter, last;
-    uint32_t i = 0;
-    for (ChildIterator::Init(mContent, &iter, &last);
-         iter != last && i < 100;
-         ++iter, ++i) {
-      nsIContent *child = (*iter);
-
+    FlattenedChildIterator iter(mContent);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       if (child->Tag() == nsGkAtoms::listitem) {
         nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
         if (rendContext) {
           nsAutoString value;
           uint32_t textCount = child->GetChildCount();
           for (uint32_t j = 0; j < textCount; ++j) {
             nsIContent* text = child->GetChildAt(j);
             if (text && text->IsNodeOfType(nsINode::eTEXT)) {
@@ -709,23 +699,21 @@ nsListBoxBodyFrame::ComputeIntrinsicWidt
   mStringWidth = largestWidth;
   return mStringWidth;
 }
 
 void
 nsListBoxBodyFrame::ComputeTotalRowCount()
 {
   mRowCount = 0;
-
-  ChildIterator iter, last;
-  for (ChildIterator::Init(mContent, &iter, &last);
-       iter != last;
-       ++iter) {
-    if ((*iter)->Tag() == nsGkAtoms::listitem)
+  FlattenedChildIterator iter(mContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+    if (child->Tag() == nsGkAtoms::listitem) {
       ++mRowCount;
+    }
   }
 }
 
 void
 nsListBoxBodyFrame::PostReflowCallback()
 {
   if (!mReflowCallbackPosted) {
     mReflowCallbackPosted = true;
@@ -1372,26 +1360,27 @@ nsListBoxBodyFrame::OnContentRemoved(nsP
         VerticalScroll(mYPosition);
       }
     } else if (mCurrentIndex > 0) {
       // At this point, we know we have a scrollbar, and we need to know 
       // if we are scrolled to the last row.  In this case, the behavior
       // of the scrollbar is to stay locked to the bottom.  Since we are
       // removing visible content, the first visible row will have to move
       // down by one, and we will have to insert a new frame at the top.
-      
+
       // if the last content node has a frame, we are scrolled to the bottom
-      ChildIterator iter, last;
-      ChildIterator::Init(mContent, &iter, &last);
-      if (iter != last) {
-        iter = last;
-        --iter;
-        nsIContent *lastChild = *iter;
+      nsIContent* lastChild = nullptr;
+      FlattenedChildIterator iter(mContent);
+      for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+        lastChild = child;
+      }
+
+      if (lastChild) {
         nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
-      
+
         if (lastChildFrame) {
           mTopFrame = nullptr;
           mRowsToPrepend = 1;
           --mCurrentIndex;
           mYPosition = mCurrentIndex*mRowHeight;
           VerticalScroll(mYPosition);
         }
       }
@@ -1414,53 +1403,46 @@ nsListBoxBodyFrame::OnContentRemoved(nsP
 }
 
 void
 nsListBoxBodyFrame::GetListItemContentAt(int32_t aIndex, nsIContent** aContent)
 {
   *aContent = nullptr;
 
   int32_t itemsFound = 0;
-  ChildIterator iter, last;
-  for (ChildIterator::Init(mContent, &iter, &last);
-       iter != last;
-       ++iter) {
-    nsIContent *kid = (*iter);
-    if (kid->Tag() == nsGkAtoms::listitem) {
+  FlattenedChildIterator iter(mContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+    if (child->Tag() == nsGkAtoms::listitem) {
       ++itemsFound;
       if (itemsFound-1 == aIndex) {
-        *aContent = kid;
+        *aContent = child;
         NS_IF_ADDREF(*aContent);
         return;
       }
     }
   }
 }
 
 void
 nsListBoxBodyFrame::GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, int32_t& aSiblingIndex)
 {
   *aContent = nullptr;
   aSiblingIndex = -1;
   nsIContent *prevKid = nullptr;
-  ChildIterator iter, last;
-  for (ChildIterator::Init(mContent, &iter, &last);
-       iter != last;
-       ++iter) {
-    nsIContent *kid = (*iter);
-
-    if (kid->Tag() == nsGkAtoms::listitem) {
+  FlattenedChildIterator iter(mContent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+    if (child->Tag() == nsGkAtoms::listitem) {
       ++aSiblingIndex;
       if (prevKid == aListItem) {
-        *aContent = kid;
+        *aContent = child;
         NS_IF_ADDREF(*aContent);
         return;
       }
     }
-    prevKid = kid;
+    prevKid = child;
   }
 
   aSiblingIndex = -1; // no match, so there is no next sibling
 }
 
 void
 nsListBoxBodyFrame::RemoveChildFrame(nsBoxLayoutState &aState,
                                      nsIFrame         *aFrame)
--- a/layout/xul/base/src/nsListBoxObject.cpp
+++ b/layout/xul/base/src/nsListBoxObject.cpp
@@ -8,16 +8,17 @@
 #include "nsBoxObject.h"
 #include "nsIFrame.h"
 #include "nsBindingManager.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNodeList.h"
 #include "nsGkAtoms.h"
 #include "nsIScrollableFrame.h"
 #include "nsListBoxBodyFrame.h"
+#include "ChildIterator.h"
 
 class nsListBoxObject : public nsPIListBoxObject, public nsBoxObject
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSILISTBOXOBJECT
 
   // nsPIListBoxObject
@@ -114,40 +115,32 @@ nsListBoxObject::GetIndexOfItem(nsIDOMEl
   nsListBoxBodyFrame* body = GetListBoxBody(true);
   if (body)
     return body->GetIndexOfItem(aElement, aResult);
   return NS_OK;
 }
 
 //////////////////////
 
-static void
-FindBodyContent(nsIContent* aParent, nsIContent** aResult)
+static nsIContent*
+FindBodyContent(nsIContent* aParent)
 {
   if (aParent->Tag() == nsGkAtoms::listboxbody) {
-    *aResult = aParent;
-    NS_IF_ADDREF(*aResult);
+    return aParent;
   }
-  else {
-    nsCOMPtr<nsIDOMNodeList> kids;
-    aParent->OwnerDoc()->BindingManager()->GetXBLChildNodesFor(aParent, getter_AddRefs(kids));
-    if (!kids) return;
 
-    uint32_t i;
-    kids->GetLength(&i);
-    // start from the end, cuz we're smart and we know the listboxbody is probably at the end
-    while (i > 0) {
-      nsCOMPtr<nsIDOMNode> childNode;
-      kids->Item(--i, getter_AddRefs(childNode));
-      nsCOMPtr<nsIContent> childContent(do_QueryInterface(childNode));
-      FindBodyContent(childContent, aResult);
-      if (*aResult)
-        break;
+  mozilla::dom::FlattenedChildIterator iter(aParent);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+    nsIContent* result = FindBodyContent(child);
+    if (result) {
+      return result;
     }
   }
+
+  return nullptr;
 }
 
 nsListBoxBodyFrame*
 nsListBoxObject::GetListBoxBody(bool aFlush)
 {
   if (mListBoxBody) {
     return mListBoxBody;
   }
@@ -159,18 +152,17 @@ nsListBoxObject::GetListBoxBody(bool aFl
 
   nsIFrame* frame = aFlush ? 
                       GetFrame(false) /* does Flush_Frames */ :
                       mContent->GetPrimaryFrame();
   if (!frame)
     return nullptr;
 
   // Iterate over our content model children looking for the body.
-  nsCOMPtr<nsIContent> content;
-  FindBodyContent(frame->GetContent(), getter_AddRefs(content));
+  nsCOMPtr<nsIContent> content = FindBodyContent(frame->GetContent());
 
   if (!content)
     return nullptr;
 
   // this frame will be a nsGFXScrollFrame
   frame = content->GetPrimaryFrame();
   if (!frame)
      return nullptr;
--- a/layout/xul/base/src/nsMenuBarFrame.cpp
+++ b/layout/xul/base/src/nsMenuBarFrame.cpp
@@ -165,17 +165,18 @@ nsMenuBarFrame::ToggleMenuActiveState()
 
 static void
 GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
                   nsIFrame** aResult)
 {
   nsIContent* child = nullptr;
   if (aChild)
     child = aChild->GetContent();
-  aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
+  *aResult = aShell->FrameConstructor()->
+    GetInsertionPoint(aFrame->GetContent(), child);
 }
 
 nsMenuFrame*
 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
 {
   uint32_t charCode;
   aKeyEvent->GetCharCode(&charCode);
 
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -1617,19 +1617,18 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
 {
   uint32_t charCode, keyCode;
   aKeyEvent->GetCharCode(&charCode);
   aKeyEvent->GetKeyCode(&keyCode);
 
   doAction = false;
 
   // Enumerate over our list of frames.
-  nsIFrame* immediateParent = nullptr;
-  PresContext()->PresShell()->
-    FrameConstructor()->GetInsertionPoint(this, nullptr, &immediateParent);
+  nsIFrame* immediateParent = PresContext()->PresShell()->
+    FrameConstructor()->GetInsertionPoint(GetContent(), nullptr);
   if (!immediateParent)
     immediateParent = this;
 
   uint32_t matchCount = 0, matchShortcutCount = 0;
   bool foundActive = false;
   bool isShortcut;
   nsMenuFrame* frameBefore = nullptr;
   nsMenuFrame* frameAfter = nullptr;
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -1949,20 +1949,19 @@ nsXULPopupManager::HandleKeyboardNavigat
   return false;
 }
 
 nsMenuFrame*
 nsXULPopupManager::GetNextMenuItem(nsIFrame* aParent,
                                    nsMenuFrame* aStart,
                                    bool aIsPopup)
 {
-  nsIFrame* immediateParent = nullptr;
   nsPresContext* presContext = aParent->PresContext();
-  presContext->PresShell()->
-    FrameConstructor()->GetInsertionPoint(aParent, nullptr, &immediateParent);
+  nsIFrame* immediateParent = presContext->PresShell()->
+    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
   if (!immediateParent)
     immediateParent = aParent;
 
   nsIFrame* currFrame = nullptr;
   if (aStart)
     currFrame = aStart->GetNextSibling();
   else 
     currFrame = immediateParent->GetFirstPrincipalChild();
@@ -1991,20 +1990,19 @@ nsXULPopupManager::GetNextMenuItem(nsIFr
   return aStart;
 }
 
 nsMenuFrame*
 nsXULPopupManager::GetPreviousMenuItem(nsIFrame* aParent,
                                        nsMenuFrame* aStart,
                                        bool aIsPopup)
 {
-  nsIFrame* immediateParent = nullptr;
   nsPresContext* presContext = aParent->PresContext();
-  presContext->PresShell()->
-    FrameConstructor()->GetInsertionPoint(aParent, nullptr, &immediateParent);
+  nsIFrame* immediateParent = presContext->PresShell()->
+    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
   if (!immediateParent)
     immediateParent = aParent;
 
   const nsFrameList& frames(immediateParent->PrincipalChildList());
 
   nsIFrame* currFrame = nullptr;
   if (aStart)
     currFrame = aStart->GetPrevSibling();
--- a/layout/xul/tree/Makefile.in
+++ b/layout/xul/tree/Makefile.in
@@ -11,16 +11,17 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 LIBXUL_LIBRARY  = 1
 FAIL_ON_WARNINGS = 1
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir) \
 		-I$(topsrcdir)/content/events/src \
+		-I$(topsrcdir)/content/base/src \
 		-I$(srcdir)/../base/src \
 		-I$(srcdir)/../../base \
 		-I$(srcdir)/../../generic \
 		-I$(srcdir)/../../style \
 		-I$(srcdir)/../../forms \
 		$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -40,17 +40,16 @@
 #include "nsWidgetsCID.h"
 #include "nsBoxFrame.h"
 #include "nsBoxObject.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsBoxLayoutState.h"
 #include "nsTreeContentView.h"
 #include "nsTreeUtils.h"
-#include "nsChildIterator.h"
 #include "nsITheme.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "nsINodeInfo.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
--- a/layout/xul/tree/nsTreeBoxObject.cpp
+++ b/layout/xul/tree/nsTreeBoxObject.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTreeBoxObject.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMXULElement.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsTreeContentView.h"
 #include "nsITreeSelection.h"
-#include "nsChildIterator.h"
+#include "ChildIterator.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsTreeBodyFrame.h"
 
 NS_IMPL_CYCLE_COLLECTION_1(nsTreeBoxObject, mView)
 
 NS_IMPL_ADDREF_INHERITED(nsTreeBoxObject, nsBoxObject)
 NS_IMPL_RELEASE_INHERITED(nsTreeBoxObject, nsBoxObject)
@@ -47,40 +47,36 @@ nsTreeBoxObject::nsTreeBoxObject()
 {
 }
 
 nsTreeBoxObject::~nsTreeBoxObject()
 {
   /* destructor code */
 }
 
-
-static void FindBodyElement(nsIContent* aParent, nsIContent** aResult)
+static nsIContent* FindBodyElement(nsIContent* aParent)
 {
-  *aResult = nullptr;
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aParent, &iter, &last); iter != last; ++iter) {
-    nsCOMPtr<nsIContent> content = *iter;
-
+  mozilla::dom::FlattenedChildIterator iter(aParent);
+  for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
     nsINodeInfo *ni = content->NodeInfo();
     if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) {
-      *aResult = content;
-      NS_ADDREF(*aResult);
-      break;
+      return content;
     } else if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) {
       // There are nesting tree elements. Only the innermost should
       // find the treechilren.
-      break;
+      return nullptr;
     } else if (content->IsElement() &&
                !ni->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
-      FindBodyElement(content, aResult);
-      if (*aResult)
-        break;
+      nsIContent* result = FindBodyElement(content);
+      if (result)
+        return result;
     }
   }
+
+  return nullptr;
 }
 
 nsTreeBodyFrame*
 nsTreeBoxObject::GetTreeBody(bool aFlushLayout)
 {
   // Make sure our frames are up to date, and layout as needed.  We
   // have to do this before checking for our cached mTreeBody, since
   // it might go away on style flush, and in any case if aFlushLayout
@@ -101,18 +97,17 @@ nsTreeBoxObject::GetTreeBody(bool aFlush
 
   if (!aFlushLayout) {
     frame = GetFrame(aFlushLayout);
     if (!frame)
       return nullptr;
   }
 
   // Iterate over our content model children looking for the body.
-  nsCOMPtr<nsIContent> content;
-  FindBodyElement(frame->GetContent(), getter_AddRefs(content));
+  nsCOMPtr<nsIContent> content = FindBodyElement(frame->GetContent());
   if (!content)
     return nullptr;
 
   frame = content->GetPrimaryFrame();
   if (!frame)
      return nullptr;
 
   // Make sure that the treebodyframe has a pointer to |this|.
--- a/layout/xul/tree/nsTreeContentView.cpp
+++ b/layout/xul/tree/nsTreeContentView.cpp
@@ -3,17 +3,17 @@
  * 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/. */
 
 #include "nsINameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsIBoxObject.h"
 #include "nsTreeUtils.h"
 #include "nsTreeContentView.h"
-#include "nsChildIterator.h"
+#include "ChildIterator.h"
 #include "nsDOMClassInfoID.h"
 #include "nsError.h"
 #include "nsEventStates.h"
 #include "nsINodeInfo.h"
 #include "nsIXULSortService.h"
 #include "nsContentUtils.h"
 #include "nsTreeBodyFrame.h"
 #include "mozilla/dom/Element.h"
@@ -1041,19 +1041,18 @@ nsTreeContentView::NodeWillBeDestroyed(c
 void
 nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex,
                              int32_t* aIndex, nsTArray<nsAutoPtr<Row> >& aRows)
 {
   // Don't allow non-XUL nodes.
   if (!aContent->IsXUL())
     return;
 
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aContent, &iter, &last); iter != last; ++iter) {
-    nsIContent* content = *iter;
+  dom::FlattenedChildIterator iter(aContent);
+  for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
     nsIAtom *tag = content->Tag();
     int32_t count = aRows.Length();
 
     if (content->IsXUL()) {
       if (tag == nsGkAtoms::treeitem)
         SerializeItem(content, aParentIndex, aIndex, aRows);
       else if (tag == nsGkAtoms::treeseparator)
         SerializeSeparator(content, aParentIndex, aIndex, aRows);
@@ -1362,20 +1361,18 @@ nsTreeContentView::GetCell(nsIContent* a
   int32_t colIndex;
   aCol->GetAtom(getter_AddRefs(colAtom));
   aCol->GetIndex(&colIndex);
 
   // Traverse through cells, try to find the cell by "ref" attribute or by cell
   // index in a row. "ref" attribute has higher priority.
   nsIContent* result = nullptr;
   int32_t j = 0;
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aContainer, &iter, &last); iter != last; ++iter) {
-    nsIContent* cell = *iter;
-
+  dom::FlattenedChildIterator iter(aContainer);
+  for (nsIContent* cell = iter.GetNextChild(); cell; cell = iter.GetNextChild()) {
     if (cell->Tag() == nsGkAtoms::treecell) {
       if (colAtom && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
                                        colAtom, eCaseMatters)) {
         result = cell;
         break;
       }
       else if (j == colIndex) {
         result = cell;
--- a/layout/xul/tree/nsTreeUtils.cpp
+++ b/layout/xul/tree/nsTreeUtils.cpp
@@ -1,22 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #include "nsReadableUtils.h"
 #include "nsTreeUtils.h"
-#include "nsChildIterator.h"
+#include "ChildIterator.h"
 #include "nsCRT.h"
 #include "nsIAtom.h"
 #include "nsINameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsINodeInfo.h"
 
+namespace dom = mozilla::dom;
+
 nsresult
 nsTreeUtils::TokenizeProperties(const nsAString& aProperties, AtomArray & aPropertiesArray)
 {
   nsAString::const_iterator end;
   aProperties.EndReading(end);
 
   nsAString::const_iterator iter;
   aProperties.BeginReading(iter);
@@ -47,34 +49,31 @@ nsTreeUtils::TokenizeProperties(const ns
   } while (iter != end);
 
   return NS_OK;
 }
 
 nsIContent*
 nsTreeUtils::GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag)
 {
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aContainer, &iter, &last); iter != last; ++iter) {
-    nsIContent* child = *iter;
-
+  dom::FlattenedChildIterator iter(aContainer);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     if (child->Tag() == aTag) {
       return child;
     }
   }
 
   return nullptr;
 }
 
 nsIContent*
 nsTreeUtils::GetDescendantChild(nsIContent* aContainer, nsIAtom* aTag)
 {
-  ChildIterator iter, last;
-  for (ChildIterator::Init(aContainer, &iter, &last); iter != last; ++iter) {
-    nsIContent* child = *iter;
+  dom::FlattenedChildIterator iter(aContainer);
+  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     if (child->Tag() == aTag) {
       return child;
     }
 
     child = GetDescendantChild(child, aTag);
     if (child) {
       return child;
     }
--- a/toolkit/content/tests/chrome/test_bug562554.xul
+++ b/toolkit/content/tests/chrome/test_bug562554.xul
@@ -1,28 +1,37 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 <!--
   XUL Widget Test for bug 562554
   -->
-<window title="Bug 557987" width="400" height="400"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<window title="Bug 562554" width="400" height="400"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>  
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
-  <toolbarbutton type="menu" id="toolbarmenu" height="200px">
+<xbl:bindings xmlns:xbl="http://www.mozilla.org/xbl">
+  <xbl:binding id="menu" display="xul:menu"
+               extends="chrome://global/content/bindings/button.xml#button-base">
+    <xbl:content>
+      <xbl:children includes="menupopup"/>
+      <xul:stack>
+         <xul:button width="100" left="0" top="0" height="30" allowevents="true"
+                     onclick="eventReceived('clickbutton1'); return false;"/>
+         <xul:button width="100" left="70" top="0" height="30" 
+                     onclick="eventReceived('clickbutton2'); return false;"/>
+      </xul:stack>
+    </xbl:content>
+  </xbl:binding>
+</xbl:bindings>
+
+  <toolbarbutton type="menu" id="toolbarmenu" height="200" style="-moz-binding: url(#menu);">
     <menupopup id="menupopup" onpopupshowing="eventReceived('popupshowing'); return false;"/>
-    <stack>
-       <button width="100px" left="0px" height="30px" id="button1"
-        allowevents="true" onclick="eventReceived('clickbutton1'); return false;"/>
-
-       <button width="100px" left="70px" id="button2"
-        onclick="eventReceived('clickbutton2'); return false;"/>
-    </stack>
   </toolbarbutton>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
 <script type="application/javascript">
 <![CDATA[
 
@@ -35,23 +44,23 @@ function test() {
   nextTest();
 }
 
 let tests = [
   // Click on the toolbarbutton itself - should call popupshowing
   function() synthesizeMouse($("toolbarmenu"), 10, 50, {}, window),
 
   // Click on button1 which has allowevents="true" - should call clickbutton1
-  function() synthesizeMouse($("button1"), 10, 15, {}, window),
+  function() synthesizeMouse($("toolbarmenu"), 10, 15, {}, window),
 
   // Click on button2 where it intersects with button1 - should call popupshowing
-  function() synthesizeMouse($("button2"), 5, 15, {}, window),
+  function() synthesizeMouse($("toolbarmenu"), 85, 15, {}, window),
 
   // Click on button2 outside of intersection - should call popupshowing
-  function() synthesizeMouse($("button2"), 50, 15, {}, window)
+  function() synthesizeMouse($("toolbarmenu"), 150, 15, {}, window)
 ];
 
 function nextTest() {
   if (tests.length) {
     let func = tests.shift();
     func();
     SimpleTest.executeSoon(nextTest);
   } else {