Merge inbound to mozilla-central a=merge
authorarthur.iakab <aiakab@mozilla.com>
Tue, 07 Aug 2018 12:36:18 +0300
changeset 485398 399464979f21
parent 485387 ab8293506be2 (current diff)
parent 485397 d745e4118de7 (diff)
child 485399 fe6020e5c9d9
child 485406 95375f6b578e
child 485432 bf6961db9405
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
399464979f21 / 63.0a1 / 20180807100107 / files
nightly linux64
399464979f21 / 63.0a1 / 20180807100107 / files
nightly mac
399464979f21 / 63.0a1 / 20180807100107 / files
nightly win32
399464979f21 / 63.0a1 / 20180807100107 / files
nightly win64
399464979f21 / 63.0a1 / 20180807100107 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge
toolkit/components/telemetry/Histograms.json
--- a/browser/modules/LightweightThemeChildHelper.jsm
+++ b/browser/modules/LightweightThemeChildHelper.jsm
@@ -8,17 +8,17 @@ ChromeUtils.import("resource://gre/modul
 
 var EXPORTED_SYMBOLS = ["LightweightThemeChildHelper"];
 
 /**
  * LightweightThemeChildHelper forwards theme data to in-content pages.
  */
 var LightweightThemeChildHelper = {
   listener: null,
-  whitelist: [],
+  whitelist: null,
 
   /**
    * Listen to theme updates for the current process
    * @param {Array} whitelist The pages that can receive theme updates.
    */
   listen(whitelist) {
     if (!this.listener) {
       // Clone the whitelist to avoid leaking the global the whitelist
@@ -35,19 +35,20 @@ var LightweightThemeChildHelper = {
 
   /**
    * Update the theme data for the whole process
    * @param {Array} changedKeys The sharedData keys that were changed.
    */
   _updateProcess(changedKeys) {
     const windowEnumerator = Services.ww.getWindowEnumerator();
     while (windowEnumerator.hasMoreElements()) {
-      const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
-      const tabChildGlobal = window.docShell.messageManager;
-      const {chromeOuterWindowID, content} = tabChildGlobal;
+      const {
+        chromeOuterWindowID,
+        content,
+      } = windowEnumerator.getNext().docShell.messageManager;
       if (changedKeys.includes(`theme/${chromeOuterWindowID}`) &&
           content && this.whitelist.has(content.document.documentURI)) {
         this.update(chromeOuterWindowID, content);
       }
     }
   },
 
   /**
--- a/dom/base/Attr.cpp
+++ b/dom/base/Attr.cpp
@@ -250,46 +250,16 @@ Attr::SetTextContentInternal(const nsASt
 }
 
 bool
 Attr::IsNodeOfType(uint32_t aFlags) const
 {
     return false;
 }
 
-uint32_t
-Attr::GetChildCount() const
-{
-  return 0;
-}
-
-nsIContent *
-Attr::GetChildAt_Deprecated(uint32_t aIndex) const
-{
-  return nullptr;
-}
-
-int32_t
-Attr::ComputeIndexOf(const nsINode* aPossibleChild) const
-{
-  return -1;
-}
-
-nsresult
-Attr::InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                        bool aNotify)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-void
-Attr::RemoveChildNode(nsIContent* aKid, bool aNotify)
-{
-}
-
 void
 Attr::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   aVisitor.mCanHandle = true;
 }
 
 void
 Attr::Initialize()
--- a/dom/base/Attr.h
+++ b/dom/base/Attr.h
@@ -65,22 +65,16 @@ public:
   /**
    * Called when our ownerElement is moved into a new document.
    * Updates the nodeinfo of this node.
    */
   nsresult SetOwnerDocument(nsIDocument* aDocument);
 
   // nsINode interface
   virtual bool IsNodeOfType(uint32_t aFlags) const override;
-  virtual uint32_t GetChildCount() const override;
-  virtual nsIContent *GetChildAt_Deprecated(uint32_t aIndex) const override;
-  virtual int32_t ComputeIndexOf(const nsINode* aPossibleChild) const override;
-  virtual nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                                     bool aNotify) override;
-  virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
   virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override;
 
   static void Initialize();
   static void Shutdown();
 
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Attr)
--- a/dom/base/CharacterData.h
+++ b/dom/base/CharacterData.h
@@ -94,43 +94,16 @@ public:
   }
 
   NS_IMPL_FROMNODE_HELPER(CharacterData, IsCharacterData())
 
   virtual void GetNodeValueInternal(nsAString& aNodeValue) override;
   virtual void SetNodeValueInternal(const nsAString& aNodeValue,
                                     ErrorResult& aError) override;
 
-  // nsINode methods
-  uint32_t GetChildCount() const final
-  {
-    return 0;
-  }
-
-  nsIContent* GetChildAt_Deprecated(uint32_t aIndex) const final
-  {
-    return nullptr;
-  }
-
-  int32_t ComputeIndexOf(const nsINode* aPossibleChild) const final
-  {
-    return -1;
-  }
-
-  nsresult InsertChildBefore(nsIContent* aKid,
-                             nsIContent* aBeforeThis,
-                             bool aNotify) final
-  {
-    return NS_OK;
-  }
-
-  void RemoveChildNode(nsIContent* aKid, bool aNotify) final
-  {
-  }
-
   void GetTextContentInternal(nsAString& aTextContent, OOMReporter&) final
   {
     GetNodeValue(aTextContent);
   }
 
   void SetTextContentInternal(const nsAString& aTextContent,
                               nsIPrincipal* aSubjectPrincipal,
                               ErrorResult& aError) final
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -176,19 +176,19 @@ using mozilla::gfx::Matrix4x4;
 #define ASSERT_ELEMENT_SIZE(type, opt_size) \
 template<int a, int b> struct Check##type##Size \
 { \
   static_assert(sizeof(void*) != 8 || a == b, "DOM size changed"); \
 }; \
 Check##type##Size<sizeof(type), opt_size + EXTRA_DOM_ELEMENT_BYTES> g##type##CES;
 
 // Note that mozjemalloc uses a 16 byte quantum, so 128 is a bin/bucket size.
-ASSERT_ELEMENT_SIZE(Element, 120);
-ASSERT_ELEMENT_SIZE(HTMLDivElement, 120);
-ASSERT_ELEMENT_SIZE(HTMLSpanElement, 120);
+ASSERT_ELEMENT_SIZE(Element, 128);
+ASSERT_ELEMENT_SIZE(HTMLDivElement, 128);
+ASSERT_ELEMENT_SIZE(HTMLSpanElement, 128);
 
 #undef ASSERT_ELEMENT_SIZE
 #undef EXTRA_DOM_ELEMENT_BYTES
 
 nsAtom*
 nsIContent::DoGetID() const
 {
   MOZ_ASSERT(HasID(), "Unexpected call");
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1203,37 +1203,16 @@ nsIContent::SetXBLInsertionPoint(nsICont
     slots->mXBLInsertionPoint = aContent;
   } else {
     if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) {
       slots->mXBLInsertionPoint = nullptr;
     }
   }
 }
 
-nsresult
-FragmentOrElement::InsertChildBefore(nsIContent* aKid,
-                                     nsIContent* aBeforeThis,
-                                     bool aNotify)
-{
-  MOZ_ASSERT(aKid, "null ptr");
-
-  int32_t index = aBeforeThis ? ComputeIndexOf(aBeforeThis) : GetChildCount();
-  MOZ_ASSERT(index >= 0);
-
-  return doInsertChildAt(aKid, index, aNotify, mAttrsAndChildren);
-}
-
-void
-FragmentOrElement::RemoveChildNode(nsIContent* aKid, bool aNotify)
-{
-  // Let's keep the node alive.
-  nsCOMPtr<nsIContent> kungFuDeathGrip = aKid;
-  doRemoveChildAt(ComputeIndexOf(aKid), aNotify, aKid, mAttrsAndChildren);
-}
-
 void
 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
                                           OOMReporter& aError)
 {
   if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
     aError.ReportOOM();
   }
 }
@@ -1344,34 +1323,30 @@ public:
 
   void UnbindSubtree(nsIContent* aNode)
   {
     if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
         aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
       return;
     }
     FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
-    uint32_t childCount = container->mAttrsAndChildren.ChildCount();
-    if (childCount) {
+    if (container->HasChildren()) {
       // Invalidate cached array of child nodes
       container->InvalidateChildNodes();
 
-      while (childCount-- > 0) {
+      while (container->HasChildren()) {
         // Hold a strong ref to the node when we remove it, because we may be
-        // the last reference to it.  We need to call TakeChildAt() and
-        // update mFirstChild before calling UnbindFromTree, since this last
-        // can notify various observers and they should really see consistent
+        // the last reference to it.  We need to call DisconnectChild()
+        // before calling UnbindFromTree, since this last can notify various
+        // observers and they should really see consistent
         // tree state.
         // If this code changes, change the corresponding code in
         // FragmentOrElement's and nsDocument's unlink impls.
-        nsCOMPtr<nsIContent> child =
-          container->mAttrsAndChildren.TakeChildAt(childCount);
-        if (childCount == 0) {
-          container->mFirstChild = nullptr;
-        }
+        nsCOMPtr<nsIContent> child = container->GetLastChild();
+        container->DisconnectChild(child);
         UnbindSubtree(child);
         child->UnbindFromTree();
       }
     }
   }
 
   NS_IMETHOD Run() override
   {
@@ -1466,36 +1441,28 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
           tmp->DeleteProperty(effectProps[i]);
         }
       }
     }
   }
 
   // Unlink child content (and unbind our subtree).
   if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
-    uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
-    if (childCount) {
-      // Don't allow script to run while we're unbinding everything.
-      nsAutoScriptBlocker scriptBlocker;
-      while (childCount-- > 0) {
-        // Hold a strong ref to the node when we remove it, because we may be
-        // the last reference to it.  We need to call TakeChildAt() and
-        // update mFirstChild before calling UnbindFromTree, since this last
-        // can notify various observers and they should really see consistent
-        // tree state.
-        // If this code changes, change the corresponding code in nsDocument's
-        // unlink impl and ContentUnbinder::UnbindSubtree.
-        nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
-        if (childCount == 0) {
-          tmp->mFirstChild = nullptr;
-        }
-        child->UnbindFromTree();
-      }
+    // Don't allow script to run while we're unbinding everything.
+    nsAutoScriptBlocker scriptBlocker;
+    while (tmp->HasChildren()) {
+      // Hold a strong ref to the node when we remove it, because we may be
+      // the last reference to it.
+      // If this code changes, change the corresponding code in nsDocument's
+      // unlink impl and ContentUnbinder::UnbindSubtree.
+      nsCOMPtr<nsIContent> child = tmp->GetLastChild();
+      tmp->DisconnectChild(child);
+      child->UnbindFromTree();
     }
-  } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
+  } else if (!tmp->GetParent() && tmp->HasChildren()) {
     ContentUnbinder::Append(tmp);
   } /* else {
     The subtree root will end up to a ContentUnbinder, and that will
     unbind the child nodes.
   } */
 
   // Clear flag here because unlinking slots will clear the
   // containing shadow root pointer.
@@ -2044,35 +2011,29 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
           if (effectSet) {
             effectSet->Traverse(cb);
           }
         }
       }
     }
   }
 
-  // Traverse attribute names and child content.
+  // Traverse attribute names.
   {
     uint32_t i;
     uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
     for (i = 0; i < attrs; i++) {
       const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
       if (!name->IsAtom()) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
                                            "mAttrsAndChildren[i]->NodeInfo()");
         cb.NoteNativeChild(name->NodeInfo(),
                            NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
       }
     }
-
-    uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
-    for (i = 0; i < kids; i++) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
-      cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
-    }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
 NS_INTERFACE_MAP_END_INHERITING(nsIContent)
 
@@ -2123,34 +2084,16 @@ FragmentOrElement::TextIsOnlyWhitespace(
 }
 
 bool
 FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const
 {
   return false;
 }
 
-uint32_t
-FragmentOrElement::GetChildCount() const
-{
-  return mAttrsAndChildren.ChildCount();
-}
-
-nsIContent *
-FragmentOrElement::GetChildAt_Deprecated(uint32_t aIndex) const
-{
-  return mAttrsAndChildren.GetSafeChildAt(aIndex);
-}
-
-int32_t
-FragmentOrElement::ComputeIndexOf(const nsINode* aPossibleChild) const
-{
-  return mAttrsAndChildren.IndexOfChild(aPossibleChild);
-}
-
 static inline bool
 IsVoidTag(nsAtom* aTag)
 {
   static const nsAtom* voidElements[] = {
     nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
     nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
     nsGkAtoms::embed, nsGkAtoms::frame,
     nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -88,22 +88,16 @@ public:
   // refcounting when we're not doing refcount logging, so we can't
   // NS_DECL_ISUPPORTS_INHERITED.
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_INLINE_DECL_REFCOUNTING_INHERITED(FragmentOrElement, nsIContent);
 
   NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   // nsINode interface methods
-  virtual uint32_t GetChildCount() const override;
-  virtual nsIContent *GetChildAt_Deprecated(uint32_t aIndex) const override;
-  virtual int32_t ComputeIndexOf(const nsINode* aPossibleChild) const override;
-  virtual nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                                     bool aNotify) override;
-  virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override;
   virtual void GetTextContentInternal(nsAString& aTextContent,
                                       mozilla::OOMReporter& aError) override;
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       nsIPrincipal* aSubjectPrincipal,
                                       mozilla::ErrorResult& aError) override;
 
   // nsIContent interface methods
   virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) override;
--- a/dom/base/nsAttrAndChildArray.cpp
+++ b/dom/base/nsAttrAndChildArray.cpp
@@ -102,199 +102,16 @@ nsAttrAndChildArray::~nsAttrAndChildArra
     return;
   }
 
   Clear();
 
   free(mImpl);
 }
 
-nsIContent*
-nsAttrAndChildArray::GetSafeChildAt(uint32_t aPos) const
-{
-  if (aPos < ChildCount()) {
-    return ChildAt(aPos);
-  }
-
-  return nullptr;
-}
-
-nsIContent * const *
-nsAttrAndChildArray::GetChildArray(uint32_t* aChildCount) const
-{
-  *aChildCount = ChildCount();
-
-  if (!*aChildCount) {
-    return nullptr;
-  }
-
-  return reinterpret_cast<nsIContent**>(mImpl->mBuffer + AttrSlotsSize());
-}
-
-nsresult
-nsAttrAndChildArray::InsertChildAt(nsIContent* aChild, uint32_t aPos)
-{
-  NS_ASSERTION(aChild, "nullchild");
-  NS_ASSERTION(aPos <= ChildCount(), "out-of-bounds");
-
-  uint32_t offset = AttrSlotsSize();
-  uint32_t childCount = ChildCount();
-
-  NS_ENSURE_TRUE(childCount < ATTRCHILD_ARRAY_MAX_CHILD_COUNT,
-                 NS_ERROR_FAILURE);
-
-  // First try to fit new child in existing childlist
-  if (mImpl && offset + childCount < mImpl->mBufferSize) {
-    void** pos = mImpl->mBuffer + offset + aPos;
-    if (childCount != aPos) {
-      memmove(pos + 1, pos, (childCount - aPos) * sizeof(nsIContent*));
-    }
-    SetChildAtPos(pos, aChild, aPos, childCount);
-
-    SetChildCount(childCount + 1);
-
-    return NS_OK;
-  }
-
-  // Try to fit new child in existing buffer by compressing attrslots
-  if (offset && !mImpl->mBuffer[offset - ATTRSIZE]) {
-    // Compress away all empty slots while we're at it. This might not be the
-    // optimal thing to do.
-    uint32_t attrCount = NonMappedAttrCount();
-    void** newStart = mImpl->mBuffer + attrCount * ATTRSIZE;
-    void** oldStart = mImpl->mBuffer + offset;
-    memmove(newStart, oldStart, aPos * sizeof(nsIContent*));
-    memmove(&newStart[aPos + 1], &oldStart[aPos],
-            (childCount - aPos) * sizeof(nsIContent*));
-    SetChildAtPos(newStart + aPos, aChild, aPos, childCount);
-
-    SetAttrSlotAndChildCount(attrCount, childCount + 1);
-
-    return NS_OK;
-  }
-
-  // We can't fit in current buffer, Realloc time!
-  if (!GrowBy(1)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  void** pos = mImpl->mBuffer + offset + aPos;
-  if (childCount != aPos) {
-    memmove(pos + 1, pos, (childCount - aPos) * sizeof(nsIContent*));
-  }
-  SetChildAtPos(pos, aChild, aPos, childCount);
-
-  SetChildCount(childCount + 1);
-
-  return NS_OK;
-}
-
-void
-nsAttrAndChildArray::RemoveChildAt(uint32_t aPos)
-{
-  // Just store the return value of TakeChildAt in an nsCOMPtr to
-  // trigger a release.
-  nsCOMPtr<nsIContent> child = TakeChildAt(aPos);
-}
-
-already_AddRefed<nsIContent>
-nsAttrAndChildArray::TakeChildAt(uint32_t aPos)
-{
-  NS_ASSERTION(aPos < ChildCount(), "out-of-bounds");
-
-  uint32_t childCount = ChildCount();
-  void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos;
-  nsIContent* child = static_cast<nsIContent*>(*pos);
-  if (child->mPreviousSibling) {
-    child->mPreviousSibling->mNextSibling = child->mNextSibling;
-  }
-  if (child->mNextSibling) {
-    child->mNextSibling->mPreviousSibling = child->mPreviousSibling;
-  }
-  child->mPreviousSibling = child->mNextSibling = nullptr;
-
-  memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
-  SetChildCount(childCount - 1);
-
-  return dont_AddRef(child);
-}
-
-int32_t
-nsAttrAndChildArray::IndexOfChild(const nsINode* aPossibleChild) const
-{
-  if (!mImpl) {
-    return -1;
-  }
-  void** children = mImpl->mBuffer + AttrSlotsSize();
-  // Use signed here since we compare count to cursor which has to be signed
-  int32_t i, count = ChildCount();
-
-  if (count >= CACHE_CHILD_LIMIT) {
-    int32_t cursor = GetIndexFromCache(this);
-    // Need to compare to count here since we may have removed children since
-    // the index was added to the cache.
-    // We're also relying on that GetIndexFromCache returns -1 if no cached
-    // index was found.
-    if (cursor >= count) {
-      cursor = -1;
-    }
-
-    // Seek outward from the last found index. |inc| will change sign every
-    // run through the loop. |sign| just exists to make sure the absolute
-    // value of |inc| increases each time through.
-    int32_t inc = 1, sign = 1;
-    while (cursor >= 0 && cursor < count) {
-      if (children[cursor] == aPossibleChild) {
-        AddIndexToCache(this, cursor);
-
-        return cursor;
-      }
-
-      cursor += inc;
-      inc = -inc - sign;
-      sign = -sign;
-    }
-
-    // We ran into one 'edge'. Add inc to cursor once more to get back to
-    // the 'side' where we still need to search, then step in the |sign|
-    // direction.
-    cursor += inc;
-
-    if (sign > 0) {
-      for (; cursor < count; ++cursor) {
-        if (children[cursor] == aPossibleChild) {
-          AddIndexToCache(this, cursor);
-
-          return static_cast<int32_t>(cursor);
-        }
-      }
-    }
-    else {
-      for (; cursor >= 0; --cursor) {
-        if (children[cursor] == aPossibleChild) {
-          AddIndexToCache(this, cursor);
-
-          return static_cast<int32_t>(cursor);
-        }
-      }
-    }
-
-    // The child wasn't even in the remaining children
-    return -1;
-  }
-
-  for (i = 0; i < count; ++i) {
-    if (children[i] == aPossibleChild) {
-      return static_cast<int32_t>(i);
-    }
-  }
-
-  return -1;
-}
-
 uint32_t
 nsAttrAndChildArray::AttrCount() const
 {
   return NonMappedAttrCount() + MappedAttrCount();
 }
 
 const nsAttrValue*
 nsAttrAndChildArray::GetAttr(nsAtom* aLocalName, int32_t aNamespaceID) const
@@ -647,27 +464,23 @@ nsAttrAndChildArray::Compact()
 {
   if (!mImpl) {
     return;
   }
 
   // First compress away empty attrslots
   uint32_t slotCount = AttrSlotCount();
   uint32_t attrCount = NonMappedAttrCount();
-  uint32_t childCount = ChildCount();
 
   if (attrCount < slotCount) {
-    memmove(mImpl->mBuffer + attrCount * ATTRSIZE,
-            mImpl->mBuffer + slotCount * ATTRSIZE,
-            childCount * sizeof(nsIContent*));
     SetAttrSlotCount(attrCount);
   }
 
   // Then resize or free buffer
-  uint32_t newSize = attrCount * ATTRSIZE + childCount;
+  uint32_t newSize = attrCount * ATTRSIZE;
   if (!newSize && !mImpl->mMappedAttrs) {
     free(mImpl);
     mImpl = nullptr;
   }
   else if (newSize < mImpl->mBufferSize) {
     mImpl = static_cast<Impl*>(realloc(mImpl, (newSize + NS_IMPL_EXTRA_SIZE) * sizeof(nsIContent*)));
     NS_ASSERTION(mImpl, "failed to reallocate to smaller buffer");
 
@@ -686,38 +499,16 @@ nsAttrAndChildArray::Clear()
     NS_RELEASE(mImpl->mMappedAttrs);
   }
 
   uint32_t i, slotCount = AttrSlotCount();
   for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
     ATTRS(mImpl)[i].~InternalAttr();
   }
 
-  nsAutoScriptBlocker scriptBlocker;
-  uint32_t end = slotCount * ATTRSIZE + ChildCount();
-  for (i = slotCount * ATTRSIZE; i < end; ++i) {
-    nsIContent* child = static_cast<nsIContent*>(mImpl->mBuffer[i]);
-    // making this false so tree teardown doesn't end up being
-    // O(N*D) (number of nodes times average depth of tree).
-    child->UnbindFromTree(false); // XXX is it better to let the owner do this?
-    // Make sure to unlink our kids from each other, since someone
-    // else could stil be holding references to some of them.
-
-    // XXXbz We probably can't push this assignment down into the |aNullParent|
-    // case of UnbindFromTree because we still need the assignment in
-    // RemoveChildAt.  In particular, ContentRemoved fires between
-    // RemoveChildAt and UnbindFromTree, and in ContentRemoved the sibling
-    // chain needs to be correct.  Though maybe we could set the prev and next
-    // to point to each other but keep the kid being removed pointing to them
-    // through ContentRemoved so consumers can find where it used to be in the
-    // list?
-    child->mPreviousSibling = child->mNextSibling = nullptr;
-    NS_RELEASE(child);
-  }
-
   SetAttrSlotAndChildCount(0, 0);
 }
 
 uint32_t
 nsAttrAndChildArray::NonMappedAttrCount() const
 {
   if (!mImpl) {
     return 0;
@@ -811,30 +602,25 @@ nsAttrAndChildArray::GetMapped() const
 }
 
 nsresult nsAttrAndChildArray::EnsureCapacityToClone(const nsAttrAndChildArray& aOther,
                                                     bool aAllocateChildren)
 {
   MOZ_ASSERT(!mImpl, "nsAttrAndChildArray::EnsureCapacityToClone requires the array be empty when called");
 
   uint32_t attrCount = aOther.NonMappedAttrCount();
-  uint32_t childCount = 0;
-  if (aAllocateChildren) {
-    childCount = aOther.ChildCount();
-  }
 
-  if (attrCount == 0 && childCount == 0) {
+  if (attrCount == 0) {
     return NS_OK;
   }
 
   // No need to use a CheckedUint32 because we are cloning. We know that we
   // have already allocated an nsAttrAndChildArray of this size.
   uint32_t size = attrCount;
   size *= ATTRSIZE;
-  size += childCount;
   uint32_t totalSize = size;
   totalSize += NS_IMPL_EXTRA_SIZE;
 
   mImpl = static_cast<Impl*>(malloc(totalSize * sizeof(void*)));
   NS_ENSURE_TRUE(mImpl, NS_ERROR_OUT_OF_MEMORY);
 
   mImpl->mMappedAttrs = nullptr;
   mImpl->mBufferSize = size;
@@ -905,66 +691,38 @@ nsAttrAndChildArray::GrowBy(uint32_t aGr
 
   return true;
 }
 
 bool
 nsAttrAndChildArray::AddAttrSlot()
 {
   uint32_t slotCount = AttrSlotCount();
-  uint32_t childCount = ChildCount();
 
   CheckedUint32 size = slotCount;
   size += 1;
   size *= ATTRSIZE;
-  size += childCount;
   if (!size.isValid()) {
     return false;
   }
 
   // Grow buffer if needed
   if (!(mImpl && mImpl->mBufferSize >= size.value()) &&
       !GrowBy(ATTRSIZE)) {
     return false;
   }
 
   void** offset = mImpl->mBuffer + slotCount * ATTRSIZE;
 
-  if (childCount > 0) {
-    memmove(&ATTRS(mImpl)[slotCount + 1], &ATTRS(mImpl)[slotCount],
-            childCount * sizeof(nsIContent*));
-  }
-
   SetAttrSlotCount(slotCount + 1);
   memset(static_cast<void*>(offset), 0, sizeof(InternalAttr));
 
   return true;
 }
 
-inline void
-nsAttrAndChildArray::SetChildAtPos(void** aPos, nsIContent* aChild,
-                                   uint32_t aIndex, uint32_t aChildCount)
-{
-  MOZ_ASSERT(!aChild->GetNextSibling(), "aChild with next sibling?");
-  MOZ_ASSERT(!aChild->GetPreviousSibling(), "aChild with prev sibling?");
-
-  *aPos = aChild;
-  NS_ADDREF(aChild);
-  if (aIndex != 0) {
-    nsIContent* previous = static_cast<nsIContent*>(*(aPos - 1));
-    aChild->mPreviousSibling = previous;
-    previous->mNextSibling = aChild;
-  }
-  if (aIndex != aChildCount) {
-    nsIContent* next = static_cast<nsIContent*>(*(aPos + 1));
-    aChild->mNextSibling = next;
-    next->mPreviousSibling = aChild;
-  }
-}
-
 size_t
 nsAttrAndChildArray::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   if (mImpl) {
     // Don't add the size taken by *mMappedAttrs because it's shared.
 
     n += aMallocSizeOf(mImpl);
--- a/dom/base/nsAttrAndChildArray.h
+++ b/dom/base/nsAttrAndChildArray.h
@@ -47,34 +47,16 @@ class nsMappedAttributeElement;
 
 class nsAttrAndChildArray
 {
   typedef mozilla::dom::BorrowedAttrInfo BorrowedAttrInfo;
 public:
   nsAttrAndChildArray();
   ~nsAttrAndChildArray();
 
-  uint32_t ChildCount() const
-  {
-    return mImpl ? (mImpl->mAttrAndChildCount >> ATTRCHILD_ARRAY_ATTR_SLOTS_BITS) : 0;
-  }
-  nsIContent* ChildAt(uint32_t aPos) const
-  {
-    NS_ASSERTION(aPos < ChildCount(), "out-of-bounds access in nsAttrAndChildArray");
-    return reinterpret_cast<nsIContent*>(mImpl->mBuffer[AttrSlotsSize() + aPos]);
-  }
-  nsIContent* GetSafeChildAt(uint32_t aPos) const;
-  nsIContent * const * GetChildArray(uint32_t* aChildCount) const;
-  nsresult InsertChildAt(nsIContent* aChild, uint32_t aPos);
-  void RemoveChildAt(uint32_t aPos);
-  // Like RemoveChildAt but hands the reference to the child being
-  // removed back to the caller instead of just releasing it.
-  already_AddRefed<nsIContent> TakeChildAt(uint32_t aPos);
-  int32_t IndexOfChild(const nsINode* aPossibleChild) const;
-
   bool HasAttrs() const
   {
     return MappedAttrCount() || (AttrSlotCount() && AttrSlotIsTaken(0));
   }
 
   uint32_t AttrCount() const;
   const nsAttrValue* GetAttr(nsAtom* aLocalName,
                              int32_t aNamespaceID = kNameSpaceID_None) const;
@@ -218,24 +200,16 @@ private:
     mImpl->mAttrAndChildCount = aSlotCount |
       (aChildCount << ATTRCHILD_ARRAY_ATTR_SLOTS_BITS);
   }
 
   bool GrowBy(uint32_t aGrowSize);
   bool AddAttrSlot();
 
   /**
-   * Set *aPos to aChild and update sibling pointers as needed.  aIndex is the
-   * index at which aChild is actually being inserted.  aChildCount is the
-   * number of kids we had before the insertion.
-   */
-  inline void SetChildAtPos(void** aPos, nsIContent* aChild, uint32_t aIndex,
-                            uint32_t aChildCount);
-
-  /**
    * Guts of SetMappedAttrStyleSheet for the rare case when we have mapped attrs
    */
   nsresult DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet);
 
   /**
    * Guts of UpdateMappedAttrRuleMapper for the case  when we have mapped attrs.
    */
   nsresult DoUpdateMappedAttrRuleMapper(nsMappedAttributeElement& aElement);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1691,21 +1691,20 @@ nsDocument::~nsDocument()
   // links one by one
   DestroyElementMaps();
 
   nsAutoScriptBlocker scriptBlocker;
 
   // Invalidate cached array of child nodes
   InvalidateChildNodes();
 
-  for (uint32_t indx = mChildren.ChildCount(); indx-- != 0; ) {
-    mChildren.ChildAt(indx)->UnbindFromTree();
-    mChildren.RemoveChildAt(indx);
-  }
-  mFirstChild = nullptr;
+  // We should not have child nodes when destructor is called,
+  // since child nodes keep their owner document alive.
+  MOZ_ASSERT(!HasChildren());
+
   mCachedRootElement = nullptr;
 
   for (auto& sheets : mAdditionalSheets) {
     for (StyleSheet* sheet : sheets) {
       sheet->ClearAssociatedDocumentOrShadowRoot();
     }
   }
 
@@ -1861,22 +1860,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
        iter.Next()) {
     iter.Get()->Traverse(&cb);
   }
 
   tmp->mExternalResourceMap.Traverse(&cb);
 
-  // Traverse the mChildren nsAttrAndChildArray.
-  for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
-    cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
-  }
-
   // Traverse all nsIDocument pointer members.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
 
   // Traverse all nsDocument nsCOMPtrs.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
@@ -1982,35 +1975,25 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   // Clear out our external resources
   tmp->mExternalResourceMap.Shutdown();
 
   nsAutoScriptBlocker scriptBlocker;
 
   nsINode::Unlink(tmp);
 
-  // Unlink the mChildren nsAttrAndChildArray.
-  uint32_t childCount = tmp->mChildren.ChildCount();
-  if (childCount) {
-    while (childCount-- > 0) {
-      // Hold a strong ref to the node when we remove it, because we may be
-      // the last reference to it.  We need to call TakeChildAt() and
-      // update mFirstChild before calling UnbindFromTree, since this last
-      // can notify various observers and they should really see consistent
-      // tree state.
-      // If this code changes, change the corresponding code in
-      // FragmentOrElement's unlink impl and ContentUnbinder::UnbindSubtree.
-      nsCOMPtr<nsIContent> child = tmp->mChildren.TakeChildAt(childCount);
-      if (childCount == 0) {
-        tmp->mFirstChild = nullptr;
-      }
-      child->UnbindFromTree();
-    }
-  }
-  tmp->mFirstChild = nullptr;
+  while (tmp->HasChildren()) {
+    // Hold a strong ref to the node when we remove it, because we may be
+    // the last reference to it.
+    // If this code changes, change the corresponding code in nsDocument's
+    // unlink impl and ContentUnbinder::UnbindSubtree.
+    nsCOMPtr<nsIContent> child = tmp->GetLastChild();
+    tmp->DisconnectChild(child);
+    child->UnbindFromTree();
+  }
 
   tmp->UnlinkOriginalDocumentIfStatic();
 
   tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
@@ -2248,32 +2231,26 @@ nsIDocument::ResetToURI(nsIURI* aURI,
   mSubDocuments = nullptr;
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   bool oldVal = mInUnlinkOrDeletion;
   mInUnlinkOrDeletion = true;
-  uint32_t count = mChildren.ChildCount();
   { // Scope for update
     MOZ_AUTO_DOC_UPDATE(this, true);
 
     // Invalidate cached array of child nodes
     InvalidateChildNodes();
 
-    for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
-      nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
-
+    while (HasChildren()) {
+      nsCOMPtr<nsIContent> content = GetLastChild();
       nsIContent* previousSibling = content->GetPreviousSibling();
-
-      if (nsINode::GetFirstChild() == content) {
-        mFirstChild = content->GetNextSibling();
-      }
-      mChildren.RemoveChildAt(i);
+      DisconnectChild(content);
       if (content == mCachedRootElement) {
         // Immediately clear mCachedRootElement, now that it's been removed
         // from mChildren, so that GetRootElement() will stop returning this
         // now-stale value.
         mCachedRootElement = nullptr;
       }
       nsNodeUtils::ContentRemoved(this, content, previousSibling);
       content->UnbindFromTree();
@@ -4138,39 +4115,36 @@ nsIDocument::InsertChildBefore(nsIConten
                                nsIContent* aBeforeThis,
                                bool aNotify)
 {
   if (aKid->IsElement() && GetRootElement()) {
     NS_WARNING("Inserting root element when we already have one");
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
-  int32_t index = aBeforeThis ? ComputeIndexOf(aBeforeThis) : GetChildCount();
-  MOZ_ASSERT(index >= 0);
-
-  return doInsertChildAt(aKid, index, aNotify, mChildren);
+  return nsINode::InsertChildBefore(aKid, aBeforeThis, aNotify);
 }
 
 void
 nsIDocument::RemoveChildNode(nsIContent* aKid, bool aNotify)
 {
   if (aKid->IsElement()) {
     // Destroy the link map up front before we mess with the child list.
     DestroyElementMaps();
   }
 
   // Preemptively clear mCachedRootElement, since we may be about to remove it
   // from our child list, and we don't want to return this maybe-obsolete value
-  // from any GetRootElement() calls that happen inside of doRemoveChildAt().
-  // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
+  // from any GetRootElement() calls that happen inside of RemoveChildNode().
+  // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
   // GetRootElement() calls until after it's removed the child from mChildren.
   // Any call before that point would restore this soon-to-be-obsolete cached
   // answer, and our clearing here would be fruitless.)
   mCachedRootElement = nullptr;
-  doRemoveChildAt(ComputeIndexOf(aKid), aNotify, aKid, mChildren);
+  nsINode::RemoveChildNode(aKid, aNotify);
   MOZ_ASSERT(mCachedRootElement != aKid,
              "Stale pointer in mCachedRootElement, after we tried to clear it "
              "(maybe somebody called GetRootElement() too early?)");
 }
 
 void
 nsIDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet)
 {
@@ -8766,20 +8740,16 @@ nsDocument::CloneDocHelper(nsDocument* c
   clone->SetContentTypeInternal(GetContentTypeInternal());
   clone->mSecurityInfo = mSecurityInfo;
 
   // State from nsDocument
   clone->mType = mType;
   clone->mXMLDeclarationBits = mXMLDeclarationBits;
   clone->mBaseTarget = mBaseTarget;
 
-  // Preallocate attributes and child arrays
-  rv = clone->mChildren.EnsureCapacityToClone(mChildren, aPreallocateChildren);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   return NS_OK;
 }
 
 void
 nsIDocument::SetReadyStateInternal(ReadyState rs)
 {
   mReadyState = rs;
   if (rs == READYSTATE_UNINITIALIZED) {
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -527,31 +527,16 @@ public:
   virtual void SetSuppressParserErrorElement(bool aSuppress) {}
   virtual bool SuppressParserErrorElement() { return false; }
 
   virtual void SetSuppressParserErrorConsoleMessages(bool aSuppress) {}
   virtual bool SuppressParserErrorConsoleMessages() { return false; }
 
   // nsINode
   bool IsNodeOfType(uint32_t aFlags) const final;
-  nsIContent* GetChildAt_Deprecated(uint32_t aIndex) const final
-  {
-    return mChildren.GetSafeChildAt(aIndex);
-  }
-
-  int32_t ComputeIndexOf(const nsINode* aPossibleChild) const final
-  {
-    return mChildren.IndexOfChild(aPossibleChild);
-  }
-
-  uint32_t GetChildCount() const final
-  {
-    return mChildren.ChildCount();
-  }
-
   nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
                              bool aNotify) override;
   void RemoveChildNode(nsIContent* aKid, bool aNotify) final;
   nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo,
                  nsINode **aResult,
                  bool aPreallocateChildren) const override
   {
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -145,16 +145,31 @@ nsINode::nsSlots::Unlink()
 {
   if (mChildNodes) {
     mChildNodes->DropReference();
   }
 }
 
 //----------------------------------------------------------------------
 
+#ifdef MOZILLA_INTERNAL_API
+nsINode::nsINode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : mNodeInfo(aNodeInfo)
+  , mParent(nullptr)
+#ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
+  , mBoolFlags(0)
+#endif
+  , mChildCount(0)
+  , mPreviousOrLastSibling(nullptr)
+  , mSubtreeRoot(this)
+  , mSlots(nullptr)
+{
+}
+#endif
+
 nsINode::~nsINode()
 {
   MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
   MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
 }
 
 void*
 nsINode::GetProperty(nsAtom* aPropertyName, nsresult* aStatus) const
@@ -410,16 +425,22 @@ nsINode::ChildNodes()
     slots->mChildNodes = IsAttr()
       ? new nsAttrChildContentList(this)
       : new nsParentNodeChildContentList(this);
   }
 
   return slots->mChildNodes;
 }
 
+nsIContent*
+nsINode::GetLastChild() const
+{
+  return mFirstChild ? mFirstChild->mPreviousOrLastSibling : nullptr;
+}
+
 void
 nsINode::InvalidateChildNodes()
 {
   MOZ_ASSERT(!IsAttr());
 
   nsSlots* slots = GetExistingSlots();
   if (!slots || !slots->mChildNodes) {
     return;
@@ -1203,16 +1224,18 @@ nsINode::Traverse(nsINode *tmp, nsCycleC
           MOZ_ASSERT(parent->ComputeIndexOf(tmp) >= 0, "Parent doesn't own us?");
           return false;
         }
       }
     }
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
 
   nsSlots *slots = tmp->GetExistingSlots();
   if (slots) {
     slots->Traverse(cb);
   }
 
   if (tmp->HasProperties()) {
@@ -1330,19 +1353,23 @@ ReparentWrappersInSubtree(nsIContent* aR
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
-                         bool aNotify, nsAttrAndChildArray& aChildArray)
-{
+nsINode::InsertChildBefore(nsIContent* aKid, nsIContent* aChildToInsertBefore,
+                           bool aNotify)
+ {
+  if (!IsContainerNode()) {
+    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
+  }
+
   MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent");
   MOZ_ASSERT(!IsAttr());
 
   // The id-handling code, and in the future possibly other code, need to
   // react to unexpected attribute changes.
   nsMutationGuard::DidMutate();
 
   // Do this before checking the child-count since this could cause mutations
@@ -1368,55 +1395,48 @@ nsINode::doInsertChildAt(nsIContent* aKi
     // exception (which it can) and we're neither propagating the
     // error out nor unconditionally suppressing it.
     error.WouldReportJSException();
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
-  uint32_t childCount = aChildArray.ChildCount();
-  NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
-  bool isAppend = (aIndex == childCount);
-
-  nsresult rv = aChildArray.InsertChildAt(aKid, aIndex);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (aIndex == 0) {
-    mFirstChild = aKid;
+  if (!aChildToInsertBefore) {
+    AppendChildToChildList(aKid);
+  } else {
+    InsertChildToChildList(aKid, aChildToInsertBefore);
   }
 
   nsIContent* parent = IsContent() ? AsContent() : nullptr;
 
   bool wasInXBLScope = ShouldUseXBLScope(aKid);
-  rv = aKid->BindToTree(doc, parent,
-                        parent ? parent->GetBindingParent() : nullptr);
+  nsresult rv = aKid->BindToTree(doc, parent,
+                                 parent ? parent->GetBindingParent() : nullptr);
   if (NS_SUCCEEDED(rv) && !wasInXBLScope && ShouldUseXBLScope(aKid)) {
     MOZ_ASSERT(ShouldUseXBLScope(this),
                "Why does the kid need to use an XBL scope?");
     rv = ReparentWrappersInSubtree(aKid);
   }
   if (NS_FAILED(rv)) {
-    if (GetFirstChild() == aKid) {
-      mFirstChild = aKid->GetNextSibling();
-    }
-    aChildArray.RemoveChildAt(aIndex);
+    DisconnectChild(aKid);
     aKid->UnbindFromTree();
     return rv;
   }
 
   // Invalidate cached array of child nodes
   InvalidateChildNodes();
 
   NS_ASSERTION(aKid->GetParentNode() == this,
                "Did we run script inappropriately?");
 
   if (aNotify) {
     // Note that we always want to call ContentInserted when things are added
     // as kids to documents
-    if (parent && isAppend) {
+    if (parent && !aChildToInsertBefore) {
       nsNodeUtils::ContentAppended(parent, aKid);
     } else {
       nsNodeUtils::ContentInserted(this, aKid);
     }
 
     if (nsContentUtils::HasMutationListeners(aKid,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) {
       InternalMutationEvent mutation(true, eLegacyNodeInserted);
@@ -1425,16 +1445,137 @@ nsINode::doInsertChildAt(nsIContent* aKi
       mozAutoSubtreeModified subtree(OwnerDoc(), this);
       (new AsyncEventDispatcher(aKid, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
 }
 
+nsIContent*
+nsINode::GetPreviousSibling() const
+{
+  // Do not expose circular linked list
+  if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) {
+    return nullptr;
+  }
+  return mPreviousOrLastSibling;
+}
+
+void
+nsINode::AppendChildToChildList(nsIContent* aKid)
+{
+  MOZ_ASSERT(aKid);
+  MOZ_ASSERT(!aKid->mNextSibling);
+
+  if (mFirstChild) {
+    nsIContent* lastChild = GetLastChild();
+    lastChild->mNextSibling = aKid;
+    aKid->mPreviousOrLastSibling = lastChild;
+  } else {
+    mFirstChild = aKid;
+  }
+
+  // Maintain link to the last child
+  mFirstChild->mPreviousOrLastSibling = aKid;
+  ++mChildCount;
+}
+
+void
+nsINode::InsertChildToChildList(nsIContent* aKid, nsIContent* aNextSibling)
+{
+  MOZ_ASSERT(aKid);
+  MOZ_ASSERT(aNextSibling);
+
+  nsIContent* previousSibling = aNextSibling->mPreviousOrLastSibling;
+  aNextSibling->mPreviousOrLastSibling = aKid;
+  aKid->mPreviousOrLastSibling = previousSibling;
+  aKid->mNextSibling = aNextSibling;
+
+  if (aNextSibling == mFirstChild) {
+    MOZ_ASSERT(!previousSibling->mNextSibling);
+    mFirstChild = aKid;
+  } else {
+    previousSibling->mNextSibling = aKid;
+  }
+
+  ++mChildCount;
+}
+
+void
+nsINode::DisconnectChild(nsIContent* aKid)
+{
+  MOZ_ASSERT(aKid);
+  MOZ_ASSERT(GetChildCount() > 0);
+
+  nsIContent* previousSibling = aKid->GetPreviousSibling();
+  nsCOMPtr<nsIContent> ref = aKid;
+
+  if (aKid->mNextSibling) {
+    aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
+  } else {
+    // aKid is the last child in the list
+    mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
+  }
+  aKid->mPreviousOrLastSibling = nullptr;
+
+  if (previousSibling) {
+    previousSibling->mNextSibling = aKid->mNextSibling.forget();
+  } else {
+    // aKid is the first child in the list
+    mFirstChild = aKid->mNextSibling.forget();
+  }
+
+  --mChildCount;
+}
+
+nsIContent*
+nsINode::GetChildAt_Deprecated(uint32_t aIndex) const
+{
+  if (aIndex >= GetChildCount()) {
+    return nullptr;
+  }
+
+  nsIContent* child = mFirstChild;
+  while (aIndex--) {
+    child = child->GetNextSibling();
+  }
+
+  return child;
+}
+
+int32_t
+nsINode::ComputeIndexOf(const nsINode* aChild) const
+{
+  if (!aChild) {
+    return -1;
+  }
+
+  if (aChild->GetParentNode() != this) {
+    return -1;
+  }
+
+  if (aChild == GetLastChild()) {
+    return GetChildCount() - 1;
+  }
+
+  int32_t index = 0;
+  nsINode* current = mFirstChild;
+  while (current) {
+    MOZ_ASSERT(current->GetParentNode() == this);
+    if (current == aChild) {
+      return index;
+    }
+    current = current->GetNextSibling();
+    ++index;
+  }
+
+  return -1;
+}
+
 Element*
 nsINode::GetPreviousElementSibling() const
 {
   nsIContent* previousSibling = GetPreviousSibling();
   while (previousSibling) {
     if (previousSibling->IsElement()) {
       return previousSibling->AsElement();
     }
@@ -1751,17 +1892,17 @@ nsINode::Prepend(const Sequence<OwningNo
                  ErrorResult& aRv)
 {
   nsCOMPtr<nsIDocument> doc = OwnerDoc();
   nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   if (aRv.Failed()) {
     return;
   }
 
-  nsCOMPtr<nsINode> refNode = mFirstChild;
+  nsCOMPtr<nsIContent> refNode = mFirstChild;;
   InsertBefore(*node, refNode, aRv);
 }
 
 void
 nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
                  ErrorResult& aRv)
 {
   nsCOMPtr<nsIDocument> doc = OwnerDoc();
@@ -1769,39 +1910,34 @@ nsINode::Append(const Sequence<OwningNod
   if (aRv.Failed()) {
     return;
   }
 
   AppendChild(*node, aRv);
 }
 
 void
-nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
-                         nsIContent* aKid, nsAttrAndChildArray& aChildArray)
+nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify)
 {
   // NOTE: This function must not trigger any calls to
   // nsIDocument::GetRootElement() calls until *after* it has removed aKid from
   // aChildArray. Any calls before then could potentially restore a stale
   // value for our cached root element, per note in
   // nsDocument::RemoveChildNode().
-  MOZ_ASSERT(aKid && aKid->GetParentNode() == this &&
-             aKid == GetChildAt_Deprecated(aIndex) &&
-             ComputeIndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");
+  MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid");
   MOZ_ASSERT(!IsAttr());
 
   nsMutationGuard::DidMutate();
   mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
 
   nsIContent* previousSibling = aKid->GetPreviousSibling();
 
-  if (GetFirstChild() == aKid) {
-    mFirstChild = aKid->GetNextSibling();
-  }
-
-  aChildArray.RemoveChildAt(aIndex);
+  // Since aKid is use also after DisconnectChild, ensure it stays alive.
+  nsCOMPtr<nsIContent> kungfuDeathGrip = aKid;
+  DisconnectChild(aKid);
 
   // Invalidate cached array of child nodes
   InvalidateChildNodes();
 
   if (aNotify) {
     nsNodeUtils::ContentRemoved(this, aKid, previousSibling);
   }
 
@@ -2429,18 +2565,18 @@ nsINode::AddSizeOfExcludingThis(nsWindow
   }
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mNodeInfo
   // - mSlots
   //
   // The following members are not measured:
-  // - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
-  //   non-owning
+  // - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because they're
+  //   non-owning, from "exclusive ownership" point of view.
 }
 
 void
 nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
 {
   *aNodeSize += aSizes.mState.mMallocSizeOf(this);
   AddSizeOfExcludingThis(aSizes, aNodeSize);
 }
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -365,29 +365,17 @@ public:
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const;
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
   friend class nsAttrAndChildArray;
 
 #ifdef MOZILLA_INTERNAL_API
-  explicit nsINode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
-  : mNodeInfo(aNodeInfo)
-  , mParent(nullptr)
-#ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
-  , mBoolFlags(0)
-#endif
-  , mNextSibling(nullptr)
-  , mPreviousSibling(nullptr)
-  , mFirstChild(nullptr)
-  , mSubtreeRoot(this)
-  , mSlots(nullptr)
-  {
-  }
+  explicit nsINode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 #endif
 
   virtual ~nsINode();
 
   /**
    * Bit-flags to pass (or'ed together) to IsNodeOfType()
    */
   enum {
@@ -577,37 +565,40 @@ public:
    * Return if this node has any children.
    */
   bool HasChildren() const { return !!mFirstChild; }
 
   /**
    * Get the number of children
    * @return the number of children
    */
-  virtual uint32_t GetChildCount() const = 0;
+  uint32_t GetChildCount() const
+  {
+    return mChildCount;
+  }
 
   /**
    * NOTE: this function is going to be removed soon (hopefully!) Don't use it
    * in new code.
    *
    * Get a child by index
    * @param aIndex the index of the child to get
    * @return the child, or null if index out of bounds
    */
-  virtual nsIContent* GetChildAt_Deprecated(uint32_t aIndex) const = 0;
+  nsIContent* GetChildAt_Deprecated(uint32_t aIndex) const;
 
   /**
    * Get the index of a child within this content
    * @param aPossibleChild the child to get the index of.
    * @return the index of the child, or -1 if not a child
    *
    * If the return value is not -1, then calling GetChildAt_Deprecated() with
    * that value will return aPossibleChild.
    */
-  virtual int32_t ComputeIndexOf(const nsINode* aPossibleChild) const = 0;
+  virtual int32_t ComputeIndexOf(const nsINode* aPossibleChild) const;
 
   /**
    * Returns the "node document" of this node.
    *
    * https://dom.spec.whatwg.org/#concept-node-document
    *
    * Note that in the case that this node is a document node this method
    * will return |this|.  That is different to the Node.ownerDocument DOM
@@ -822,17 +813,17 @@ public:
    * assert -- you shouldn't be doing it!  Check with
    * nsIDocument::GetRootElement() first if you're not sure.  Apart from this
    * one constraint, this doesn't do any checking on whether aKid is a valid
    * child of |this|.
    *
    * @throws NS_ERROR_OUT_OF_MEMORY in some cases (from BindToTree).
    */
   virtual nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                                     bool aNotify) = 0;
+                                     bool aNotify);
 
   /**
    * Append a content node to the end of the child list.  This method handles
    * calling BindToTree on the child appropriately.
    *
    * @param aKid the content to append
    * @param aNotify whether to notify the document (current document for
    *        nsIContent, and |this| for nsIDocument) that the append has
@@ -856,17 +847,17 @@ public:
    * Remove a child from this node.  This method handles calling UnbindFromTree
    * on the child appropriately.
    *
    * @param aKid the content to remove
    * @param aNotify whether to notify the document (current document for
    *        nsIContent, and |this| for nsIDocument) that the remove has
    *        occurred
    */
-  virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) = 0;
+  virtual void RemoveChildNode(nsIContent* aKid, bool aNotify);
 
   /**
    * Get a property associated with this node.
    *
    * @param aPropertyName  name of property to get.
    * @param aStatus        out parameter for storing resulting status.
    *                       Set to NS_PROPTABLE_PROP_NOT_THERE if the property
    *                       is not set.
@@ -1291,23 +1282,23 @@ public:
    * node is not in an editor, the result comes from the nsFrameSelection that
    * is related to aPresShell, so the result might not be the ancestor of this
    * node. Be aware that if this node and the computed selection limiter are
    * not in same subtree, this returns the root content of the closeset subtree.
    */
   nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell);
 
   nsINodeList* ChildNodes();
-  nsIContent* GetFirstChild() const { return mFirstChild; }
-  nsIContent* GetLastChild() const
+
+  nsIContent* GetFirstChild() const
   {
-    uint32_t count = GetChildCount();
+    return mFirstChild;
+  }
 
-    return count > 0 ? GetChildAt_Deprecated(count - 1) : nullptr;
-  }
+  nsIContent* GetLastChild() const;
 
   /**
    * Implementation is in nsIDocument.h, because it needs to cast from
    * nsIDocument* to nsINode*.
    */
   nsIDocument* GetOwnerDocument() const;
 
   void Normalize();
@@ -1368,29 +1359,33 @@ public:
   already_AddRefed<nsINodeList> QuerySelectorAll(const nsAString& aSelector,
                                                  mozilla::ErrorResult& aResult);
 
 protected:
   // nsIDocument overrides this with its own (faster) version.  This
   // should really only be called for elements and document fragments.
   mozilla::dom::Element* GetElementById(const nsAString& aId);
 
+  void AppendChildToChildList(nsIContent* aKid);
+  void InsertChildToChildList(nsIContent* aKid, nsIContent* aNextSibling);
+  void DisconnectChild(nsIContent* aKid);
+
 public:
   void LookupPrefix(const nsAString& aNamespace, nsAString& aResult);
   bool IsDefaultNamespace(const nsAString& aNamespaceURI)
   {
     nsAutoString defaultNamespace;
     LookupNamespaceURI(EmptyString(), defaultNamespace);
     return aNamespaceURI.Equals(defaultNamespace);
   }
   void LookupNamespaceURI(const nsAString& aNamespacePrefix,
                           nsAString& aNamespaceURI);
 
   nsIContent* GetNextSibling() const { return mNextSibling; }
-  nsIContent* GetPreviousSibling() const { return mPreviousSibling; }
+  nsIContent* GetPreviousSibling() const;
 
   /**
    * Get the next node in the pre-order tree traversal of the DOM.  If
    * aRoot is non-null, then it must be an ancestor of |this|
    * (possibly equal to |this|) and only nodes that are descendants of
    * aRoot, not including aRoot itself, will be returned.  Returns
    * null if there are no more nodes to traverse.
    */
@@ -1987,43 +1982,16 @@ protected:
    * Returns the Element that should be used for resolving namespaces
    * on this node (ie the ownerElement for attributes, the documentElement for
    * documents, the node itself for elements and for other nodes the parentNode
    * if it is an element).
    */
   virtual mozilla::dom::Element* GetNameSpaceElement() = 0;
 
   /**
-   * Most of the implementation of the nsINode RemoveChildAt method.
-   * Should only be called on document, element, and document fragment
-   * nodes.  The aChildArray passed in should be the one for |this|.
-   *
-   * @param aIndex The index to remove at.
-   * @param aNotify Whether to notify.
-   * @param aKid The kid at aIndex.  Must not be null.
-   * @param aChildArray The child array to work with.
-   * @param aMutationEvent whether to fire a mutation event for this removal.
-   */
-  void doRemoveChildAt(uint32_t aIndex, bool aNotify, nsIContent* aKid,
-                       nsAttrAndChildArray& aChildArray);
-
-  /**
-   * Most of the implementation of the nsINode InsertChildAt_Deprecated method.
-   * Should only be called on document, element, and document fragment
-   * nodes.  The aChildArray passed in should be the one for |this|.
-   *
-   * @param aKid The child to insert.
-   * @param aIndex The index to insert at.
-   * @param aNotify Whether to notify.
-   * @param aChildArray The child array to work with
-   */
-  nsresult doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
-                           bool aNotify, nsAttrAndChildArray& aChildArray);
-
-  /**
    * Parse the given selector string into a servo SelectorList.
    *
    * Never returns null if aRv is not failing.
    *
    * Note that the selector list returned here is owned by the owner doc's
    * selector cache.
    */
   const RawServoSelectorList* ParseSelectorList(const nsAString& aSelectorString,
@@ -2060,26 +2028,27 @@ protected:
   nsINode* MOZ_OWNING_REF mParent;
 
 private:
 #ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
   // Boolean flags.
   uint32_t mBoolFlags;
 #endif
 
+  //NOTE, there are 32 bits left here, at least in 64 bit builds.
+
+  uint32_t mChildCount;
 
 protected:
-  // These references are non-owning and safe, as they are managed by
-  // nsAttrAndChildArray.
-  nsIContent* MOZ_NON_OWNING_REF mNextSibling;
-  nsIContent* MOZ_NON_OWNING_REF mPreviousSibling;
-  // This reference is non-owning and safe, since in the case of documents,
-  // it is set to null when the document gets destroyed, and in the case of
-  // other nodes, the children keep the parents alive.
-  nsIContent* MOZ_NON_OWNING_REF mFirstChild;
+  // mNextSibling and mFirstChild are strong references while
+  // mPreviousOrLastSibling is a weak ref. |mFirstChild->mPreviousOrLastSibling|
+  // points to the last child node.
+  nsCOMPtr<nsIContent> mFirstChild;
+  nsCOMPtr<nsIContent> mNextSibling;
+  nsIContent* MOZ_NON_OWNING_REF mPreviousOrLastSibling;
 
   union {
     // Pointer to our primary frame.  Might be null.
     nsIFrame* mPrimaryFrame;
 
     // Pointer to the root of our subtree.  Might be null.
     // This reference is non-owning and safe, since it either points to the
     // object itself, or is reset by ClearSubtreeRootPointer.
--- a/dom/svg/SVGTests.cpp
+++ b/dom/svg/SVGTests.cpp
@@ -64,35 +64,36 @@ SVGTests::IsConditionalProcessingAttribu
     }
   }
   return false;
 }
 
 int32_t
 SVGTests::GetBestLanguagePreferenceRank(const nsAString& aAcceptLangs) const
 {
-  const nsDefaultStringComparator defaultComparator;
+  const nsCaseInsensitiveStringComparator caseInsensitiveComparator;
 
   if (!mStringListAttributes[LANGUAGE].IsExplicitlySet()) {
     return -2;
   }
 
   int32_t lowestRank = -1;
 
   for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) {
     nsCharSeparatedTokenizer languageTokenizer(aAcceptLangs, ',');
     int32_t index = 0;
     while (languageTokenizer.hasMoreTokens()) {
       const nsAString& languageToken = languageTokenizer.nextToken();
-      bool exactMatch = (languageToken == mStringListAttributes[LANGUAGE][i]);
+      bool exactMatch = languageToken.Equals(mStringListAttributes[LANGUAGE][i],
+                                             caseInsensitiveComparator);
       bool prefixOnlyMatch =
         !exactMatch &&
         nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i],
                                       languageTokenizer.nextToken(),
-                                      defaultComparator);
+                                      caseInsensitiveComparator);
       if (index == 0 && exactMatch) {
         // best possible match
         return 0;
       }
       if ((exactMatch || prefixOnlyMatch) &&
           (lowestRank == -1 || 2 * index + prefixOnlyMatch < lowestRank)) {
         lowestRank = 2 * index + prefixOnlyMatch;
       }
@@ -150,24 +151,24 @@ SVGTests::PassesConditionalProcessingTes
       Preferences::GetLocalizedString("intl.accept_languages", acceptLangs);
     }
 
     if (acceptLangs.IsEmpty()) {
       NS_WARNING("no default language specified for systemLanguage conditional test");
       return false;
     }
 
-    const nsDefaultStringComparator defaultComparator;
+    const nsCaseInsensitiveStringComparator caseInsensitiveComparator;
 
     for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) {
       nsCharSeparatedTokenizer languageTokenizer(acceptLangs, ',');
       while (languageTokenizer.hasMoreTokens()) {
         if (nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i],
                                           languageTokenizer.nextToken(),
-                                          defaultComparator)) {
+                                          caseInsensitiveComparator)) {
           return true;
         }
       }
     }
     return false;
   }
 
   return true;
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -11,16 +11,18 @@
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "X11UndefineNone.h"
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/Range.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/widget/GtkCompositorWidget.h"
 #include "mozilla/Unused.h"
 
 #include "prenv.h"
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "nsDebug.h"
@@ -103,16 +105,17 @@ GLXLibrary::EnsureInitialized()
 #define END_OF_SYMBOLS { nullptr, { nullptr } }
 
     const GLLibraryLoader::SymLoadStruct symbols[] = {
         /* functions that were in GLX 1.0 */
         SYMBOL(DestroyContext),
         SYMBOL(MakeCurrent),
         SYMBOL(SwapBuffers),
         SYMBOL(QueryVersion),
+        SYMBOL(GetConfig),
         SYMBOL(GetCurrentContext),
         SYMBOL(WaitGL),
         SYMBOL(WaitX),
 
         /* functions introduced in GLX 1.1 */
         SYMBOL(QueryExtensionsString),
         SYMBOL(GetClientString),
         SYMBOL(QueryServerString),
@@ -883,49 +886,107 @@ ChooseConfig(GLXLibrary* glx, Display* d
 
     return false;
 }
 
 bool
 GLContextGLX::FindVisual(Display* display, int screen, bool useWebRender,
                          bool useAlpha, int* const out_visualId)
 {
-    int attribs[] = {
-        LOCAL_GLX_RGBA,
-        LOCAL_GLX_DOUBLEBUFFER,
-        LOCAL_GLX_RED_SIZE, 8,
-        LOCAL_GLX_GREEN_SIZE, 8,
-        LOCAL_GLX_BLUE_SIZE, 8,
-        LOCAL_GLX_ALPHA_SIZE, useAlpha ? 8 : 0,
-        LOCAL_GLX_DEPTH_SIZE, useWebRender ? 24 : 0,
-        LOCAL_GL_NONE
-    };
-
     if (!sGLXLibrary.EnsureInitialized()) {
         return false;
     }
 
-    XVisualInfo* visuals = sGLXLibrary.fChooseVisual(display, screen, attribs);
-    if (!visuals) {
+    XVisualInfo visualTemplate;
+    visualTemplate.screen = screen;
+
+    // Get all visuals of screen
+
+    int visualsLen = 0;
+    XVisualInfo* xVisuals = XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen);
+    if (!xVisuals) {
+        return false;
+    }
+    const Range<XVisualInfo> visualInfos(xVisuals, visualsLen);
+    auto cleanupVisuals = MakeScopeExit([&] {
+        XFree(xVisuals);
+    });
+
+    // Get default visual info
+
+    Visual* defaultVisual = DefaultVisual(display, screen);
+    const auto defaultVisualInfo = [&]() -> const XVisualInfo* {
+        for (const auto& cur : visualInfos) {
+            if (cur.visual == defaultVisual) {
+                return &cur;
+            }
+        }
+        return nullptr;
+    }();
+    if (!defaultVisualInfo) {
+        MOZ_ASSERT(false);
         return false;
     }
 
-    *out_visualId = visuals[0].visualid;
-    return true;
+    const int bpp = useAlpha ? 32 : 24;
+    const int alphaSize = useAlpha ? 8 : 0;
+    const int depthSize = useWebRender ? 24 : 0;
+
+    for (auto& cur : visualInfos) {
+
+        const auto fnConfigMatches = [&](const int pname, const int expected) {
+            int actual;
+            if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) {
+                return false;
+            }
+            return actual == expected;
+        };
+
+        // Check if visual is compatible.
+        if (cur.depth != bpp ||
+            cur.c_class != defaultVisualInfo->c_class) {
+            continue;
+        }
+
+        // Check if visual is compatible to GL requests.
+        if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) &&
+            fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) &&
+            fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) &&
+            fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) &&
+            fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) &&
+            fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, alphaSize) &&
+            fnConfigMatches(LOCAL_GLX_DEPTH_SIZE, depthSize))
+        {
+            *out_visualId = cur.visualid;
+            return true;
+        }
+    }
+
+    return false;
 }
 
 bool
 GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window,
                                     ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
                                     GLXFBConfig* const out_config, int* const out_visid,
                                     bool aWebRender)
 {
+    // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
+    // we could probably do this first and replace the glXGetFBConfigs
+    // with glXChooseConfigs.  Docs are sparklingly clear as always.
+    XWindowAttributes windowAttrs;
+    if (!XGetWindowAttributes(display, window, &windowAttrs)) {
+        NS_WARNING("[GLX] XGetWindowAttributes() failed");
+        return false;
+    }
+
     ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
     int numConfigs;
     const int webrenderAttribs[] = {
+        LOCAL_GLX_ALPHA_SIZE, windowAttrs.depth == 32 ? 8 : 0,
         LOCAL_GLX_DEPTH_SIZE, 24,
         LOCAL_GLX_DOUBLEBUFFER, True,
         0
     };
 
     if (aWebRender) {
       cfgs = sGLXLibrary.fChooseFBConfig(display,
                                          screen,
@@ -938,24 +999,16 @@ GLContextGLX::FindFBConfigForWindow(Disp
     }
 
     if (!cfgs) {
         NS_WARNING("[GLX] glXGetFBConfigs() failed");
         return false;
     }
     NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
 
-    // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
-    // we could probably do this first and replace the glXGetFBConfigs
-    // with glXChooseConfigs.  Docs are sparklingly clear as always.
-    XWindowAttributes windowAttrs;
-    if (!XGetWindowAttributes(display, window, &windowAttrs)) {
-        NS_WARNING("[GLX] XGetWindowAttributes() failed");
-        return false;
-    }
     const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
 #ifdef DEBUG
     printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
 #endif
 
     for (int i = 0; i < numConfigs; i++) {
         int visid = X11None;
         sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
--- a/gfx/gl/GLXLibrary.h
+++ b/gfx/gl/GLXLibrary.h
@@ -76,16 +76,19 @@ public:
     }
 
     void           fDestroyContext(Display* display, GLXContext context) const
         VOID_WRAP( fDestroyContext(display, context) )
 
     Bool      fMakeCurrent(Display* display, GLXDrawable drawable, GLXContext context) const
         WRAP( fMakeCurrent(display, drawable, context) )
 
+    XVisualInfo* fGetConfig(Display* display, XVisualInfo* info, int attrib, int* value) const
+        WRAP(    fGetConfig(display, info, attrib, value) )
+
     GLXContext fGetCurrentContext() const
         WRAP(  fGetCurrentContext() )
 
     GLXFBConfig* fChooseFBConfig(Display* display, int screen, const int* attrib_list, int* nelements) const
         WRAP(    fChooseFBConfig(display, screen, attrib_list, nelements) )
 
     XVisualInfo* fChooseVisual(Display* display, int screen, int* attrib_list) const
         WRAP(    fChooseVisual(display, screen, attrib_list) )
@@ -174,16 +177,17 @@ public:
     PRFuncPtr GetGetProcAddress() const {
         return (PRFuncPtr)mSymbols.fGetProcAddress;
     }
 
 private:
     struct {
         void         (GLAPIENTRY *fDestroyContext) (Display*, GLXContext);
         Bool         (GLAPIENTRY *fMakeCurrent) (Display*, GLXDrawable, GLXContext);
+        XVisualInfo* (GLAPIENTRY *fGetConfig) (Display*, XVisualInfo*, int, int*);
         GLXContext   (GLAPIENTRY *fGetCurrentContext) ();
         void*        (GLAPIENTRY *fGetProcAddress) (const char*);
         GLXFBConfig* (GLAPIENTRY *fChooseFBConfig) (Display*, int, const int*, int*);
         XVisualInfo* (GLAPIENTRY *fChooseVisual) (Display*, int, const int*);
         GLXFBConfig* (GLAPIENTRY *fGetFBConfigs) (Display*, int, int*);
         GLXContext   (GLAPIENTRY *fCreateNewContext) (Display*, GLXFBConfig, int,
                                                       GLXContext, Bool);
         int          (GLAPIENTRY *fGetFBConfigAttrib) (Display*, GLXFBConfig, int, int*);
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -102,19 +102,16 @@ RasterImage::~RasterImage()
     mSourceBuffer->Complete(NS_ERROR_ABORT);
   }
 
   // Release all frames from the surface cache.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   // Record Telemetry.
   Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
-  if (mAnimationState) {
-    Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_COUNT, mDecodeCount);
-  }
 }
 
 nsresult
 RasterImage::Init(const char* aMimeType,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mInitialized) {
@@ -1507,21 +1504,16 @@ RasterImage::Draw(gfxContext* aContext,
 
   auto drawResult = DrawInternal(std::move(result.Surface()), aContext, aSize,
                                  aRegion, aSamplingFilter, flags, aOpacity);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
-      if (mAnimationState) {
-        Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_ON_DRAW_LATENCY,
-                              int32_t(drawLatency.ToMicroseconds()));
-
-      }
       mDrawStartTime = TimeStamp();
   }
 
   return drawResult;
 }
 
 //******************************************************************************
 
@@ -1766,21 +1758,16 @@ RasterImage::NotifyDecodeComplete(const 
     if (aTelemetry.mChunkCount) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, aTelemetry.mChunkCount);
     }
 
     if (aStatus.mFinished) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
                             int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
 
-      if (mAnimationState) {
-        Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_TIME,
-                              int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
-      }
-
       if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
         Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
       }
     }
   }
 
   // Only act on errors if we have no usable frames from the decoder.
   if (aStatus.mHadError &&
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -142,16 +142,22 @@ Gecko_IsSignificantChild(RawGeckoNodeBor
 
 RawGeckoNodeBorrowedOrNull
 Gecko_GetLastChild(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetLastChild();
 }
 
 RawGeckoNodeBorrowedOrNull
+Gecko_GetPreviousSibling(RawGeckoNodeBorrowed aNode)
+{
+  return aNode->GetPreviousSibling();
+}
+
+RawGeckoNodeBorrowedOrNull
 Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetFlattenedTreeParentNodeForStyle();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed aElement, bool aIsBefore)
 {
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -140,16 +140,17 @@ struct MediumFeaturesChangedResult {
 };
 
 // Debugging stuff.
 void Gecko_Element_DebugListAttributes(RawGeckoElementBorrowed, nsCString*);
 void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot*, nsCString*);
 
 bool Gecko_IsSignificantChild(RawGeckoNodeBorrowed node, bool whitespace_is_significant);
 RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
+RawGeckoNodeBorrowedOrNull Gecko_GetPreviousSibling(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed element, bool is_before);
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed element);
 const nsTArray<RefPtr<nsINode>>* Gecko_GetAssignedNodes(RawGeckoElementBorrowed element);
 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);
 
 void Gecko_ComputedStyle_Init(mozilla::ComputedStyle* context,
                               RawGeckoPresContextBorrowed pres_context,
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1991,17 +1991,17 @@ nsHttpHandler::PrefsChanged(const char *
         if (NS_SUCCEEDED(rv)) {
             if (val < 0) {
                 val = 0;
             }
             mFastOpenStallsIdleTime = val;
         }
     }
 
-    if (PREF_CHANGED(HTTP_PREF("spdy.hpack-default-buffer"))) {
+    if (PREF_CHANGED(HTTP_PREF("spdy.default-hpack-buffer"))) {
         rv = Preferences::GetInt(HTTP_PREF("spdy.default-hpack-buffer"), &val);
         if (NS_SUCCEEDED(rv)) {
             mDefaultHpackBuffer = val;
         }
     }
 
     // Enable HTTP response timeout if TCP Keepalives are disabled.
     mResponseTimeoutEnabled = !mTCPKeepaliveShortLivedEnabled &&
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -25,17 +25,17 @@ use dom::{LayoutIterator, NodeInfo, Opaq
 use element_state::{DocumentState, ElementState};
 use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
 use gecko::data::GeckoStyleSheet;
 use gecko::global_style_data::GLOBAL_STYLE_DATA;
 use gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
 use gecko::snapshot_helpers;
 use gecko_bindings::bindings;
 use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme};
-use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetNextStyleChild};
+use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetPreviousSibling, Gecko_GetNextStyleChild};
 use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
 use gecko_bindings::bindings::Gecko_ClassOrClassList;
 use gecko_bindings::bindings::Gecko_ElementHasAnimations;
 use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
 use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
 use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
 use gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
 use gecko_bindings::bindings::Gecko_GetAnimationRule;
@@ -370,37 +370,40 @@ impl<'ln> TNode for GeckoNode<'ln> {
 
     #[inline]
     fn parent_node(&self) -> Option<Self> {
         unsafe { self.0.mParent.as_ref().map(GeckoNode) }
     }
 
     #[inline]
     fn first_child(&self) -> Option<Self> {
-        unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) }
+        unsafe {
+            self.0
+                .mFirstChild.raw::<nsIContent>()
+                .as_ref()
+                .map(GeckoNode::from_content) }
     }
 
     #[inline]
     fn last_child(&self) -> Option<Self> {
         unsafe { Gecko_GetLastChild(self.0).map(GeckoNode) }
     }
 
     #[inline]
     fn prev_sibling(&self) -> Option<Self> {
-        unsafe {
-            self.0
-                .mPreviousSibling
-                .as_ref()
-                .map(GeckoNode::from_content)
-        }
+        unsafe { Gecko_GetPreviousSibling(self.0).map(GeckoNode) }
     }
 
     #[inline]
     fn next_sibling(&self) -> Option<Self> {
-        unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) }
+        unsafe {
+            self.0
+                .mNextSibling.raw::<nsIContent>()
+                .as_ref()
+                .map(GeckoNode::from_content) }
     }
 
     #[inline]
     fn owner_doc(&self) -> Self::ConcreteDocument {
         debug_assert!(!self.node_info().mDocument.is_null());
         GeckoDocument(unsafe { &*self.node_info().mDocument })
     }
 
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -30,27 +30,25 @@ skip-if = os == 'android' && debug # The
 [test_ext_contentscript_exporthelpers.js]
 [test_ext_contentscript_restrictSchemes.js]
 [test_ext_contentscript_teardown.js]
 [test_ext_contextual_identities.js]
 skip-if = appname == "thunderbird" || os == "android" # Containers are not exposed to android.
 [test_ext_debugging_utils.js]
 [test_ext_dns.js]
 [test_ext_downloads.js]
+skip-if = appname == "thunderbird"
 [test_ext_downloads_download.js]
-fail-if = appname == "thunderbird"
-skip-if = os == "android"
+skip-if = appname == "thunderbird" || os == "android"
 [test_ext_downloads_misc.js]
-fail-if = appname == "thunderbird"
-skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
+skip-if = appname == "thunderbird" || os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
 [test_ext_downloads_private.js]
 skip-if = appname == "thunderbird" || os == "android"
 [test_ext_downloads_search.js]
-fail-if = appname == "thunderbird"
-skip-if = os == "android"
+skip-if = appname == "thunderbird" || os == "android"
 [test_ext_error_location.js]
 [test_ext_eventpage_warning.js]
 [test_ext_experiments.js]
 fail-if = appname == "thunderbird"
 [test_ext_extension.js]
 [test_ext_extensionPreferencesManager.js]
 [test_ext_extensionSettingsStore.js]
 [test_ext_extension_content_telemetry.js]
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1231,48 +1231,26 @@
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 50,
     "high": 50000000,
     "n_buckets": 100,
     "description": "Time spent decoding an image (us)"
   },
-  "IMAGE_ANIMATED_DECODE_TIME": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
-    "bug_numbers": [1347302],
-    "expires_in_version": "57",
-    "kind": "exponential",
-    "low": 50,
-    "high": 50000000,
-    "n_buckets": 100,
-    "description": "Time spent decoding an animated image (us)"
-  },
   "IMAGE_DECODE_ON_DRAW_LATENCY": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 50,
     "high": 50000000,
     "n_buckets": 100,
     "description": "Time from starting a decode to it showing up on the screen (us)"
   },
-  "IMAGE_ANIMATED_DECODE_ON_DRAW_LATENCY": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
-    "bug_numbers": [1347302],
-    "expires_in_version": "57",
-    "kind": "exponential",
-    "low": 50,
-    "high": 50000000,
-    "n_buckets": 100,
-    "description": "Time from starting a decode of an animated image to it showing up on the screen (us)"
-  },
   "IMAGE_DECODE_CHUNKS": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 500,
     "n_buckets": 50,
     "description": "Number of chunks per decode attempt"
@@ -1281,26 +1259,16 @@
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 500,
     "n_buckets": 50,
     "description": "Decode count"
   },
-  "IMAGE_ANIMATED_DECODE_COUNT": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
-    "bug_numbers": [1347302],
-    "expires_in_version": "57",
-    "kind": "exponential",
-    "high": 500,
-    "n_buckets": 50,
-    "description": "Decode count of animated images"
-  },
   "IMAGE_DECODE_SPEED_JPEG": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 500,
     "high": 50000000,
     "n_buckets": 50,