From 2b1c90da3e849e1c9d7457658290aa8eb01d0fa9 Mon Sep 17 00:00:00 2001
From: Mats Palmgren <mats@mozilla.com>
Date: Thu, 16 Apr 2015 09:04:19 +0000
Subject: [PATCH] Bug 1153478 - Part 1: Add nsInlineFrame::StealFrame and make
 it deal with being called on the wrong parent for aChild (due to lazy
 reparenting). r=roc, a=sledru

---
 layout/generic/nsContainerFrame.cpp |  7 +++----
 layout/generic/nsInlineFrame.cpp    | 39 +++++++++++++++++++++++++++++++++++++
 layout/generic/nsInlineFrame.h      |  4 +++-
 3 files changed, 45 insertions(+), 5 deletions(-)

diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp
index 3ffcba7..34878af 100644
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -172,13 +172,12 @@ nsContainerFrame::RemoveFrame(ChildListID aListID,
   nsIPresShell* shell = PresContext()->PresShell();
   nsContainerFrame* lastParent = nullptr;
   while (aOldFrame) {
-    //XXXfr probably should use StealFrame here. I'm not sure if we need to
-    //      check the overflow lists atm, but we'll need a prescontext lookup
-    //      for overflow containers once we can split abspos elements with
-    //      inline containing blocks.
     nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
     nsContainerFrame* parent =
       static_cast<nsContainerFrame*>(aOldFrame->GetParent());
+    // Please note that 'parent' may not actually be where 'aOldFrame' lives.
+    // We really MUST use StealFrame() and nothing else here.
+    // @see nsInlineFrame::StealFrame for details.
     parent->StealFrame(aOldFrame, true);
     aOldFrame->Destroy();
     aOldFrame = oldFrameNextContinuation;
diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp
index 526041e..a392a15 100644
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -172,6 +172,45 @@ nsInlineFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
   return CONTINUE;
 }
 
+nsresult
+nsInlineFrame::StealFrame(nsIFrame* aChild,
+                          bool      aForceNormal)
+{
+  if (aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) &&
+      !aForceNormal) {
+    return nsContainerFrame::StealFrame(aChild, aForceNormal);
+  }
+
+  nsInlineFrame* parent = this;
+  bool removed = false;
+  do {
+    removed = parent->mFrames.StartRemoveFrame(aChild);
+    if (removed) {
+      break;
+    }
+
+    // We didn't find the child in our principal child list.
+    // Maybe it's on the overflow list?
+    nsFrameList* frameList = parent->GetOverflowFrames();
+    if (frameList) {
+      removed = frameList->ContinueRemoveFrame(aChild);
+      if (frameList->IsEmpty()) {
+        parent->DestroyOverflowList();
+      }
+      if (removed) {
+        break;
+      }
+    }
+
+    // Due to our "lazy reparenting" optimization 'aChild' might not actually
+    // be on any of our child lists, but instead in one of our next-in-flows.
+    parent = static_cast<nsInlineFrame*>(parent->GetNextInFlow());
+  } while (parent);
+
+  MOZ_ASSERT(removed, "nsInlineFrame::StealFrame: can't find aChild");
+  return removed ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
 void
 nsInlineFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
diff --git a/layout/generic/nsInlineFrame.h b/layout/generic/nsInlineFrame.h
index 1a9899e..3e49241 100644
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -61,7 +61,9 @@ public:
 
   virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
                                      bool aRespectClusters = true) MOZ_OVERRIDE;
-  
+
+  virtual nsresult StealFrame(nsIFrame* aChild, bool aForceNormal) MOZ_OVERRIDE;
+
   // nsIHTMLReflow overrides
   virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext,
                                  InlineMinWidthData *aData) MOZ_OVERRIDE;
-- 
2.2.1

