Bug 468563 - [r=roc a=dveditz]
authorMats Palmgren <matspal@gmail.com>
Sun, 21 Nov 2010 14:42:31 -0800
changeset 27225 cf3d01c081fdc2d93e1e2ca989d2b565548b9401
parent 27224 179b608dc745b3b4a04eeb7531dff89e7e424a94
child 27226 bf70192dcb9c3e974b74841cea8296a8f6714f50
child 27229 3b74f6a6bc710d73864f0b4cceb3c6d1e12bf331
push id2582
push userreed@reedloden.com
push dateSun, 21 Nov 2010 22:43:00 +0000
reviewersroc, dveditz
bugs468563
milestone1.9.1.16pre
Bug 468563 - [r=roc a=dveditz]
layout/base/nsCSSFrameConstructor.cpp
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9199,16 +9199,43 @@ nsCSSFrameConstructor::ReinsertContent(n
     // If ContentRemoved just reconstructed everything, there is no need to
     // reinsert the content here
     res = ContentInserted(aContainer, aChild, ix, nsnull);
   }
 
   return res;
 }
 
+static void
+DoDeletingFrameSubtree(nsFrameManager* aFrameManager,
+                       nsVoidArray&    aDestroyQueue,
+                       nsIFrame*       aRemovedFrame,
+                       nsIFrame*       aFrame);
+
+static void
+DoDeletingOverflowContainers(nsFrameManager* aFrameManager,
+                             nsVoidArray&    aDestroyQueue,
+                             nsIFrame*       aRemovedFrame,
+                             nsIFrame*       aFrame)
+{
+  // The invariant that "continuing frames should be found as part of the
+  // walk over the top-most frame's continuing frames" does not hold for
+  // out-of-flow overflow containers, so we need to walk them too.
+  // Note that DoDeletingFrameSubtree() skips the child lists where
+  // overflow containers live so we won't process them twice.
+  const PRBool orphanSubtree = aRemovedFrame == aFrame;
+  for (nsIFrame* next = aFrame->GetNextContinuation();
+       next && (next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
+       next = next->GetNextContinuation()) {
+    DoDeletingFrameSubtree(aFrameManager, aDestroyQueue,
+                           orphanSubtree ? next : aRemovedFrame,
+                           next);
+  }
+}
+
 /**
  * Called when a frame subtree is about to be deleted. Two important
  * things happen:
  *
  * 1. For each frame in the subtree, we remove the mapping from the
  *    content object to its frame
  *
  * 2. For child frames that have been moved out of the flow, we enqueue
@@ -9222,17 +9249,17 @@ nsCSSFrameConstructor::ReinsertContent(n
  * @param   aRemovedFrame this is the frame that was removed from the
  *            content model. As we recurse we need to remember this so we
  *            can check if out-of-flow frames are a descendant of the frame
  *            being removed
  * @param   aFrame the local subtree that is being deleted. This is initially
  *            the same as aRemovedFrame, but as we recurse down the tree
  *            this changes
  */
-static nsresult
+static void
 DoDeletingFrameSubtree(nsFrameManager* aFrameManager,
                        nsVoidArray&    aDestroyQueue,
                        nsIFrame*       aRemovedFrame,
                        nsIFrame*       aFrame)
 {
   // Remove the mapping from the content object to its frame.
   nsIContent* content = aFrame->GetContent();
   if (content) {
@@ -9242,20 +9269,25 @@ DoDeletingFrameSubtree(nsFrameManager* a
 
   nsIAtom* childListName = nsnull;
   PRInt32 childListIndex = 0;
 
   do {
     // Walk aFrame's normal flow child frames looking for placeholder frames.
     nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
     for (; childFrame; childFrame = childFrame->GetNextSibling()) {
+      if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+        NS_ASSERTION(childListName == nsGkAtoms::overflowContainersList ||
+                     childListName == nsGkAtoms::excessOverflowContainersList,
+                     "out-of-flow on wrong child list");
+        continue;
+      }
       if (NS_LIKELY(nsGkAtoms::placeholderFrame != childFrame->GetType())) {
         DoDeletingFrameSubtree(aFrameManager, aDestroyQueue,
                                aRemovedFrame, childFrame);
-
       } else {
         nsIFrame* outOfFlowFrame =
           nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
   
         // Remove the mapping from the out-of-flow frame to its placeholder.
         aFrameManager->UnregisterPlaceholderFrame((nsPlaceholderFrame*)childFrame);
         // Don't SetOutOfFlowFrame(nsnull) here because the float cache depends
         // on it when the float is removed later on, see bug 348688 comment 6.
@@ -9267,34 +9299,38 @@ DoDeletingFrameSubtree(nsFrameManager* a
         if (outOfFlowFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP ||
             !nsLayoutUtils::IsProperAncestorFrame(aRemovedFrame, outOfFlowFrame)) {
           NS_ASSERTION(aDestroyQueue.IndexOf(outOfFlowFrame) == kNotFound,
                        "out-of-flow is already in the destroy queue");
           aDestroyQueue.AppendElement(outOfFlowFrame);
           // Recurse into the out-of-flow, it is now the aRemovedFrame.
           DoDeletingFrameSubtree(aFrameManager, aDestroyQueue,
                                  outOfFlowFrame, outOfFlowFrame);
+          DoDeletingOverflowContainers(aFrameManager, aDestroyQueue,
+                                       outOfFlowFrame, outOfFlowFrame);
         }
         else {
           // Also recurse into the out-of-flow when it's a descendant of aRemovedFrame
           // since we don't walk those lists, see |childListName| increment below.
           DoDeletingFrameSubtree(aFrameManager, aDestroyQueue,
                                  aRemovedFrame, outOfFlowFrame);
+          DoDeletingOverflowContainers(aFrameManager, aDestroyQueue,
+                                       aRemovedFrame, outOfFlowFrame);
         }
       }
     }
 
     // Move to next child list but skip lists with frames we should have
-    // a placeholder for.
+    // a placeholder for.  Note that we only process in-flow overflow
+    // containers on the overflowContainersList/excessOverflowContainersList,
+    // out-of-flows are reached through the next-in-flow chain (bug 468563).
     do {
       childListName = aFrame->GetAdditionalChildListName(childListIndex++);
     } while (IsOutOfFlowList(childListName));
   } while (childListName);
-
-  return NS_OK;
 }
 
 /**
  * Called when a frame is about to be deleted. Calls DoDeletingFrameSubtree()
  * for aFrame and each of its continuing frames
  */
 static nsresult
 DeletingFrameSubtree(nsFrameManager* aFrameManager,
@@ -9318,16 +9354,20 @@ DeletingFrameSubtree(nsFrameManager* aFr
   do {
     DoDeletingFrameSubtree(aFrameManager, destroyQueue, aFrame, aFrame);
 
     // If it's split, then get the continuing frame. Note that we only do
     // this for the top-most frame being deleted. Don't do it if we're
     // recursing over a subtree, because those continuing frames should be
     // found as part of the walk over the top-most frame's continuing frames.
     // Walking them again will make this an N^2/2 algorithm.
+    // The above is true for normal child next-in-flows but not overflow
+    // containers which we do walk because they *can* escape the subtree
+    // we're deleting.  We skip [excess]overflowContainersList where
+    // they live to avoid processing them more than once.
     aFrame = aFrame->GetNextContinuation();
   } while (aFrame);
 
   // Now destroy any out-of-flow frames that have been enqueued for
   // destruction.
   for (PRInt32 i = destroyQueue.Count() - 1; i >= 0; --i) {
     nsIFrame* outOfFlowFrame = static_cast<nsIFrame*>(destroyQueue[i]);