Bug 475968. Eliminate NS_FRAME_OUTSIDE_CHILDREN flag, store small overflow areas cheaply within the frame. r+sr=roc
authorJonathan Kew <jfkthame@gmail.com>
Mon, 06 Apr 2009 12:31:50 +1200
changeset 26950 9ff3a933d89fcfbdf56868c16a82ede7b57e5150
parent 26949 e6812f79d2ba827f76610965a93bfa649dc03cc7
child 26951 8f7b87bf2c1e5972c83bd2b348654913cff87c5e
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs475968
milestone1.9.2a1pre
Bug 475968. Eliminate NS_FRAME_OUTSIDE_CHILDREN flag, store small overflow areas cheaply within the frame. r+sr=roc
layout/doc/frame_reflow_debug.html
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockReflowContext.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsHTMLReflowMetrics.h
layout/generic/nsIFrame.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsLineLayout.cpp
layout/generic/nsLineLayout.h
layout/generic/nsPageContentFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableRowFrame.cpp
layout/xul/base/src/nsBox.cpp
layout/xul/base/src/nsLeafBoxFrame.cpp
layout/xul/base/src/nsPopupSetFrame.cpp
layout/xul/base/src/nsScrollbarFrame.h
--- a/layout/doc/frame_reflow_debug.html
+++ b/layout/doc/frame_reflow_debug.html
@@ -143,18 +143,18 @@ The table cell requires its children to 
 <p><code class="log">block 02D7BCF8 d=300,300 me=300</code></p>
 <p>The  block max. element size is 300 twips.
 <p> The second table reflow is started at 
 <p><code class="log">rowG 00B984A4 r=2 a=1500,UC c=1500,UC cnt=879</code></p>
 <p>where the previous information is used. 
 The block has been required to compute the max. element size only once and it reports now:
 <p> <code class="log">block 02D7BCF8 d=1410,300</code></p>
 <p>The block shows the same address as the previous one. 
-<p>Frames with children that overflow  the parent have the <code>NS_FRAME_OUTSIDE_CHILDREN</code> flag set. For these frames 
-the overflowarea  is displayed as  <code class="log">block 025ED8F0 d=8940,1020 o=(0,0) 9180 x 1020</code>. The overflow area is specified as (x,y) origin and width x height.
+<p>Frames with children that overflow  the parent will return <code>PR_TRUE</code> from <code>HasOverflowRect()</code>. For these frames 
+the overflow area  is displayed as  <code class="log">block 025ED8F0 d=8940,1020 o=(0,0) 9180 x 1020</code>. The overflow area is specified as (x,y) origin and width x height.
 <p> The reflow finishes at the same level where it started.
 
 <h2>Advanced reflow debugging</h2>
 <p>The previously described technique dumps the data for every frame. Sometimes the log is clearer if only the main frames are shown.
 The entries in the reflow log can be controlled on a frame level.  For instance adding <code>text 0</code> to the rules in <code>reflow_rules.txt</code> would hide the text entries from the reflow. The display of the following frames can be turned on by adding a line with the frame name and  <code>1</code>  or turned off by adding a line with the frame name and <code>0</code>:</p>
 <table cellspacing="5">
  <tr><th style="text-align:left">short name</th><th style="text-align:left">layout tag</th></tr>
  <tr><td>area</td><td>area</td></tr>
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -363,17 +363,17 @@ nsBlockFrame::List(FILE* out, PRInt32 aI
   }
 
   // Output the rect and state
   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
   if (0 != mState) {
     fprintf(out, " [state=%08x]", mState);
   }
   nsBlockFrame* f = const_cast<nsBlockFrame*>(this);
-  if (f->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+  if (f->HasOverflowRect()) {
     nsRect overflowArea = f->GetOverflowRect();
     fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
   }
   PRInt32 numInlineLines = 0;
   PRInt32 numBlockLines = 0;
   if (!mLines.empty()) {
     const_line_iterator line = begin_lines(), line_end = end_lines();
@@ -1181,17 +1181,17 @@ nsBlockFrame::Reflow(nsPresContext*     
 #ifdef DEBUG
   if (gNoisyReflow) {
     IndentBy(stdout, gNoiseIndent);
     ListTag(stdout);
     printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
            aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
            aMetrics.width, aMetrics.height,
            aMetrics.mCarriedOutBottomMargin.get());
-    if (mState & NS_FRAME_OUTSIDE_CHILDREN) {
+    if (HasOverflowRect()) {
       printf(" combinedArea={%d,%d,%d,%d}",
              aMetrics.mOverflowArea.x,
              aMetrics.mOverflowArea.y,
              aMetrics.mOverflowArea.width,
              aMetrics.mOverflowArea.height);
     }
     printf("\n");
   }
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -321,17 +321,17 @@ nsBlockReflowContext::ReflowBlock(const 
         (mMetrics.height == nscoord(0xdeadbeef))) {
       printf("nsBlockReflowContext: ");
       nsFrame::ListTag(stdout, mFrame);
       printf(" didn't set w/h %d,%d!\n", mMetrics.width, mMetrics.height);
     }
   }
 #endif
 
-  if (!(NS_FRAME_OUTSIDE_CHILDREN & mFrame->GetStateBits())) {
+  if (!mFrame->HasOverflowRect()) {
     // Provide overflow area for child that doesn't have any
     mMetrics.mOverflowArea.x = 0;
     mMetrics.mOverflowArea.y = 0;
     mMetrics.mOverflowArea.width = mMetrics.width;
     mMetrics.mOverflowArea.height = mMetrics.height;
   }
 
   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) ||
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -559,18 +559,18 @@ nsContainerFrame::SyncFrameViewAfterRefl
   }
 
   if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
     nsIViewManager* vm = aView->GetViewManager();
 
     vm->ResizeView(aView, *aCombinedArea, PR_TRUE);
 
     // Even if the size hasn't changed, we need to sync up the
-    // geometry dependent properties, because (kidState &
-    // NS_FRAME_OUTSIDE_CHILDREN) might have changed, and we can't
+    // geometry dependent properties, because overflow areas of
+    // children might have changed, and we can't
     // detect whether it has or not. Likewise, whether the view size
     // has changed or not, we may need to change the transparency
     // state even if there is no clip.
     nsStyleContext* savedStyleContext = aFrame->GetStyleContext();
     SyncFrameViewGeometryDependentProperties(aPresContext, aFrame, savedStyleContext, aView, aFlags);
   }
 }
 
@@ -1603,17 +1603,17 @@ nsContainerFrame::List(FILE* out, PRInt3
     fprintf(out, " next-continuation=%p", static_cast<void*>(GetNextContinuation()));
   }
   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
   if (0 != mState) {
     fprintf(out, " [state=%08x]", mState);
   }
   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
   nsContainerFrame* f = const_cast<nsContainerFrame*>(this);
-  if (f->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+  if (f->HasOverflowRect()) {
     nsRect overflowArea = f->GetOverflowRect();
     fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
   }
   fprintf(out, " [sc=%p]", static_cast<void*>(mStyleContext));
   nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
   if (pseudoTag) {
     nsAutoString atomString;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3970,21 +3970,32 @@ nsRect
 nsIFrame::GetOverflowRect() const
 {
   // Note that in some cases the overflow area might not have been
   // updated (yet) to reflect any outline set on the frame or the area
   // of child frames. That's OK because any reflow that updates these
   // areas will invalidate the appropriate area, so any (mis)uses of
   // this method will be fixed up.
 
-  if (GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN)
+  if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+    // there is an overflow rect, and it's not stored as deltas but as
+    // a separately-allocated rect
     return *const_cast<nsIFrame*>(this)->GetOverflowAreaProperty(PR_FALSE);
-  // NOTE this won't return accurate info if the overflow rect was updated
-  // but the mRect hasn't been set yet!
-  return nsRect(nsPoint(0, 0), GetSize());
+  }
+
+  // Calculate the rect using deltas from the frame's border rect.
+  // Note that the mOverflow.mDeltas fields are unsigned, but we will often
+  // need to return negative values for the left and top, so take care
+  // to cast away the unsigned-ness.
+  return nsRect(-(PRInt32)mOverflow.mDeltas.mLeft,
+                -(PRInt32)mOverflow.mDeltas.mTop,
+                mRect.width + mOverflow.mDeltas.mRight +
+                              mOverflow.mDeltas.mLeft,
+                mRect.height + mOverflow.mDeltas.mBottom +
+                               mOverflow.mDeltas.mTop);
 }
 
 nsRect
 nsIFrame::GetOverflowRectRelativeToParent() const
 {
   return GetOverflowRect() + mRect.TopLeft();
 }
   
@@ -4063,17 +4074,17 @@ nsIFrame::CheckInvalidateSizeChange(cons
 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4)
 
 PRBool
 nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState,
                             nsHTMLReflowMetrics& aMetrics)
 {
   if (aReflowState.mReflowDepth >  MAX_FRAME_DEPTH) {
     mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
-    mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
+    ClearOverflowRect();
     aMetrics.width = 0;
     aMetrics.height = 0;
     aMetrics.ascent = 0;
     aMetrics.mCarriedOutBottomMargin.Zero();
     aMetrics.mOverflowArea.x = 0;
     aMetrics.mOverflowArea.y = 0;
     aMetrics.mOverflowArea.width = 0;
     aMetrics.mOverflowArea.height = 0;
@@ -4143,17 +4154,17 @@ nsFrame::List(FILE* out, PRInt32 aIndent
   if (nsnull != prevInFlow) {
     fprintf(out, " prev-in-flow=%p", static_cast<void*>(prevInFlow));
   }
   if (nsnull != nextInFlow) {
     fprintf(out, " next-in-flow=%p", static_cast<void*>(nextInFlow));
   }
   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
   nsFrame* f = const_cast<nsFrame*>(this);
-  if (f->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+  if (f->HasOverflowRect()) {
     nsRect overflowArea = f->GetOverflowRect();
     fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
   }
   fputs("\n", out);
   return NS_OK;
 }
 
@@ -5564,17 +5575,18 @@ nsFrame::GetAccessible(nsIAccessible** a
 /** Create or retrieve the previously stored overflow area, if the frame does 
  * not overflow and no creation is required return nsnull.
  * @param aCreateIfNecessary  create a new nsRect for the overflow area
  * @return pointer to the overflow area rectangle 
  */
 nsRect*
 nsIFrame::GetOverflowAreaProperty(PRBool aCreateIfNecessary) 
 {
-  if (!((GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) || aCreateIfNecessary)) {
+  if (!((mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) ||
+        aCreateIfNecessary)) {
     return nsnull;
   }
 
   nsPropertyTable *propTable = PresContext()->PropertyTable();
   void *value = propTable->GetProperty(this,
                                        nsGkAtoms::overflowAreaProperty);
 
   if (value) {
@@ -5583,20 +5595,54 @@ nsIFrame::GetOverflowAreaProperty(PRBool
     // The property isn't set yet, so allocate a new rect, set the property,
     // and return the newly allocated rect
     nsRect*  overflow = new nsRect(0, 0, 0, 0);
     propTable->SetProperty(this, nsGkAtoms::overflowAreaProperty,
                            overflow, DestroyRectFunc, nsnull);
     return overflow;
   }
 
-  NS_NOTREACHED("Frame abuses NS_FRAME_OUTSIDE_CHILDREN flag");
+  NS_NOTREACHED("Frame abuses GetOverflowAreaProperty()");
   return nsnull;
 }
 
+/** Set the overflowArea rect, storing it as deltas or a separate rect
+ * depending on its size in relation to the primary frame rect.
+ */
+void
+nsIFrame::SetOverflowRect(const nsRect& aRect)
+{
+  PRUint32 l = -aRect.x, // left edge: positive delta is leftwards
+           t = -aRect.y, // top: positive is upwards
+           r = aRect.XMost() - mRect.width, // right: positive is rightwards
+           b = aRect.YMost() - mRect.height; // bottom: positive is downwards
+  if (l <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+      t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+      r <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+      b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+      (l | t | r | b) != 0) {
+    // It's a "small" overflow area so we store the deltas for each edge
+    // directly in the frame, rather than allocating a separate rect.
+    // Note that we do NOT store in this way if *all* the deltas are zero,
+    // as that would be indistinguishable from the complete absence of
+    // an overflow rect.
+    DeleteProperty(nsGkAtoms::overflowAreaProperty);
+    mOverflow.mDeltas.mLeft   = l;
+    mOverflow.mDeltas.mTop    = t;
+    mOverflow.mDeltas.mRight  = r;
+    mOverflow.mDeltas.mBottom = b;
+  } else {
+    // it's a large overflow area that we need to store as a property
+    mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
+    nsRect* overflowArea = GetOverflowAreaProperty(PR_TRUE); 
+    NS_ASSERTION(overflowArea, "should have created rect");
+    *overflowArea = aRect;
+  }
+}
+
 inline PRBool
 IsInlineFrame(nsIFrame *aFrame)
 {
   nsIAtom *type = aFrame->GetType();
   return type == nsGkAtoms::inlineFrame ||
          type == nsGkAtoms::positionedInlineFrame;
 }
 
@@ -5677,28 +5723,24 @@ nsIFrame::FinishAndStoreOverflow(nsRect*
      * ensured us we'll use.
      */
     nsRect newBounds(nsPoint(0, 0), aNewSize);
     *aOverflowArea = nsDisplayTransform::TransformRect(*aOverflowArea, this, nsPoint(0, 0), &newBounds);
   }
 
   PRBool overflowChanged;
   if (*aOverflowArea != nsRect(nsPoint(0, 0), aNewSize)) {
-    mState |= NS_FRAME_OUTSIDE_CHILDREN;
-    nsRect* overflowArea = GetOverflowAreaProperty(PR_TRUE); 
-    NS_ASSERTION(overflowArea, "should have created rect");
-    overflowChanged = *overflowArea != *aOverflowArea;
-    *overflowArea = *aOverflowArea;
+    overflowChanged = *aOverflowArea != GetOverflowRect();
+    SetOverflowRect(*aOverflowArea);
   }
   else {
-    if (mState & NS_FRAME_OUTSIDE_CHILDREN) {
+    if (HasOverflowRect()) {
       // remove the previously stored overflow area 
-      DeleteProperty(nsGkAtoms::overflowAreaProperty);
+      ClearOverflowRect();
       overflowChanged = PR_TRUE;
-      mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
     } else {
       overflowChanged = PR_FALSE;
     }
   }
 
   if (overflowChanged && hasOutlineOrEffects) {
     // When there's an outline or box-shadow or SVG effects, changes to
     // those styles might require repainting of the old and new overflow
@@ -6619,17 +6661,17 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
     Reflow(aPresContext, aDesiredSize, reflowState, status);
 
     NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
 
    // printf("width: %d, height: %d\n", aDesiredSize.mCombinedArea.width, aDesiredSize.mCombinedArea.height);
 
     // see if the overflow option is set. If it is then if our child's bounds overflow then
     // we will set the child's rect to include the overflow size.
-    if (GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+    if (HasOverflowRect()) {
       // This kinda sucks. We should be able to handle the case
       // where there's overflow above or to the left of the
       // origin. But for now just chop that stuff off.
       // (note: For RTL mode, replace "to the left of the origin" 
       // with "to the right of the range [0, aDesiredSize.width]")
 
       //printf("OutsideChildren width=%d, height=%d\n", aDesiredSize.mOverflowArea.width, aDesiredSize.mOverflowArea.height);
 
@@ -6656,17 +6698,17 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
         #ifdef DEBUG_REFLOW
         nsAdaptorAddIndents();
         nsAdaptorPrintReason(reflowState);
         printf("\n");
         #endif
         AddStateBits(NS_FRAME_IS_DIRTY);
         WillReflow(aPresContext);
         Reflow(aPresContext, aDesiredSize, reflowState, status);
-        if (GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN)
+        if (HasOverflowRect())
           aDesiredSize.height = aDesiredSize.mOverflowArea.YMost();
       }
     }
 
     if (redrawAfterReflow) {
        nsRect r = GetRect();
        r.width = aDesiredSize.width;
        r.height = aDesiredSize.height;
@@ -7689,23 +7731,23 @@ void nsFrame::DisplayReflowExit(nsPresCo
     char y[16];
     DR_state->PrettyUC(aMetrics.width, width);
     DR_state->PrettyUC(aMetrics.height, height);
     printf("Reflow d=%s,%s", width, height);
 
     if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
       printf(" status=0x%x", aStatus);
     }
-    if (aFrame->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+    if (aFrame->HasOverflowRect()) {
       DR_state->PrettyUC(aMetrics.mOverflowArea.x, x);
       DR_state->PrettyUC(aMetrics.mOverflowArea.y, y);
       DR_state->PrettyUC(aMetrics.mOverflowArea.width, width);
       DR_state->PrettyUC(aMetrics.mOverflowArea.height, height);
       printf(" o=(%s,%s) %s x %s", x, y, width, height);
-      if (aFrame->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+      if (aFrame->HasOverflowRect()) {
         nsRect storedOverflow = aFrame->GetOverflowRect();
         DR_state->PrettyUC(storedOverflow.x, x);
         DR_state->PrettyUC(storedOverflow.y, y);
         DR_state->PrettyUC(storedOverflow.width, width);
         DR_state->PrettyUC(storedOverflow.height, height);
         printf(" sto=(%s,%s) %s x %s", x, y, width, height);
       }
     }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -535,17 +535,17 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S
                     &kidReflowState, *aMetrics, 0, 0,
                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_SIZE_VIEW);
 
   // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
   // setting their mOverflowArea. This is wrong because every frame should
   // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
   // support the 'outline' property because of this. Rather than fix the world
   // right now, just fix up the overflow area if necessary. Note that we don't
-  // check NS_FRAME_OUTSIDE_CHILDREN because it could be set even though the
+  // check HasOverflowRect() because it could be set even though the
   // overflow area doesn't include the frame bounds.
   aMetrics->mOverflowArea.UnionRect(aMetrics->mOverflowArea,
                                     nsRect(0, 0, aMetrics->width, aMetrics->height));
 
   aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
   aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
   
   return rv;
@@ -707,22 +707,22 @@ nsHTMLScrollFrame::PlaceScrollArea(const
   nsRect scrolledArea;
   // Preserve the width or height of empty rects
   scrolledArea.UnionRectIncludeEmpty(mInner.GetScrolledRect(aState.mScrollPortRect.Size()),
                                      nsRect(nsPoint(0,0), aState.mScrollPortRect.Size()));
 
   // Store the new overflow area. Note that this changes where an outline
   // of the scrolled frame would be painted, but scrolled frames can't have
   // outlines (the outline would go on this scrollframe instead).
-  // Using FinishAndStoreOverflow is needed so NS_FRAME_OUTSIDE_CHILDREN
+  // Using FinishAndStoreOverflow is needed so the overflow rect
   // gets set correctly.  It also messes with the overflow rect in the
   // -moz-hidden-unscrollable case, but scrolled frames can't have
   // 'overflow' either.
   // This needs to happen before SyncFrameViewAfterReflow so
-  // NS_FRAME_OUTSIDE_CHILDREN is set.
+  // HasOverflowRect() will return the correct value.
   scrolledFrame->FinishAndStoreOverflow(&scrolledArea,
                                         scrolledFrame->GetSize());
 
   // Note that making the view *exactly* the size of the scrolled area
   // is critical, since the view scrolling code uses the size of the
   // scrolled view to clamp scroll requests.
   nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame->PresContext(),
                                              scrolledFrame,
@@ -2078,19 +2078,17 @@ nsXULScrollFrame::LayoutScrollArea(nsBox
   if (childRect.width < aRect.width || childRect.height < aRect.height)
   {
     childRect.width = PR_MAX(childRect.width, aRect.width);
     childRect.height = PR_MAX(childRect.height, aRect.height);
 
     // remove overflow area when we update the bounds,
     // because we've already accounted for it
     mInner.mScrolledFrame->SetBounds(aState, childRect);
-    PresContext()->PropertyTable()->
-        DeleteProperty(mInner.mScrolledFrame, nsGkAtoms::overflowAreaProperty);
-    mInner.mScrolledFrame->RemoveStateBits(NS_FRAME_OUTSIDE_CHILDREN);
+    mInner.mScrolledFrame->ClearOverflowRect();
   }
 
   aState.SetLayoutFlags(oldflags);
 
 }
 
 void nsGfxScrollFrameInner::PostOverflowEvent()
 {
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -154,17 +154,17 @@ struct nsHTMLReflowMetrics {
   nsBoundingMetrics mBoundingMetrics;  // [OUT]
 #endif
 
   // Carried out bottom margin values. This is the collapsed
   // (generational) bottom margin value.
   nsCollapsingMargin mCarriedOutBottomMargin;
   
   // For frames that have content that overflow their content area
-  // (NS_FRAME_OUTSIDE_CHILDREN) this rectangle represents the total area
+  // (HasOverflowRect() is true) this rectangle represents the total area
   // of the frame including visible overflow, i.e., don't include overflowing
   // content that is hidden.
   // The rect is in the local coordinate space of the frame, and should be at
   // least as big as the desired size. If there is no content that overflows,
   // then the overflow area is identical to the desired size and should be
   // {0, 0, mWidth, mHeight}.
   nsRect mOverflowArea;
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -157,19 +157,24 @@ enum {
   // cleared.
   NS_FRAME_FIRST_REFLOW =                       0x00000002,
 
   // For a continuation frame, if this bit is set, then this a "fluid" 
   // continuation, i.e., across a line boundary. Otherwise it's a "hard"
   // continuation, e.g. a bidi continuation.
   NS_FRAME_IS_FLUID_CONTINUATION =              0x00000004,
 
-  // This bit is set when the frame's overflow rect is
-  // different from its border rect (i.e. GetOverflowRect() != GetRect())
-  NS_FRAME_OUTSIDE_CHILDREN =                   0x00000008,
+/*
+ * This bit is obsolete, replaced by HasOverflowRect().
+ * The definition is left here as a placeholder for now, to remind us
+ * that this bit is now free to allocate for other purposes.
+ * // This bit is set when the frame's overflow rect is
+ * // different from its border rect (i.e. GetOverflowRect() != GetRect())
+ * NS_FRAME_OUTSIDE_CHILDREN =                   0x00000008,
+ */
 
   // If this bit is set, then a reference to the frame is being held
   // elsewhere.  The frame may want to send a notification when it is
   // destroyed to allow these references to be cleared.
   NS_FRAME_EXTERNAL_REFERENCE =                 0x00000010,
 
   // If this bit is set, this frame or one of its descendants has a
   // percentage height that depends on an ancestor of this frame.
@@ -436,16 +441,34 @@ void NS_MergeReflowStatusInto(nsReflowSt
 /**
  * DidReflow status values.
  */
 typedef PRBool nsDidReflowStatus;
 
 #define NS_FRAME_REFLOW_NOT_FINISHED PR_FALSE
 #define NS_FRAME_REFLOW_FINISHED     PR_TRUE
 
+/**
+ * The overflow rect may be stored as four 1-byte deltas each strictly
+ * LESS THAN 0xff, for the four edges of the rectangle, or the four bytes
+ * may be read as a single 32-bit "overflow-rect type" value including
+ * at least one 0xff byte as an indicator that the value does NOT
+ * represent four deltas.
+ * If all four deltas are zero, this means that no overflow rect has
+ * actually been set (this is the initial state of newly-created frames).
+ */
+#define NS_FRAME_OVERFLOW_DELTA_MAX     0xfe // max delta we can store
+
+#define NS_FRAME_OVERFLOW_NONE    0x00000000 // there is no overflow rect;
+                                             // code relies on this being
+                                             // the all-zero value
+
+#define NS_FRAME_OVERFLOW_LARGE   0x000000ff // overflow is stored as a
+                                             // separate rect property
+
 //----------------------------------------------------------------------
 
 /**
  * A frame in the layout model. This interface is supported by all frame
  * objects.
  *
  * Frames can have multiple child lists: the default unnamed child list
  * (referred to as the <i>principal</i> child list, and additional named
@@ -705,19 +728,42 @@ public:
    * content area, borders, and padding.
    *
    * Note: moving or sizing the frame does not affect the view's size or
    * position.
    */
   nsRect GetRect() const { return mRect; }
   nsPoint GetPosition() const { return nsPoint(mRect.x, mRect.y); }
   nsSize GetSize() const { return nsSize(mRect.width, mRect.height); }
-  void SetRect(const nsRect& aRect) { mRect = aRect; }
+
+  /**
+   * When we change the size of the frame's border-box rect, we may need to
+   * reset the overflow rect if it was previously stored as deltas.
+   * (If it is currently a "large" overflow and could be re-packed as deltas,
+   * we don't bother as the cost of the allocation has already been paid.)
+   */
+  void SetRect(const nsRect& aRect) {
+    if (HasOverflowRect() && mOverflow.mType != NS_FRAME_OVERFLOW_LARGE) {
+      nsRect r = GetOverflowRect();
+      mRect = aRect;
+      SetOverflowRect(r);
+    } else {
+      mRect = aRect;
+    }
+  }
+  void SetSize(const nsSize& aSize) {
+    if (HasOverflowRect() && mOverflow.mType != NS_FRAME_OVERFLOW_LARGE) {
+      nsRect r = GetOverflowRect();
+      mRect.SizeTo(aSize);
+      SetOverflowRect(r);
+    } else {
+      mRect.SizeTo(aSize);
+    }
+  }
   void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); }
-  void SetSize(const nsSize& aSize) { mRect.SizeTo(aSize); }
 
   /**
    * Return frame's computed offset due to relative positioning
    */
   nsPoint GetRelativeOffset(const nsStyleDisplay* aDisplay = nsnull) const;
 
   virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
   { return aChild->GetPosition(); }
@@ -1751,18 +1797,18 @@ public:
   void InvalidateOverflowRect();
   
   /**
    * Computes a rect that encompasses everything that might be painted by
    * this frame.  This includes this frame, all its descendent frames, this
    * frame's outline, and descentant frames' outline, but does not include
    * areas clipped out by the CSS "overflow" and "clip" properties.
    *
-   * The NS_FRAME_OUTSIDE_CHILDREN state bit is set when this overflow rect
-   * is different from nsRect(0, 0, GetRect().width, GetRect().height).
+   * HasOverflowRect() (below) will return PR_TRUE when this overflow rect
+   * has been explicitly set, even if it matches mRect.
    * XXX Note: because of a space optimization using the formula above,
    * during reflow this function does not give accurate data if
    * FinishAndStoreOverflow has been called but mRect hasn't yet been
    * updated yet.
    *
    * @return the rect relative to this frame's origin, but after
    * CSS transforms have been applied (i.e. not really this frame's coordinate
    * system, and may not contain the frame's border-box, e.g. if there
@@ -1771,17 +1817,17 @@ public:
   nsRect GetOverflowRect() const;
 
   /**
    * Computes a rect that encompasses everything that might be painted by
    * this frame.  This includes this frame, all its descendent frames, this
    * frame's outline, and descentant frames' outline, but does not include
    * areas clipped out by the CSS "overflow" and "clip" properties.
    *
-   * The NS_FRAME_OUTSIDE_CHILDREN state bit is set when this overflow rect
+   * HasOverflowRect() (below) will return PR_TRUE when this overflow rect
    * is different from nsRect(0, 0, GetRect().width, GetRect().height).
    * XXX Note: because of a space optimization using the formula above,
    * during reflow this function does not give accurate data if
    * FinishAndStoreOverflow has been called but mRect hasn't yet been
    * updated yet.
    *
    * @return the rect relative to the parent frame, in the parent frame's
    * coordinate system
@@ -1795,27 +1841,43 @@ public:
    * areas clipped out by the CSS "overflow" and "clip" properties.
    *
    * @return the rect relative to this frame, before any CSS transforms have
    * been applied, i.e. in this frame's coordinate system
    */
   nsRect GetOverflowRectRelativeToSelf() const;
 
   /**
-   * Set/unset the NS_FRAME_OUTSIDE_CHILDREN flag and store the overflow area
+   * Store the overflow area in the frame's mOverflow.mDeltas fields or
    * as a frame property in the frame manager so that it can be retrieved
    * later without reflowing the frame.
    */
   void FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize);
 
   void FinishAndStoreOverflow(nsHTMLReflowMetrics* aMetrics) {
     FinishAndStoreOverflow(&aMetrics->mOverflowArea, nsSize(aMetrics->width, aMetrics->height));
   }
 
   /**
+   * Returns whether the frame has an overflow rect that is different from
+   * its border-box.
+   */
+  PRBool HasOverflowRect() const {
+    return mOverflow.mType != NS_FRAME_OVERFLOW_NONE;
+  }
+
+  /**
+   * Removes any stored overflow rect from the frame.
+   */
+  void ClearOverflowRect() {
+    DeleteProperty(nsGkAtoms::overflowAreaProperty);
+    mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
+  }
+
+  /**
    * Determine whether borders should not be painted on certain sides of the
    * frame.
    */
   virtual PRIntn GetSkipSides() const { return 0; }
 
   /** Selection related calls
    */
   /** 
@@ -2250,16 +2312,35 @@ NS_PTR_TO_INT32(frame->GetProperty(nsGkA
 protected:
   // Members
   nsRect           mRect;
   nsIContent*      mContent;
   nsStyleContext*  mStyleContext;
   nsIFrame*        mParent;
   nsIFrame*        mNextSibling;  // singly-linked list of frames
   nsFrameState     mState;
+
+  // When there is an overflow area only slightly larger than mRect,
+  // we store a set of four 1-byte deltas from the edges of mRect
+  // rather than allocating a whole separate rectangle property.
+  // Note that these are unsigned values, all measured "outwards"
+  // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from
+  // our normal coordinate system.
+  // If mOverflow.mType == NS_FRAME_OVERFLOW_LARGE, then the
+  // delta values are not meaningful and the overflow area is stored
+  // as a separate rect property.
+  union {
+    PRUint32  mType;
+    struct {
+      PRUint8 mLeft;
+      PRUint8 mTop;
+      PRUint8 mRight;
+      PRUint8 mBottom;
+    } mDeltas;
+  } mOverflow;
   
   // Helpers
   /**
    * For frames that have top-level windows (top-level viewports,
    * comboboxes, menupoups) this function will invalidate the window.
    */
   void InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags);
 
@@ -2352,16 +2433,17 @@ protected:
    *              used by this method: 
    *              Input: mDirection
    *              Output: mResultContent, mContentOffset
    */
    nsresult PeekOffsetParagraph(nsPeekOffsetStruct *aPos);
 
 private:
   nsRect* GetOverflowAreaProperty(PRBool aCreateIfNecessary = PR_FALSE);
+  void SetOverflowRect(const nsRect& aRect);
 };
 
 //----------------------------------------------------------------------
 
 /**
  * nsWeakFrame can be used to keep a reference to a nsIFrame in a safe way.
  * Whenever an nsIFrame object is deleted, the nsWeakFrames pointing
  * to it will be cleared.
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -379,17 +379,17 @@ nsInlineFrame::Reflow(nsPresContext*    
     // whether we have an anonymous block or not.
     PRBool complete;
     (void) PullOneFrame(aPresContext, irs, &complete);
   }
 
   rv = ReflowFrames(aPresContext, aReflowState, irs, aMetrics, aStatus);
   
   // Note: the line layout code will properly compute our
-  // NS_FRAME_OUTSIDE_CHILDREN state for us.
+  // overflow-rect state for us.
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
   return rv;
 }
 
 /* virtual */ PRBool
 nsInlineFrame::CanContinueTextRun() const
 {
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -2630,28 +2630,28 @@ nsLineLayout::RelativePositionFrames(Per
       // positioned.
       // All descendant views must be repositioned even if this frame
       // does have a view in case this frame's view does not have a
       // widget and some of the descendant views do have widgets --
       // otherwise the widgets won't be repositioned.
       nsContainerFrame::PositionChildViews(frame);
     }
 
-    // Do this here (rather than along with NS_FRAME_OUTSIDE_CHILDREN
-    // handling below) so we get leaf frames as well.  No need to worry
+    // Do this here (rather than along with setting the overflow rect
+    // below) so we get leaf frames as well.  No need to worry
     // about the root span, since it doesn't have a frame.
     if (frame->HasView())
       nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
                                                  frame->GetView(), &r,
                                                  NS_FRAME_NO_MOVE_VIEW);
 
     combinedAreaResult.UnionRect(combinedAreaResult, r + origin);
   }
 
   // If we just computed a spans combined area, we need to update its
-  // NS_FRAME_OUTSIDE_CHILDREN bit..
+  // overflow rect...
   if (psd->mFrame) {
     PerFrameData* spanPFD = psd->mFrame;
     nsIFrame* frame = spanPFD->mFrame;
     frame->FinishAndStoreOverflow(&combinedAreaResult, frame->GetSize());
   }
   aCombinedArea = combinedAreaResult;
 }
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -135,17 +135,17 @@ public:
 
   PRBool TrimTrailingWhiteSpace();
 
   void HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify);
 
   /**
    * Handle all the relative positioning in the line, compute the
    * combined area (== overflow area) for the line, and handle view
-   * sizing/positioning and the setting of NS_FRAME_OUTSIDE_CHILDREN.
+   * sizing/positioning and the setting of the overflow rect.
    */
   void RelativePositionFrames(nsRect& aCombinedArea);
 
   //----------------------------------------
 
   // Supporting methods and data for flags
 protected:
 #define LL_FIRSTLETTERSTYLEOK          0x00000008
--- a/layout/generic/nsPageContentFrame.cpp
+++ b/layout/generic/nsPageContentFrame.cpp
@@ -106,17 +106,17 @@ nsPageContentFrame::Reflow(nsPresContext
     // absolutely positioned elements
     nsMargin padding(0,0,0,0);
 
     // XXXbz this screws up percentage padding (sets padding to zero
     // in the percentage padding case)
     kidReflowState.mStylePadding->GetPadding(padding);
 
     // First check the combined area
-    if (NS_FRAME_OUTSIDE_CHILDREN & frame->GetStateBits()) {
+    if (frame->HasOverflowRect()) {
       // The background covers the content area and padding area, so check
       // for children sticking outside the child frame's padding edge
       if (aDesiredSize.mOverflowArea.XMost() > aDesiredSize.width) {
         mPD->mPageContentXMost =
           aDesiredSize.mOverflowArea.XMost() +
           kidReflowState.mStyleBorder->GetActualBorderWidth(NS_SIDE_RIGHT) +
           padding.right;
       }
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -6620,17 +6620,17 @@ nsTextFrame::List(FILE* out, PRInt32 aIn
   if (0 != mState) {
     if (mState & NS_FRAME_SELECTED_CONTENT) {
       fprintf(out, " [state=%08x] SELECTED", mState);
     } else {
       fprintf(out, " [state=%08x]", mState);
     }
   }
   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
-  if (GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+  if (HasOverflowRect()) {
     nsRect overflowArea = GetOverflowRect();
     fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
   }
   fprintf(out, " sc=%p", static_cast<void*>(mStyleContext));
   nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
   if (pseudoTag) {
     nsAutoString atomString;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -6418,16 +6418,18 @@ nsTableFrame::PaintBCBorders(nsIRenderin
         }
         else {
           startRowY += rowSize.height;
         }
       }
       rowY += rowSize.height; 
     }
   }
+  // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
+  // XXX but I don't understand it, so not changing it for now
   // outer table borders overflow the table, so the table might be
   // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
   // on the table
   if (!haveIntersect)
     return;  
   if (!inFlowRG || !inFlowRow) ABORT0();
 
   PRInt32 startColX;
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -918,17 +918,17 @@ nsTableRowFrame::ReflowChildren(nsPresCo
       }
       else {
         if (x != kidRect.x) {
           kidFrame->InvalidateOverflowRect();
         }
         
         desiredSize.width = cellDesiredSize.width;
         desiredSize.height = cellDesiredSize.height;
-        if (cellFrame->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN)
+        if (cellFrame->HasOverflowRect())
           desiredSize.mOverflowArea = cellFrame->GetOverflowRect();
         else
           desiredSize.mOverflowArea.SetRect(0, 0, cellDesiredSize.width,
                                             cellDesiredSize.height);
         
         // if we are in a floated table, our position is not yet established, so we cannot reposition our views
         // the containing block will do this for us after positioning the table
         if (!aTableFrame.GetStyleDisplay()->IsFloating()) {
--- a/layout/xul/base/src/nsBox.cpp
+++ b/layout/xul/base/src/nsBox.cpp
@@ -275,21 +275,19 @@ nsBox::SetBounds(nsBoxLayoutState& aStat
 
     if ((flags & NS_FRAME_NO_MOVE_FRAME) == NS_FRAME_NO_MOVE_FRAME)
       SetSize(nsSize(aRect.width, aRect.height));
     else
       SetRect(aRect);
 
     // Nuke the overflow area. The caller is responsible for restoring
     // it if necessary.
-    if (aRemoveOverflowArea && (GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN)) {
+    if (aRemoveOverflowArea && HasOverflowRect()) {
       // remove the previously stored overflow area
-      PresContext()->PropertyTable()->
-        DeleteProperty(this, nsGkAtoms::overflowAreaProperty);
-      RemoveStateBits(NS_FRAME_OUTSIDE_CHILDREN);
+      ClearOverflowRect();
     }
 
     if (!(flags & NS_FRAME_NO_MOVE_VIEW))
     {
       nsContainerFrame::PositionFrameView(this);
       if ((rect.x != aRect.x) || (rect.y != aRect.y))
         nsContainerFrame::PositionChildViews(this);
     }
--- a/layout/xul/base/src/nsLeafBoxFrame.cpp
+++ b/layout/xul/base/src/nsLeafBoxFrame.cpp
@@ -346,17 +346,17 @@ nsLeafBoxFrame::Reflow(nsPresContext*   
   // layout our children
   Layout(state);
   
   // ok our child could have gotten bigger. So lets get its bounds
   aDesiredSize.width  = mRect.width;
   aDesiredSize.height = mRect.height;
   aDesiredSize.ascent = GetBoxAscent(state);
 
-  // NS_FRAME_OUTSIDE_CHILDREN is set in SetBounds() above
+  // the overflow rect is set in SetBounds() above
   aDesiredSize.mOverflowArea = GetOverflowRect();
 
 #ifdef DO_NOISY_REFLOW
   {
     printf("%p ** nsLBF(done) W:%d H:%d  ", this, aDesiredSize.width, aDesiredSize.height);
 
     if (maxElementWidth) {
       printf("MW:%d\n", *maxElementWidth); 
--- a/layout/xul/base/src/nsPopupSetFrame.cpp
+++ b/layout/xul/base/src/nsPopupSetFrame.cpp
@@ -324,17 +324,17 @@ nsPopupSetFrame::List(FILE* out, PRInt32
     fprintf(out, " next-continuation=%p", static_cast<void*>(GetNextContinuation()));
   }
   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
   if (0 != mState) {
     fprintf(out, " [state=%08x]", mState);
   }
   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
   nsPopupSetFrame* f = const_cast<nsPopupSetFrame*>(this);
-  if (f->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
+  if (f->HasOverflowRect()) {
     nsRect overflowArea = f->GetOverflowRect();
     fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
   }
   fprintf(out, " [sc=%p]", static_cast<void*>(mStyleContext));
   nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
   if (pseudoTag) {
     nsAutoString atomString;
--- a/layout/xul/base/src/nsScrollbarFrame.h
+++ b/layout/xul/base/src/nsScrollbarFrame.h
@@ -100,17 +100,17 @@ public:
   // nsIScrollbarFrame
   virtual void SetScrollbarMediatorContent(nsIContent* aMediator);
   virtual nsIScrollbarMediator* GetScrollbarMediator();
 
   // nsBox methods
 
   /**
    * Treat scrollbars as clipping their children; overflowing children
-   * will not be allowed to make NS_FRAME_OUTSIDE_CHILDREN on this
+   * will not be allowed to set an overflow rect on this
    * frame. This means that when the scroll code decides to hide a
    * scrollframe by setting its height or width to zero, that will
    * hide the children too.
    */
   virtual PRBool DoesClipChildren() { return PR_TRUE; }
 
 private:
   nsCOMPtr<nsIContent> mScrollbarMediator;