Bug 504221 part 3. Switch overflowFrames storage to nsFrameList. r=fantasai, r+sr=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 28 Jul 2009 08:51:09 -0400
changeset 30781 7b288267eb7ca4af310cae4475a021d907e06bb0
parent 30780 ae178a6cd27678851b2b2808174933513e36fc27
child 30782 bab42673f96dd3edb65f0e26417704c9cef8abee
push idunknown
push userunknown
push dateunknown
reviewersfantasai, r
bugs504221
milestone1.9.2a1pre
Bug 504221 part 3. Switch overflowFrames storage to nsFrameList. r=fantasai, r+sr=roc
layout/build/nsLayoutStatics.cpp
layout/generic/nsColumnSetFrame.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFirstLetterFrame.cpp
layout/generic/nsFrameList.cpp
layout/generic/nsFrameList.h
layout/generic/nsHTMLFrame.cpp
layout/generic/nsInlineFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableRowGroupFrame.cpp
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -80,16 +80,17 @@
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsXMLHttpRequest.h"
 #include "nsDOMThreadService.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsHtml5Module.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsFocusManager.h"
+#include "nsFrameList.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULElement.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
@@ -274,16 +275,22 @@ nsLayoutStatics::Initialize()
 #ifdef MOZ_SYDNEYAUDIO
   nsAudioStream::InitLibrary();
 #endif
 
   nsHtml5Module::InitializeStatics();
   
   nsCrossSiteListenerProxy::Startup();
 
+  rv = nsFrameList::Init();
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Could not initialize nsFrameList");
+    return rv;
+  }
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   nsFocusManager::Shutdown();
 #ifdef MOZ_XUL
@@ -363,16 +370,18 @@ nsLayoutStatics::Shutdown()
   nsAudioStream::ShutdownLibrary();
 #endif
 
   nsXMLHttpRequest::ShutdownACCache();
   
   nsHtml5Module::ReleaseStatics();
 
   NS_ShutdownChainItemPool();
+
+  nsFrameList::Shutdown();
 }
 
 void
 nsLayoutStatics::AddRef()
 {
   NS_ASSERTION(sLayoutStaticRefcnt,
                "nsLayoutStatics already dropped to zero!");
 
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -860,43 +860,32 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
 
 void
 nsColumnSetFrame::DrainOverflowColumns()
 {
   // First grab the prev-in-flows overflows and reparent them to this
   // frame.
   nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
   if (prev) {
-    nsIFrame* overflows = prev->GetOverflowFrames(PresContext(), PR_TRUE);
+    nsAutoPtr<nsFrameList> overflows(prev->StealOverflowFrames());
     if (overflows) {
-      // Make all the frames on the overflow list mine
-      nsIFrame* lastFrame = nsnull;
-      for (nsIFrame* f = overflows; f; f = f->GetNextSibling()) {
-        f->SetParent(this);
+      nsHTMLContainerFrame::ReparentFrameViewList(PresContext(), *overflows,
+                                                  prev, this);
 
-        // When pushing and pulling frames we need to check for whether any
-        // views need to be reparented
-        nsHTMLContainerFrame::ReparentFrameView(PresContext(), f, prev, this);
-
-        // Get the next frame
-        lastFrame = f;
-      }
-
-      NS_ASSERTION(lastFrame, "overflow list was created with no frames");
-      lastFrame->SetNextSibling(mFrames.FirstChild());
-      
-      mFrames.SetFrames(overflows);
+      mFrames.InsertFrames(this, nsnull, *overflows);
     }
   }
   
   // Now pull back our own overflows and append them to our children.
   // We don't need to reparent them since we're already their parent.
-  nsIFrame* overflows = GetOverflowFrames(PresContext(), PR_TRUE);
+  nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
   if (overflows) {
-    mFrames.AppendFrames(this, overflows);
+    // We're already the parent for these frames, so no need to set
+    // their parent again.
+    mFrames.AppendFrames(nsnull, *overflows);
   }
 }
 
 NS_IMETHODIMP 
 nsColumnSetFrame::Reflow(nsPresContext*           aPresContext,
                          nsHTMLReflowMetrics&     aDesiredSize,
                          const nsHTMLReflowState& aReflowState,
                          nsReflowStatus&          aStatus)
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -263,18 +263,17 @@ nsContainerFrame::Destroy()
   }
 
   // Delete the primary child list
   mFrames.DestroyFrames();
 
   // Destroy auxiliary frame lists
   nsPresContext* prescontext = PresContext();
 
-  nsFrameList overflowFrames(GetOverflowFrames(prescontext, PR_TRUE));
-  overflowFrames.DestroyFrames();
+  DestroyOverflowList(prescontext);
 
   if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
     nsFrameList* frameList = RemovePropTableFrames(prescontext,
                                nsGkAtoms::overflowContainersProperty);
     if (frameList)
       frameList->Destroy();
 
     frameList = RemovePropTableFrames(prescontext,
@@ -315,17 +314,18 @@ nsContainerFrame::Destroy()
 nsIFrame*
 nsContainerFrame::GetFirstChild(nsIAtom* aListName) const
 {
   // We only know about the unnamed principal child list and the overflow
   // list
   if (nsnull == aListName) {
     return mFrames.FirstChild();
   } else if (nsGkAtoms::overflowList == aListName) {
-    return GetOverflowFrames(PresContext(), PR_FALSE);
+    nsFrameList* frameList = GetOverflowFrames();
+    return frameList ? frameList->FirstChild() : nsnull;
   } else if (nsGkAtoms::overflowContainersList == aListName) {
     nsFrameList* list = GetPropTableFrames(PresContext(),
                           nsGkAtoms::overflowContainersProperty);
     return (list) ? list->FirstChild() : nsnull;
   } else if (nsGkAtoms::excessOverflowContainersList == aListName) {
     nsFrameList* list = GetPropTableFrames(PresContext(),
                           nsGkAtoms::excessOverflowContainersProperty);
     return (list) ? list->FirstChild() : nsnull;
@@ -1121,27 +1121,37 @@ nsContainerFrame::StealFrame(nsPresConte
       removed = RemovePropTableFrame(aPresContext, aChild,
                   nsGkAtoms::excessOverflowContainersProperty);
     }
   }
   else {
     if (!mFrames.RemoveFrame(aChild)) {
       // We didn't find the child in the parent's principal child list.
       // Maybe it's on the overflow list?
-      nsFrameList frameList(GetOverflowFrames(aPresContext, PR_TRUE));
-      removed = frameList.RemoveFrame(aChild);
-      if (frameList.NotEmpty()) {
-        nsresult rv = SetOverflowFrames(aPresContext, frameList.FirstChild());
-        NS_ENSURE_SUCCESS(rv, rv);
+      nsFrameList* frameList = GetOverflowFrames();
+      if (frameList) {
+        removed = frameList->RemoveFrame(aChild);
+        if (frameList->IsEmpty()) {
+          DestroyOverflowList(aPresContext);
+        }
       }
     }
   }
   return (removed) ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
+void
+nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext)
+{
+  nsFrameList* list =
+    RemovePropTableFrames(aPresContext, nsGkAtoms::overflowList);
+  if (list)
+    list->Destroy();
+}
+
 /**
  * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
  * pointers
  */
 void
 nsContainerFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
                                         nsIFrame*      aNextInFlow,
                                         PRBool         aDeletingEmptyFrames)
@@ -1179,77 +1189,59 @@ nsContainerFrame::DeleteNextInFlowChild(
 
   // Delete the next-in-flow frame and its descendants. This will also
   // remove it from its next-in-flow/prev-in-flow chain.
   aNextInFlow->Destroy();
 
   NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
 }
 
-/**
- * Get the frames on the overflow list
- */
-nsIFrame*
-nsContainerFrame::GetOverflowFrames(nsPresContext*  aPresContext,
-                                    PRBool          aRemoveProperty) const
-{
-  nsPropertyTable *propTable = aPresContext->PropertyTable();
-  if (aRemoveProperty) {
-    return (nsIFrame*) propTable->UnsetProperty(this,
-                                                nsGkAtoms::overflowProperty);
-  }
-  return (nsIFrame*) propTable->GetProperty(this,
-                                            nsGkAtoms::overflowProperty);
-}
-
-// Destructor function for the overflow frame property
-static void
-DestroyOverflowFrames(void*           aFrame,
-                      nsIAtom*        aPropertyName,
-                      void*           aPropertyValue,
-                      void*           aDtorData)
-{
-  if (aPropertyValue) {
-    nsFrameList frames((nsIFrame*)aPropertyValue);
-
-    frames.DestroyFrames();
-  }
-}
-
-/**
- * Set the frames on the overflow list
- */
-nsresult
-nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
-                                    nsIFrame*       aOverflowFrames)
-{
-  nsresult rv =
-    aPresContext->PropertyTable()->SetProperty(this,
-                                               nsGkAtoms::overflowProperty,
-                                               aOverflowFrames,
-                                               DestroyOverflowFrames,
-                                               nsnull);
-
-  // Verify that we didn't overwrite an existing overflow list
-  NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow list");
-
-  return rv;
-}
-
 // Destructor function for the proptable-stored framelists
 static void
 DestroyFrameList(void*           aFrame,
                  nsIAtom*        aPropertyName,
                  void*           aPropertyValue,
                  void*           aDtorData)
 {
   if (aPropertyValue)
     static_cast<nsFrameList*>(aPropertyValue)->Destroy();
 }
 
+/**
+ * Set the frames on the overflow list
+ */
+nsresult
+nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
+                                    const nsFrameList& aOverflowFrames)
+{
+  NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
+  nsFrameList* newList = new nsFrameList(aOverflowFrames);
+  if (!newList) {
+    // XXXbz should really destroy the frames here, but callers are holding
+    // pointers to them.... We should switch all callers to framelists, then
+    // audit and do that.
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsresult rv =
+    aPresContext->PropertyTable()->SetProperty(this,
+                                               nsGkAtoms::overflowProperty,
+                                               newList,
+                                               DestroyFrameList,
+                                               nsnull);
+  if (NS_FAILED(rv)) {
+    newList->Destroy();
+  }
+
+  // Verify that we didn't overwrite an existing overflow list
+  NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow list");
+
+  return rv;
+}
+
 nsFrameList*
 nsContainerFrame::GetPropTableFrames(nsPresContext*  aPresContext,
                                      nsIAtom*        aPropID) const
 {
   nsPropertyTable* propTable = aPresContext->PropertyTable();
   return static_cast<nsFrameList*>(propTable->GetProperty(this, aPropID));
 }
 
@@ -1355,35 +1347,37 @@ nsContainerFrame::PushChildren(nsPresCon
 PRBool
 nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
 {
   PRBool result = PR_FALSE;
 
   // Check for an overflow list with our prev-in-flow
   nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
   if (nsnull != prevInFlow) {
-    nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext,
-                                                                 PR_TRUE);
+    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
-      NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
+      // Tables are special; they can have repeated header/footer
+      // frames on mFrames at this point.
+      NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
+                   "bad overflow list");
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
-      for (nsIFrame* f = prevOverflowFrames; f; f = f->GetNextSibling()) {
-        nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
-      }
-      mFrames.InsertFrames(this, nsnull, prevOverflowFrames);
+      nsHTMLContainerFrame::ReparentFrameViewList(aPresContext,
+                                                  *prevOverflowFrames,
+                                                  prevInFlow, this);
+      mFrames.AppendFrames(this, *prevOverflowFrames);
       result = PR_TRUE;
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
-  nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
+  nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
-    mFrames.AppendFrames(nsnull, overflowFrames);
+    mFrames.AppendFrames(nsnull, *overflowFrames);
     result = PR_TRUE;
   }
   return result;
 }
 
 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsPresContext*    aPresContext,
                                                              nsContainerFrame* aFrame,
                                                              PRBool            aWalkOOFFrames,
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -39,16 +39,17 @@
 /* base class #1 for rendering objects that have child lists */
 
 #ifndef nsContainerFrame_h___
 #define nsContainerFrame_h___
 
 #include "nsSplittableFrame.h"
 #include "nsFrameList.h"
 #include "nsLayoutUtils.h"
+#include "nsAutoPtr.h"
 
 /**
  * Child list name indices
  * @see #GetAdditionalChildListName()
  */
 #define NS_CONTAINER_LIST_COUNT_SANS_OC 1
   // for frames that don't use overflow containers
 #define NS_CONTAINER_LIST_COUNT_INCL_OC 3
@@ -344,25 +345,43 @@ protected:
   // ==========================================================================
   /* Overflow Frames are frames that did not fit and must be pulled by
    * our next-in-flow during its reflow. (The same concept for overflow
    * containers is called "excess frames". We should probably make the
    * names match.)
    */
 
   /**
-   * Get the frames on the overflow list
+   * Get the frames on the overflow list.  Can return null if there are no
+   * overflow frames.  The caller does NOT take ownership of the list; it's
+   * still owned by this frame.  A non-null return value indicates that the
+   * list is nonempty.
    */
-  nsIFrame* GetOverflowFrames(nsPresContext*  aPresContext,
-                              PRBool          aRemoveProperty) const;
+  inline nsFrameList* GetOverflowFrames() const;
+
   /**
-   * Set the overflow list
+   * As GetOverflowFrames, but removes the overflow frames property.  The
+   * caller is responsible for deleting nsFrameList and either passing
+   * ownership of the frames to someone else or destroying the frames.  A
+   * non-null return value indicates that the list is nonempty.  The
+   * recommended way to use this function it to assign its return value
+   * into an nsAutoPtr.
+   */
+  inline nsFrameList* StealOverflowFrames();
+  
+  /**
+   * Set the overflow list.  aOverflowFrames must not be an empty list.
    */
   nsresult SetOverflowFrames(nsPresContext*  aPresContext,
-                             nsIFrame*       aOverflowFrames);
+                             const nsFrameList& aOverflowFrames);
+
+  /**
+   * Destroy the overflow list and any frames that are on  it.
+   */
+  void DestroyOverflowList(nsPresContext* aPresContext);
 
   /**
    * Moves any frames on both the prev-in-flow's overflow list and the
    * receiver's overflow to the receiver's child list.
    *
    * Resets the overlist pointers to nsnull, and updates the receiver's child
    * count and content mapping.
    *
@@ -564,9 +583,29 @@ private:
   nsContainerFrame* mParent;
   /* Tells SetUpListWalker whether or not to walk us past any continuations
      of overflow containers. aWalkOOFFrames is ignored when this is false. */
   PRBool mSkipOverflowContainerChildren;
   /* Tells us whether to pay attention to OOF frames or non-OOF frames */
   PRBool mWalkOOFFrames;
 };
 
+inline
+nsFrameList*
+nsContainerFrame::GetOverflowFrames() const
+{
+  nsFrameList* list =
+    static_cast<nsFrameList*>(GetProperty(nsGkAtoms::overflowProperty));
+  NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
+  return list;
+}
+
+inline
+nsFrameList*
+nsContainerFrame::StealOverflowFrames()
+{
+  nsFrameList* list =
+    static_cast<nsFrameList*>(UnsetProperty(nsGkAtoms::overflowProperty));
+  NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
+  return list;
+}
+
 #endif /* nsContainerFrame_h___ */
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -295,41 +295,38 @@ nsFirstLetterFrame::CanContinueTextRun()
 {
   // We can continue a text run through a first-letter frame.
   return PR_TRUE;
 }
 
 void
 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
 {
-  nsIFrame* overflowFrames;
+  nsAutoPtr<nsFrameList> overflowFrames;
 
   // Check for an overflow list with our prev-in-flow
   nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
   if (nsnull != prevInFlow) {
-    overflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
+    overflowFrames = prevInFlow->StealOverflowFrames();
     if (overflowFrames) {
       NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
 
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
-      nsIFrame* f = overflowFrames;
-      while (f) {
-        nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
-        f = f->GetNextSibling();
-      }
-      mFrames.InsertFrames(this, nsnull, overflowFrames);
+      nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, *overflowFrames,
+                                                  prevInFlow, this);
+      mFrames.InsertFrames(this, nsnull, *overflowFrames);
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
-  overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
+  overflowFrames = StealOverflowFrames();
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
-    mFrames.AppendFrames(nsnull, overflowFrames);
+    mFrames.AppendFrames(nsnull, *overflowFrames);
   }
 
   // Now repair our first frames style context (since we only reflow
   // one frame there is no point in doing any other ones until they
   // are reflowed)
   nsIFrame* kid = mFrames.FirstChild();
   if (kid) {
     nsRefPtr<nsStyleContext> sc;
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -47,16 +47,30 @@
 
 #ifdef IBMBIDI
 #include "nsCOMPtr.h"
 #include "nsGkAtoms.h"
 #include "nsILineIterator.h"
 #include "nsBidiPresUtils.h"
 #endif // IBMBIDI
 
+const nsFrameList* nsFrameList::sEmptyList;
+
+/* static */
+nsresult
+nsFrameList::Init()
+{
+  NS_PRECONDITION(!sEmptyList, "Shouldn't be allocated");
+  sEmptyList = new nsFrameList();
+  if (!sEmptyList)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  return NS_OK;
+}
+
 void
 nsFrameList::Destroy()
 {
   DestroyFrames();
   delete this;
 }
 
 void
@@ -198,47 +212,58 @@ nsFrameList::InsertFrame(nsIFrame* aPare
 #endif
 }
 
 void
 nsFrameList::InsertFrames(nsIFrame* aParent,
                           nsIFrame* aPrevSibling,
                           nsIFrame* aFrameList)
 {
+  // XXXbz once we have fast access to last frames (and have an nsFrameList
+  // here, not an nsIFrame*), we should clean up this method significantly.
   NS_PRECONDITION(nsnull != aFrameList, "null ptr");
   if (nsnull != aFrameList) {
     nsIFrame* lastNewFrame = nsnull;
     if (aParent) {
       for (nsIFrame* frame = aFrameList; frame;
            frame = frame->GetNextSibling()) {
         frame->SetParent(aParent);
         lastNewFrame = frame;
       }
     }
 
     // Get the last new frame if necessary
-    if (!lastNewFrame) {
+    if (!lastNewFrame &&
+        ((aPrevSibling && aPrevSibling->GetNextSibling()) ||
+         mFirstChild)) {
       nsFrameList tmp(aFrameList);
       lastNewFrame = tmp.LastChild();
     }
 
     // Link the new frames into the child list
-    if (nsnull == aPrevSibling) {
-      lastNewFrame->SetNextSibling(mFirstChild);
+    if (!aPrevSibling) {
+      NS_ASSERTION(lastNewFrame || !mFirstChild,
+                   "Should have lastNewFrame here");
+      if (lastNewFrame) {
+        lastNewFrame->SetNextSibling(mFirstChild);
+      }
       mFirstChild = aFrameList;
     }
     else {
       NS_ASSERTION(aFrameList->GetParent() == aPrevSibling->GetParent(),
                    "prev sibling has different parent");
       nsIFrame* nextFrame = aPrevSibling->GetNextSibling();
       NS_ASSERTION(!nextFrame ||
                    aFrameList->GetParent() == nextFrame->GetParent(),
                    "next sibling has different parent");
       aPrevSibling->SetNextSibling(aFrameList);
-      lastNewFrame->SetNextSibling(nextFrame);
+      NS_ASSERTION(lastNewFrame || !nextFrame, "Should have lastNewFrame here");
+      if (lastNewFrame) {
+        lastNewFrame->SetNextSibling(nextFrame);
+      }
     }
   }
 #ifdef DEBUG
   CheckForLoops();
 #endif
 }
 
 PRBool
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -198,16 +198,27 @@ public:
 
   PRBool ContainsFrame(const nsIFrame* aFrame) const;
   PRBool ContainsFrameBefore(const nsIFrame* aFrame, const nsIFrame* aEnd) const;
 
   PRInt32 GetLength() const;
 
   nsIFrame* GetPrevSiblingFor(nsIFrame* aFrame) const;
 
+  /**
+   * If this frame list has only one frame, return that frame.
+   * Otherwise, return null.
+   */
+  nsIFrame* OnlyChild() const {
+    if (FirstChild() == LastChild()) {
+      return FirstChild();
+    }
+    return nsnull;
+  }
+
 #ifdef IBMBIDI
   /**
    * Return the frame before this frame in visual order (after Bidi reordering).
    * If aFrame is null, return the last frame in visual order.
    */
   nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const;
 
   /**
@@ -216,16 +227,20 @@ public:
    */
   nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
 #endif // IBMBIDI
 
 #ifdef DEBUG
   void List(FILE* out) const;
 #endif
 
+  static nsresult Init();
+  static void Shutdown() { delete sEmptyList; }
+  static const nsFrameList& EmptyList() { return *sEmptyList; }
+
   class Enumerator;
 
   /**
    * A class representing a slice of a frame list.
    */
   class Slice {
     friend class Enumerator;
 
@@ -300,14 +315,16 @@ public:
     const nsIFrame* const mEnd; // The first frame we should NOT enumerate.
                                 // May be null.
   };
 
 private:
 #ifdef DEBUG
   void CheckForLoops();
 #endif
-  
+
+  static const nsFrameList* sEmptyList;
+
 protected:
   nsIFrame* mFirstChild;
 };
 
 #endif /* nsFrameList_h___ */
--- a/layout/generic/nsHTMLFrame.cpp
+++ b/layout/generic/nsHTMLFrame.cpp
@@ -596,25 +596,26 @@ CanvasFrame::Reflow(nsPresContext*      
   NS_FRAME_TRACE_REFLOW_IN("CanvasFrame::Reflow");
 
   // Initialize OUT parameter
   aStatus = NS_FRAME_COMPLETE;
 
   CanvasFrame* prevCanvasFrame = static_cast<CanvasFrame*>
                                                (GetPrevInFlow());
   if (prevCanvasFrame) {
-    nsIFrame* overflow = prevCanvasFrame->GetOverflowFrames(aPresContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> overflow(prevCanvasFrame->StealOverflowFrames());
     if (overflow) {
-      NS_ASSERTION(!overflow->GetNextSibling(),
+      NS_ASSERTION(overflow->OnlyChild(),
                    "must have doc root as canvas frame's only child");
-      nsHTMLContainerFrame::ReparentFrameView(aPresContext, overflow, prevCanvasFrame, this);
+      nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, *overflow,
+                                                  prevCanvasFrame, this);
       // Prepend overflow to the our child list. There may already be
       // children placeholders for fixed-pos elements, which don't get
       // reflowed but must not be lost until the canvas frame is destroyed.
-      mFrames.InsertFrames(this, nsnull, overflow);
+      mFrames.InsertFrames(this, nsnull, *overflow);
     }
   }
 
   // Reflow our one and only normal child frame. It's either the root
   // element's frame or a placeholder for that frame, if the root element
   // is abs-pos or fixed-pos. We may have additional children which
   // are placeholders for continuations of fixed-pos content, but those
   // don't need to be reflowed. The normal child is always comes before
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1,9 +1,8 @@
-
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -222,16 +221,18 @@ nsInlineFrame::ComputeTightBounds(gfxCon
   return ComputeSimpleTightBounds(aContext);
 }
 
 void
 nsInlineFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
                                             nsIFrame* aFrame,
                                             PRBool aReparentSiblings)
 {
+  // XXXbz this would be better if it took a nsFrameList or a frame
+  // list slice....
   NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
                aOurLineContainer->GetPrevContinuation(),
                "Don't call this when we have no continuation, it's a waste");
   if (!aFrame) {
     NS_ASSERTION(aReparentSiblings, "Why did we get called?");
     return;
   }
 
@@ -290,70 +291,72 @@ nsInlineFrame::Reflow(nsPresContext*    
 
   PRBool  lazilySetParentPointer = PR_FALSE;
 
   nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
 
    // Check for an overflow list with our prev-in-flow
   nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
   if (nsnull != prevInFlow) {
-    nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
 
     if (prevOverflowFrames) {
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
-      nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, prevOverflowFrames,
+      nsHTMLContainerFrame::ReparentFrameViewList(aPresContext,
+                                                  *prevOverflowFrames,
                                                   prevInFlow, this);
 
       // Check if we should do the lazilySetParentPointer optimization.
       // Only do it in simple cases where we're being reflowed for the
       // first time, nothing (e.g. bidi resolution) has already given
       // us children, and there's no next-in-flow, so all our frames
       // will be taken from prevOverflowFrames.
       if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && mFrames.IsEmpty() &&
           !GetNextInFlow()) {
-        // If our child list is empty, just set the child list rather than
-        // calling InsertFrame(). This avoids having to get the last child
-        // frame in the list.
+        // If our child list is empty, just put the new frames into it.
         // Note that we don't set the parent pointer for the new frames. Instead wait
         // to do this until we actually reflow the frame. If the overflow list contains
         // thousands of frames this is a big performance issue (see bug #5588)
-        mFrames.SetFrames(prevOverflowFrames);
+        mFrames.InsertFrames(nsnull, nsnull, *prevOverflowFrames);
         lazilySetParentPointer = PR_TRUE;
       } else {
         // Assign all floats to our block if necessary
         if (lineContainer && lineContainer->GetPrevContinuation()) {
-          ReparentFloatsForInlineChild(lineContainer, prevOverflowFrames, PR_TRUE);
+          ReparentFloatsForInlineChild(lineContainer,
+                                       prevOverflowFrames->FirstChild(),
+                                       PR_TRUE);
         }
         // Insert the new frames at the beginning of the child list
         // and set their parent pointer
-        mFrames.InsertFrames(this, nsnull, prevOverflowFrames);
+        mFrames.InsertFrames(this, nsnull, *prevOverflowFrames);
       }
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
 #ifdef DEBUG
   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     // If it's our initial reflow, then we should not have an overflow list.
     // However, add an assertion in case we get reflowed more than once with
     // the initial reflow reason
-    nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_FALSE);
-    NS_ASSERTION(!overflowFrames, "overflow list is not empty for initial reflow");
+    nsFrameList* overflowFrames = GetOverflowFrames();
+    NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
+                 "overflow list is not empty for initial reflow");
   }
 #endif
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
-    nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
     if (overflowFrames) {
       NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
 
       // Because we lazily set the parent pointer of child frames we get from
       // our prev-in-flow's overflow list, it's possible that we have not set
       // the parent pointer for these frames.
-      mFrames.AppendFrames(this, overflowFrames);
+      mFrames.AppendFrames(this, *overflowFrames);
     }
   }
 
   if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
 #ifdef DEBUG_kipp
     {
       extern char* nsPresShell_ReflowStackPointerTop;
       char marker;
@@ -398,22 +401,20 @@ nsInlineFrame::CanContinueTextRun() cons
   return PR_TRUE;
 }
 
 /* virtual */ void
 nsInlineFrame::PullOverflowsFromPrevInFlow()
 {
   nsInlineFrame* prevInFlow = static_cast<nsInlineFrame*>(GetPrevInFlow());
   if (prevInFlow) {
-    nsPresContext* presContext = PresContext();
-    nsIFrame* prevOverflowFrames =
-      prevInFlow->GetOverflowFrames(presContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // Assume that our prev-in-flow has the same line container that we do.
-      mFrames.InsertFrames(this, nsnull, prevOverflowFrames);
+      mFrames.InsertFrames(this, nsnull, *prevOverflowFrames);
     }
   }
 }
 
 nsresult
 nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
                             const nsHTMLReflowState& aReflowState,
                             InlineReflowState& irs,
@@ -557,18 +558,17 @@ nsInlineFrame::ReflowFrames(nsPresContex
       }
       irs.mPrevFrame = frame;
       frame = frame->GetNextSibling();
     }
   }
 #ifdef DEBUG
   if (NS_FRAME_IS_COMPLETE(aStatus)) {
     // We can't be complete AND have overflow frames!
-    nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_FALSE);
-    NS_ASSERTION(!overflowFrames, "whoops");
+    NS_ASSERTION(!GetOverflowFrames(), "whoops");
   }
 #endif
 
   // If after reflowing our children they take up no area then make
   // sure that we don't either.
   //
   // Note: CSS demands that empty inline elements still affect the
   // line-height calculations. However, continuations of an inline
@@ -874,25 +874,24 @@ NS_IMETHODIMP nsInlineFrame::GetAccessib
 #endif
 
 //////////////////////////////////////////////////////////////////////
 
 // nsLineFrame implementation
 
 static void
 ReParentChildListStyle(nsPresContext* aPresContext,
-                       nsFrameList& aFrameList,
+                       const nsFrameList::Slice& aFrames,
                        nsIFrame* aParentFrame)
 {
   nsFrameManager *frameManager = aPresContext->FrameManager();
 
-  for (nsIFrame* kid = aFrameList.FirstChild(); kid;
-       kid = kid->GetNextSibling()) {
-    NS_ASSERTION(kid->GetParent() == aParentFrame, "Bogus parentage");
-    frameManager->ReParentStyleContext(kid);
+  for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
+    NS_ASSERTION(e.get()->GetParent() == aParentFrame, "Bogus parentage");
+    frameManager->ReParentStyleContext(e.get());
   }
 }
 
 nsIFrame*
 NS_NewFirstLineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsFirstLineFrame(aContext);
 }
@@ -947,37 +946,38 @@ nsFirstLineFrame::Reflow(nsPresContext* 
     return NS_ERROR_INVALID_ARG;
   }
 
   nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
 
   // Check for an overflow list with our prev-in-flow
   nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
   if (nsnull != prevInFlow) {
-    nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
-      nsFrameList frames(prevOverflowFrames);
-      
       // Assign all floats to our block if necessary
       if (lineContainer && lineContainer->GetPrevContinuation()) {
-        ReparentFloatsForInlineChild(lineContainer, prevOverflowFrames, PR_TRUE);
+        ReparentFloatsForInlineChild(lineContainer,
+                                     prevOverflowFrames->FirstChild(),
+                                     PR_TRUE);
       }
-      mFrames.InsertFrames(this, nsnull, prevOverflowFrames);
-      ReParentChildListStyle(aPresContext, frames, this);
+      const nsFrameList::Slice& newFrames =
+        mFrames.InsertFrames(this, nsnull, *prevOverflowFrames);
+      ReParentChildListStyle(aPresContext, newFrames, this);
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
-  nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
+  nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
-    nsFrameList frames(overflowFrames);
 
-    mFrames.AppendFrames(nsnull, overflowFrames);
-    ReParentChildListStyle(aPresContext, frames, this);
+    const nsFrameList::Slice& newFrames =
+      mFrames.AppendFrames(nsnull, *overflowFrames);
+    ReParentChildListStyle(aPresContext, newFrames, this);
   }
 
   // Set our own reflow state (additional state above and beyond
   // aReflowState)
   InlineReflowState irs;
   irs.mPrevFrame = nsnull;
   irs.mLineContainer = lineContainer;
   irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
@@ -1051,25 +1051,22 @@ nsFirstLineFrame::Reflow(nsPresContext* 
   return rv;
 }
 
 /* virtual */ void
 nsFirstLineFrame::PullOverflowsFromPrevInFlow()
 {
   nsFirstLineFrame* prevInFlow = static_cast<nsFirstLineFrame*>(GetPrevInFlow());
   if (prevInFlow) {
-    nsPresContext* presContext = PresContext();
-    nsIFrame* prevOverflowFrames =
-      prevInFlow->GetOverflowFrames(presContext, PR_TRUE);
+    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
-      nsFrameList frames(prevOverflowFrames);
-
       // Assume that our prev-in-flow has the same line container that we do.
-      mFrames.InsertFrames(this, nsnull, prevOverflowFrames);
-      ReParentChildListStyle(presContext, frames, this);
+      const nsFrameList::Slice& newFrames =
+        mFrames.InsertFrames(this, nsnull, *prevOverflowFrames);
+      ReParentChildListStyle(PresContext(), newFrames, this);
     }
   }
 }
 
 //////////////////////////////////////////////////////////////////////
 
 nsIFrame*
 NS_NewPositionedInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -2066,59 +2066,21 @@ nsTableFrame::PushChildren(const FrameAr
     }
     // When pushing and pulling frames we need to check for whether any
     // views need to be reparented.
     for (nsIFrame* f = frames.FirstChild(); f; f = f->GetNextSibling()) {
       nsHTMLContainerFrame::ReparentFrameView(PresContext(), f, this, nextInFlow);
     }
     nextInFlow->mFrames.InsertFrames(GetNextInFlow(), prevSibling, frames.FirstChild());
   }
-  else {
+  else if (frames.NotEmpty()) {
     // Add the frames to our overflow list
-    SetOverflowFrames(PresContext(), frames.FirstChild());
-  }
-}
-
-// Table specific version that takes into account header and footer row group
-// frames that are repeated for continuing table frames
-//
-// Appends the overflow frames to the end of the child list, just like the
-// nsContainerFrame version does, except that there are no assertions that
-// the child list is empty (it may not be empty, because there may be repeated
-// header/footer frames)
-PRBool
-nsTableFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
-{
-  PRBool result = PR_FALSE;
-
-  // Check for an overflow list with our prev-in-flow
-  nsTableFrame* prevInFlow = (nsTableFrame*)GetPrevInFlow();
-  if (prevInFlow) {
-    nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
-    if (prevOverflowFrames) {
-      // When pushing and pulling frames we need to check for whether any
-      // views need to be reparented.
-      for (nsIFrame* f = prevOverflowFrames; f; f = f->GetNextSibling()) {
-        nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
-      }
-      mFrames.AppendFrames(this, prevOverflowFrames);
-      result = PR_TRUE;
-    }
-  }
-
-  // It's also possible that we have an overflow list for ourselves
-  nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
-  if (overflowFrames) {
-    mFrames.AppendFrames(nsnull, overflowFrames);
-    result = PR_TRUE;
-  }
-  return result;
-}
-
-
+    SetOverflowFrames(PresContext(), frames);
+  }
+}
 
 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
 // reflow so that it has no effect on the calculations of reflow.
 void
 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
                                           nsMargin             aBorderPadding)
 {
   nscoord yTotalOffset = 0; // total offset among all rows in all row groups
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -633,17 +633,16 @@ protected:
 
   void PlaceChild(nsTableReflowState&  aReflowState,
                   nsIFrame*            aKidFrame,
                   nsHTMLReflowMetrics& aKidDesiredSize,
                   const nsRect&        aOriginalKidRect,
                   const nsRect&        aOriginalKidOverflowRect);
 
   nsIFrame* GetFirstBodyRowGroupFrame();
-  PRBool MoveOverflowToChildList(nsPresContext* aPresContext);
   /**
    * Push all our child frames from the aFrames array, in order, starting from the
    * frame at aPushFrom to the end of the array. The frames are put on our overflow
    * list or moved directly to our next-in-flow if one exists.
    */
   typedef nsAutoTPtrArray<nsIFrame, 8> FrameArray;
   void PushChildren(const FrameArray& aFrames, PRInt32 aPushFrom);
 
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1025,29 +1025,32 @@ nsTableRowGroupFrame::SplitSpanningCells
 void
 nsTableRowGroupFrame::UndoContinuedRow(nsPresContext*   aPresContext,
                                        nsTableRowFrame* aRow)
 {
   if (!aRow) return; // allow null aRow to avoid callers doing null checks
 
   // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
   nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
+  NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
+                  "rowBefore not in our frame list?");
 
-  nsIFrame* firstOverflow = GetOverflowFrames(aPresContext, PR_TRUE); 
-  if (!rowBefore || !firstOverflow || (firstOverflow != aRow)) {
-    NS_ASSERTION(PR_FALSE, "invalid continued row");
+  nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
+  if (!rowBefore || !overflows || overflows->IsEmpty() ||
+      overflows->FirstChild() != aRow) {
+    NS_ERROR("invalid continued row");
     return;
   }
 
-  // Remove aRow from the sibling chain and hook its next-sibling up with rowBefore
-  rowBefore->SetNextSibling(aRow->GetNextSibling());
+  // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
+  // will not have reflowed yet to pick up content from any overflow lines.
+  overflows->DestroyFrame(aRow);
 
-  // Destroy the row, its cells, and their cell blocks. Cell blocks that have split
-  // will not have reflowed yet to pick up content from any overflow lines.
-  aRow->Destroy();
+  // Put the overflow rows into our child list
+  mFrames.InsertFrames(nsnull, rowBefore, *overflows);
 }
 
 static nsTableRowFrame* 
 GetRowBefore(nsTableRowFrame& aStartRow,
              nsTableRowFrame& aRow)
 {
   nsTableRowFrame* rowBefore = nsnull;
   for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {