Convert nsIFrame APIs from having a single overflow rect to having two distinct overflow rects. (Bug 542595, patch 1) r=roc sr=bzbarsky a2.0=blocking2.0:beta8
authorL. David Baron <dbaron@dbaron.org>
Wed, 06 Oct 2010 21:25:45 -0700
changeset 55021 4abdb488ea62cef7371416a63b1c42875fe2ac94
parent 55020 2b78824f6508dafdb366f6203b48ed72467ccb35
child 55022 28874ce55ee11d0b1fb190613787af46fa2f6d33
push idunknown
push userunknown
push dateunknown
reviewersroc, bzbarsky
bugs542595
milestone2.0b8pre
Convert nsIFrame APIs from having a single overflow rect to having two distinct overflow rects. (Bug 542595, patch 1) r=roc sr=bzbarsky a2.0=blocking2.0:beta8
layout/generic/Makefile.in
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsHTMLReflowMetrics.cpp
layout/generic/nsHTMLReflowMetrics.h
layout/generic/nsIFrame.h
--- a/layout/generic/Makefile.in
+++ b/layout/generic/Makefile.in
@@ -92,16 +92,17 @@ CPPSRCS		= \
 		nsFrame.cpp \
 		nsFrameList.cpp \
 		nsFrameSetFrame.cpp \
 		nsFrameUtil.cpp \
 		nsGfxScrollFrame.cpp \
 		nsHTMLCanvasFrame.cpp \
 		nsHTMLContainerFrame.cpp \
 		nsCanvasFrame.cpp \
+		nsHTMLReflowMetrics.cpp \
 		nsHTMLReflowState.cpp \
 		nsImageFrame.cpp \
 		nsImageMap.cpp \
 		nsInlineFrame.cpp \
 		nsIntervalSet.cpp \
 		nsLeafFrame.cpp \
 		nsLineBox.cpp \
 		nsLineLayout.cpp \
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -510,34 +510,32 @@ nsContainerFrame::SyncWindowProperties(n
   windowWidget->SetWindowShadowStyle(rootFrame->GetStyleUIReset()->mWindowShadow);
 #endif
 }
 
 void
 nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
                                            nsIFrame*       aFrame,
                                            nsIView*        aView,
-                                           const nsRect*   aCombinedArea,
+                                           const nsRect&   aVisualOverflowArea,
                                            PRUint32        aFlags)
 {
   if (!aView) {
     return;
   }
 
-  NS_ASSERTION(aCombinedArea, "Combined area must be passed in now");
-
   // Make sure the view is sized and positioned correctly
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aFrame);
   }
 
   if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
     nsIViewManager* vm = aView->GetViewManager();
 
-    vm->ResizeView(aView, *aCombinedArea, PR_TRUE);
+    vm->ResizeView(aView, aVisualOverflowArea, PR_TRUE);
   }
 }
 
 void
 nsContainerFrame::SyncFrameViewProperties(nsPresContext*  aPresContext,
                                           nsIFrame*        aFrame,
                                           nsStyleContext*  aStyleContext,
                                           nsIView*         aView,
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -123,17 +123,17 @@ public:
   //
   // Flags:
   // NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
   //    don't want to automatically sync the frame and view
   // NS_FRAME_NO_SIZE_VIEW - don't size the view
   static void SyncFrameViewAfterReflow(nsPresContext* aPresContext,
                                        nsIFrame*       aFrame,
                                        nsIView*        aView,
-                                       const nsRect*   aCombinedArea,
+                                       const nsRect&   aVisualOverflowArea,
                                        PRUint32        aFlags = 0);
 
   // Syncs properties to the top level view and window, like transparency and
   // shadow.
   static void SyncWindowProperties(nsPresContext*       aPresContext,
                                    nsIFrame*            aFrame,
                                    nsIView*             aView);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4349,58 +4349,71 @@ nsIFrame::GetRelativeOffset(const nsStyl
     if (offsets) {
       return *offsets;
     }
   }
   return nsPoint(0,0);
 }
 
 nsRect
-nsIFrame::GetOverflowRect() const
-{
+nsIFrame::GetOverflowRect(nsOverflowType aType) const
+{
+  NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow,
+                    "unexpected type");
+
   // 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 (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);
-  }
-
-  // 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);
+    return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)->
+             GetOverflowAreasProperty())->Overflow(aType);
+  }
+
+  if (aType == eVisualOverflow &&
+      mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
+    return GetVisualOverflowFromDeltas();
+  }
+
+  return nsRect(nsPoint(0, 0), GetSize());
+}
+
+nsOverflowAreas
+nsIFrame::GetOverflowAreas() const
+{
+  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)->GetOverflowAreasProperty();
+  }
+
+  return nsOverflowAreas(GetVisualOverflowFromDeltas(),
+                         nsRect(nsPoint(0, 0), GetSize()));
 }
 
 nsRect
-nsIFrame::GetOverflowRectRelativeToParent() const
-{
-  return GetOverflowRect() + mRect.TopLeft();
-}
-  
+nsIFrame::GetScrollableOverflowRectRelativeToParent() const
+{
+  return GetScrollableOverflowRect() + mRect.TopLeft();
+}
+
 nsRect
-nsIFrame::GetOverflowRectRelativeToSelf() const
+nsIFrame::GetVisualOverflowRectRelativeToSelf() const
 {
   if (IsTransformed()) {
     nsRect* preTransformBBox = static_cast<nsRect*>
       (Properties().Get(PreTransformBBoxProperty()));
     if (preTransformBBox)
       return *preTransformBBox;
   }
-  return GetOverflowRect();
+  return GetVisualOverflowRect();
 }
 
 void
 nsFrame::CheckInvalidateSizeChange(nsHTMLReflowMetrics& aNewDesiredSize)
 {
   nsIFrame::CheckInvalidateSizeChange(mRect, GetOverflowRect(),
       nsSize(aNewDesiredSize.width, aNewDesiredSize.height));
 }
@@ -4504,17 +4517,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;
-    ClearOverflowRect();
+    ClearOverflowRects();
     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;
@@ -5946,86 +5959,99 @@ nsFrame::ChildIsDirty(nsIFrame* aChild)
 #ifdef ACCESSIBILITY
 already_AddRefed<nsAccessible>
 nsFrame::CreateAccessible()
 {
   return nsnull;
 }
 #endif
 
-NS_DECLARE_FRAME_PROPERTY(OverflowAreaProperty, nsIFrame::DestroyRect)
+NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty,
+                          nsIFrame::DestroyOverflowAreas)
 
 void
-nsIFrame::ClearOverflowRect()
-{
-  Properties().Delete(OverflowAreaProperty());
+nsIFrame::ClearOverflowRects()
+{
+  if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+    Properties().Delete(OverflowAreasProperty());
+  }
   mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
 }
 
 /** 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 (!((mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) ||
-        aCreateIfNecessary)) {
-    return nsnull;
-  }
-
+nsOverflowAreas*
+nsIFrame::GetOverflowAreasProperty()
+{
   FrameProperties props = Properties();
-  void *value = props.Get(OverflowAreaProperty());
-
-  if (value) {
-    return (nsRect*)value;  // the property already exists
-  } else if (aCreateIfNecessary) {
-    // 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);
-    props.Set(OverflowAreaProperty(), overflow);
-    return overflow;
-  }
-
-  NS_NOTREACHED("Frame abuses GetOverflowAreaProperty()");
-  return nsnull;
+  nsOverflowAreas *overflow =
+    static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty()));
+
+  if (overflow) {
+    return overflow; // the property already exists
+  }
+
+  // The property isn't set yet, so allocate a new rect, set the property,
+  // and return the newly allocated rect
+  overflow = new nsOverflowAreas;
+  props.Set(OverflowAreasProperty(), overflow);
+  return overflow;
 }
 
 /** 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 &&
+nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
+{
+  if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
+    nsOverflowAreas *overflow =
+      static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty()));
+    *overflow = aOverflowAreas;
+
+    // Don't bother with converting to the deltas form if we already
+    // have a property.
+    return;
+  }
+
+  const nsRect& vis = aOverflowAreas.VisualOverflow();
+  PRUint32 l = -vis.x, // left edge: positive delta is leftwards
+           t = -vis.y, // top: positive is upwards
+           r = vis.XMost() - mRect.width, // right: positive is rightwards
+           b = vis.YMost() - mRect.height; // bottom: positive is downwards
+  if (aOverflowAreas.ScrollableOverflow() == nsRect(nsPoint(0, 0), GetSize()) &&
+      l <= NS_FRAME_OVERFLOW_DELTA_MAX &&
       t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
       r <= NS_FRAME_OVERFLOW_DELTA_MAX &&
       b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
+      // we have to check these against zero because we *never* want to
+      // set a frame as having no overflow in this function.  This is
+      // because FinishAndStoreOverflow calls this function prior to
+      // SetRect based on whether the overflow areas match aNewSize.
+      // In the case where the overflow areas exactly match mRect but
+      // do not match aNewSize, we need to store overflow in a property
+      // so that our eventual SetRect/SetSize will know that it has to
+      // reset our overflow areas.
       (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.
-    Properties().Delete(OverflowAreaProperty());
-    mOverflow.mDeltas.mLeft   = l;
-    mOverflow.mDeltas.mTop    = t;
-    mOverflow.mDeltas.mRight  = r;
-    mOverflow.mDeltas.mBottom = b;
+    // If they're all zero, that's fine; we're setting things to
+    // no-overflow.
+    mOverflow.mVisualDeltas.mLeft   = l;
+    mOverflow.mVisualDeltas.mTop    = t;
+    mOverflow.mVisualDeltas.mRight  = r;
+    mOverflow.mVisualDeltas.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;
+    nsOverflowAreas* overflow = GetOverflowAreasProperty();
+    NS_ASSERTION(overflow, "should have created areas");
+    *overflow = aOverflowAreas;
   }
 }
 
 inline PRBool
 IsInlineFrame(nsIFrame *aFrame)
 {
   nsIAtom *type = aFrame->GetType();
   return type == nsGkAtoms::inlineFrame ||
@@ -6159,26 +6185,27 @@ nsIFrame::FinishAndStoreOverflow(nsRect*
       // ThebesLayers, forcing it to be invalidated, so it doesn't matter
       // that we didn't reach here.
       InvalidateLayer(*aOverflowArea, nsDisplayItem::TYPE_TRANSFORM);
     }
   }
 }
 
 void
-nsFrame::ConsiderChildOverflow(nsRect&   aOverflowArea,
+nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
                                nsIFrame* aChildFrame)
 {
   const nsStyleDisplay* disp = GetStyleDisplay();
   // check here also for hidden as table frames (table, tr and td) currently 
   // don't wrap their content into a scrollable frame if overflow is specified
+  // FIXME: Why do we check this here rather than in
+  // FinishAndStoreOverflow (where we check NS_STYLE_OVERFLOW_CLIP)?
   if (!disp->IsTableClip()) {
-    nsRect childOverflow = aChildFrame->GetOverflowRect();
-    childOverflow.MoveBy(aChildFrame->GetPosition());
-    aOverflowArea.UnionRect(aOverflowArea, childOverflow);
+    aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
+                             aChildFrame->GetPosition());
   }
 }
 
 NS_IMETHODIMP 
 nsFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
                                     nsIFrame**      aProviderFrame,
                                     PRBool*         aIsChild)
 {
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -397,19 +397,19 @@ public:
   // other frame's |GetParentStyleContextFrame| methods can call this
   // method on *another* frame.  (This function handles out-of-flow
   // frames by using the frame manager's placeholder map and it also
   // handles block-within-inline and generated content wrappers.)
   nsresult DoGetParentStyleContextFrame(nsPresContext* aPresContext,
                                         nsIFrame**      aProviderFrame,
                                         PRBool*         aIsChild);
 
-  // incorporate the child overflow area into the parent overflow area
-  // if the child does not have a overflow use the child area
-  void ConsiderChildOverflow(nsRect&   aOverflowArea,
+  // Incorporate the child overflow areas into aOverflowAreas.
+  // If the child does not have a overflow, use the child area.
+  void ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
                              nsIFrame* aChildFrame);
 
   virtual const void* GetStyleDataExternal(nsStyleStructID aSID) const;
 
 
 #ifdef NS_DEBUG
   /**
    * Tracing method that writes a method enter/exit routine to the
new file mode 100644
--- /dev/null
+++ b/layout/generic/nsHTMLReflowMetrics.cpp
@@ -0,0 +1,88 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* ***** 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/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is nsHTMLReflowMetrics.cpp.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* struct containing the output from nsIFrame::Reflow */
+
+#include "nsHTMLReflowMetrics.h"
+
+void
+nsOverflowAreas::UnionWith(const nsOverflowAreas& aOther)
+{
+  // FIXME: We should probably change scrollable overflow to use
+  // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+    mRects[otype].UnionRect(mRects[otype], aOther.mRects[otype]);
+  }
+}
+
+void
+nsOverflowAreas::UnionAllWith(const nsRect& aRect)
+{
+  // FIXME: We should probably change scrollable overflow to use
+  // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+    mRects[otype].UnionRect(mRects[otype], aRect);
+  }
+}
+
+void
+nsOverflowAreas::SetAllTo(const nsRect& aRect)
+{
+  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+    mRects[otype] = aRect;
+  }
+}
+
+void
+nsHTMLReflowMetrics::SetOverflowAreasToDesiredBounds()
+{
+  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+    mOverflowAreas.Overflow(otype).SetRect(0, 0, width, height);
+  }
+}
+
+void
+nsHTMLReflowMetrics::UnionOverflowAreasWithDesiredBounds()
+{
+  // FIXME: We should probably change scrollable overflow to use
+  // UnionRectIncludeEmpty (but leave visual overflow using UnionRect).
+  nsRect rect(0, 0, width, height);
+  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+    nsRect& o = mOverflowAreas.Overflow(otype);
+    o.UnionRect(o, rect);
+  }
+}
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -50,16 +50,104 @@
 //----------------------------------------------------------------------
 
 // Option flags
 #ifdef MOZ_MATHML
 #define NS_REFLOW_CALC_BOUNDING_METRICS  0x0001
 #endif
 
 /**
+ * When we store overflow areas as an array of scrollable and visual
+ * overflow, we use these indices.
+ */
+enum nsOverflowType { eVisualOverflow, eScrollableOverflow };
+
+#define NS_FOR_FRAME_OVERFLOW_TYPES(var_)                                     \
+  for (nsOverflowType var_ = nsOverflowType(0); var_ < 2;                     \
+       var_ = nsOverflowType(var_ + 1))
+
+struct nsOverflowAreas {
+private:
+  nsRect mRects[2];
+public:
+  nsRect& Overflow(size_t aIndex) {
+    NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range");
+    return mRects[aIndex];
+  }
+  const nsRect& Overflow(size_t aIndex) const {
+    NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range");
+    return mRects[aIndex];
+  }
+
+  nsRect& VisualOverflow() { return mRects[eVisualOverflow]; }
+  const nsRect& VisualOverflow() const { return mRects[eVisualOverflow]; }
+
+  nsRect& ScrollableOverflow() { return mRects[eScrollableOverflow]; }
+  const nsRect& ScrollableOverflow() const { return mRects[eScrollableOverflow]; }
+
+  nsOverflowAreas() {
+    // default-initializes to zero due to nsRect's default constructor
+  }
+
+  nsOverflowAreas(const nsRect& aVisualOverflow,
+                  const nsRect& aScrollableOverflow)
+  {
+    mRects[eVisualOverflow] = aVisualOverflow;
+    mRects[eScrollableOverflow] = aScrollableOverflow;
+  }
+
+  nsOverflowAreas(const nsOverflowAreas& aOther) {
+    *this = aOther;
+  }
+
+  nsOverflowAreas& operator=(const nsOverflowAreas& aOther) {
+    mRects[0] = aOther.mRects[0];
+    mRects[1] = aOther.mRects[1];
+    return *this;
+  }
+
+  bool operator==(const nsOverflowAreas& aOther) const {
+    // Scrollable overflow is a point-set rectangle and visual overflow
+    // is a pixel-set rectangle.
+    return VisualOverflow() == aOther.VisualOverflow() &&
+           ScrollableOverflow().IsExactEqual(aOther.ScrollableOverflow());
+  }
+
+  bool operator!=(const nsOverflowAreas& aOther) const {
+    return !(*this == aOther);
+  }
+
+  nsOverflowAreas operator+(const nsPoint& aPoint) const {
+    nsOverflowAreas result(*this);
+    result += aPoint;
+    return result;
+  }
+
+  nsOverflowAreas& operator+=(const nsPoint& aPoint) {
+    mRects[0] += aPoint;
+    mRects[1] += aPoint;
+    return *this;
+  }
+
+  void Clear() {
+    mRects[0].SetRect(0, 0, 0, 0);
+    mRects[1].SetRect(0, 0, 0, 0);
+  }
+
+  // Mutates |this| by unioning both overflow areas with |aOther|.
+  void UnionWith(const nsOverflowAreas& aOther);
+
+  // Mutates |this| by unioning both overflow areas with |aRect|.
+  void UnionAllWith(const nsRect& aRect);
+
+  // Mutates |this| by setting both overflow areas to |aRect|.
+  void SetAllTo(const nsRect& aRect);
+};
+
+/**
  * An nsCollapsingMargin represents a vertical collapsing margin between
  * blocks as described in section 8.3.1 of CSS2,
  * <URL: http://www.w3.org/TR/REC-CSS2/box.html#collapsing-margins >.
  *
  * All adjacent vertical margins collapse, and the resulting margin is
  * the sum of the largest positive margin included and the smallest (most
  * negative) negative margin included.
  */
@@ -152,57 +240,65 @@ struct nsHTMLReflowMetrics {
   // msup is the smallest rectangle that _exactly_ encloses both the text
   // of the base and the text of the superscript.
   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
-  // (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, width, height}.
-  nsRect mOverflowArea;
+  // (HasOverflowAreas() is true) these rectangles represent the total
+  // area of the frame including visible overflow, i.e., don't include
+  // overflowing content that is hidden.  The rects are 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,
+  // width, height}.
+  nsOverflowAreas mOverflowAreas;
+
+  nsRect& VisualOverflow()
+    { return mOverflowAreas.VisualOverflow(); }
+  const nsRect& VisualOverflow() const
+    { return mOverflowAreas.VisualOverflow(); }
+  nsRect& ScrollableOverflow()
+    { return mOverflowAreas.ScrollableOverflow(); }
+  const nsRect& ScrollableOverflow() const
+    { return mOverflowAreas.ScrollableOverflow(); }
+
+  // Set all of mOverflowAreas to (0, 0, width, height).
+  void SetOverflowAreasToDesiredBounds();
+
+  // Union all of mOverflowAreas with (0, 0, width, height).
+  void UnionOverflowAreasWithDesiredBounds();
 
   PRUint32 mFlags;
- 
+
   // XXXldb Should |aFlags| generally be passed from parent to child?
   // Some places do it, and some don't.  |aFlags| should perhaps go away
   // entirely.
   nsHTMLReflowMetrics(PRUint32 aFlags = 0) {
     mFlags = aFlags;
-    mOverflowArea.x = 0;
-    mOverflowArea.y = 0;
-    mOverflowArea.width = 0;
-    mOverflowArea.height = 0;
 #ifdef MOZ_MATHML
     mBoundingMetrics.Clear();
 #endif
 
     // XXX These are OUT parameters and so they shouldn't have to be
     // initialized, but there are some bad frame classes that aren't
     // properly setting them when returning from Reflow()...
     width = height = 0;
     ascent = ASK_FOR_BASELINE;
   }
 
   nsHTMLReflowMetrics& operator=(const nsHTMLReflowMetrics& aOther)
   {
     mFlags = aOther.mFlags;
     mCarriedOutBottomMargin = aOther.mCarriedOutBottomMargin;
-    mOverflowArea.x = aOther.mOverflowArea.x;
-    mOverflowArea.y = aOther.mOverflowArea.y;
-    mOverflowArea.width = aOther.mOverflowArea.width;
-    mOverflowArea.height = aOther.mOverflowArea.height;
+    mOverflowAreas = aOther.mOverflowAreas;
 #ifdef MOZ_MATHML
     mBoundingMetrics = aOther.mBoundingMetrics;
 #endif
 
     width = aOther.width;
     height = aOther.height;
     ascent = aOther.ascent;
     return *this;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -450,27 +450,27 @@ 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.
+ * When there is no scrollable overflow rect, the visual 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;
+#define NS_FRAME_OVERFLOW_NONE    0x00000000 // there are no overflow rects;
                                              // code relies on this being
                                              // the all-zero value
 
 #define NS_FRAME_OVERFLOW_LARGE   0x000000ff // overflow is stored as a
                                              // separate rect property
 
 //----------------------------------------------------------------------
 
@@ -779,32 +779,27 @@ public:
 
   /**
    * 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();
+    if (mOverflow.mType != NS_FRAME_OVERFLOW_LARGE &&
+        mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
+      nsOverflowAreas overflow = GetOverflowAreas();
       mRect = aRect;
-      SetOverflowRect(r);
+      SetOverflowAreas(overflow);
     } 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);
-    }
+    SetRect(nsRect(mRect.TopLeft(), aSize));
   }
   void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); }
 
   /**
    * Return frame's computed offset due to relative positioning
    */
   nsPoint GetRelativeOffset(const nsStyleDisplay* aDisplay = nsnull) const;
 
@@ -831,16 +826,21 @@ public:
     delete static_cast<nsRect*>(aPropertyValue);
   }
 
   static void DestroyPoint(void* aPropertyValue)
   {
     delete static_cast<nsPoint*>(aPropertyValue);
   }
 
+  static void DestroyOverflowAreas(void* aPropertyValue)
+  {
+    delete static_cast<nsOverflowAreas*>(aPropertyValue);
+  }
+
 #ifdef _MSC_VER
 // XXX Workaround MSVC issue by making the static FramePropertyDescriptor
 // non-const.  See bug 555727.
 #define NS_PROPERTY_DESCRIPTOR_CONST
 #else
 #define NS_PROPERTY_DESCRIPTOR_CONST const
 #endif
 
@@ -2050,95 +2050,117 @@ public:
    * Invalidate the entire frame subtree for this frame. Invalidates this
    * frame's overflow rect, and also ensures that all ThebesLayer children
    * of ContainerLayers associated with frames in this subtree are
    * completely invalidated.
    */
   void InvalidateFrameSubtree();
 
   /**
-   * Invalidate the overflow area for this frame. Invalidates this
-   * frame's overflow rect. Does not necessarily cause ThebesLayers for
-   * descendant frames to be repainted; only this frame can be relied on
-   * to be repainted.
+   * Invalidates this frame's visual overflow rect. Does not necessarily
+   * cause ThebesLayers for descendant frames to be repainted; only this
+   * frame can be relied on to be repainted.
    */
   void InvalidateOverflowRect();
 
   /**
-   * Computes a rect that encompasses everything that might be painted by
+   * Returns 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.
    *
-   * HasOverflowRect() (below) will return PR_TRUE when this overflow rect
-   * has been explicitly set, even if it matches mRect.
+   * HasOverflowRects() (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.  FIXME: This actually isn't true, but it should be.
+   *
+   * The visual overflow rect should NEVER be used for things that
+   * affect layout.  The scrollable overflow rect is permitted to affect
+   * layout.
+   *
+   * @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
+   * is a CSS transform scaling it down)
+   */
+  nsRect GetVisualOverflowRect() const {
+    return GetOverflowRect(eVisualOverflow);
+  }
+
+  /**
+   * Returns a rect that encompasses the area of this frame that the
+   * user should be able to scroll to reach.  This is similar to
+   * GetVisualOverflowRect, but does not include outline or shadows, and
+   * may in the future include more margins than visual overflow does.
+   * It does not include areas clipped out by the CSS "overflow" and
+   * "clip" properties.
+   *
+   * HasOverflowRects() (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
    * is a CSS transform scaling it down)
    */
-  nsRect GetOverflowRect() const;
+  nsRect GetScrollableOverflowRect() const {
+    return GetOverflowRect(eScrollableOverflow);
+  }
+
+  nsRect GetOverflowRect(nsOverflowType aType) const;
+
+  nsOverflowAreas GetOverflowAreas() 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.
-   *
-   * 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.
+   * Same as GetScrollableOverflowRect, except relative to the parent
+   * frame.
    *
    * @return the rect relative to the parent frame, in the parent frame's
    * coordinate system
    */
-  nsRect GetOverflowRectRelativeToParent() const;
+  nsRect GetScrollableOverflowRectRelativeToParent() 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.
+   * Like GetVisualOverflowRect, except in this frame's
+   * coordinate system (before transforms are applied).
    *
    * @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;
+  nsRect GetVisualOverflowRectRelativeToSelf() const;
 
   /**
-   * 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.
+   * Store the overflow area in the frame's mOverflow.mVisualDeltas
+   * 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 {
+  PRBool HasOverflowAreas() const {
     return mOverflow.mType != NS_FRAME_OVERFLOW_NONE;
   }
 
   /**
-   * Removes any stored overflow rect from the frame.
+   * Removes any stored overflow rects (visual and scrollable) from the frame.
    */
-  void ClearOverflowRect();
+  void ClearOverflowRects();
 
   /**
    * Determine whether borders should not be painted on certain sides of the
    * frame.
    */
   virtual PRIntn GetSkipSides() const { return 0; }
 
   /** Selection related calls
@@ -2534,19 +2556,19 @@ NS_PTR_TO_INT32(frame->Properties().Get(
    *         the returned value is a 'best effort' in case errors
    *         are encountered rummaging through the frame.
    */
   nsPeekOffsetStruct GetExtremeCaretPosition(PRBool aStart);
 
   /**
    * Same thing as nsFrame::CheckInvalidateSizeChange, but more flexible.  The
    * implementation of this method must not depend on the mRect or
-   * GetOverflowRect() of the frame!  Note that it's safe to assume in this
-   * method that the frame origin didn't change.  If it did, whoever moved the
-   * frame will invalidate as needed anyway.
+   * GetVisualOverflowRect() of the frame!  Note that it's safe to
+   * assume in this method that the frame origin didn't change.  If it
+   * did, whoever moved the frame will invalidate as needed anyway.
    */
   void CheckInvalidateSizeChange(const nsRect& aOldRect,
                                  const nsRect& aOldOverflowRect,
                                  const nsSize& aNewDesiredSize);
 
   /**
    * Get a line iterator for this frame, if supported.
    *
@@ -2584,19 +2606,19 @@ protected:
   // as a separate rect property.
   union {
     PRUint32  mType;
     struct {
       PRUint8 mLeft;
       PRUint8 mTop;
       PRUint8 mRight;
       PRUint8 mBottom;
-    } mDeltas;
+    } mVisualDeltas;
   } 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);
 
   /**
@@ -2680,18 +2702,32 @@ protected:
    * @param  aPos See description in nsFrameSelection.h. The following fields are
    *              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);
+  nsOverflowAreas* GetOverflowAreasProperty();
+  nsRect GetVisualOverflowFromDeltas() const {
+    NS_ABORT_IF_FALSE(mOverflow.mType != NS_FRAME_OVERFLOW_LARGE,
+                      "should not be called when overflow is in a property");
+    // 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.mVisualDeltas.mLeft,
+                  -(PRInt32)mOverflow.mVisualDeltas.mTop,
+                  mRect.width + mOverflow.mVisualDeltas.mRight +
+                                mOverflow.mVisualDeltas.mLeft,
+                  mRect.height + mOverflow.mVisualDeltas.mBottom +
+                                 mOverflow.mVisualDeltas.mTop);
+  }
+  void SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
   nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const PRInt32 aAPD) const;
 
 #ifdef NS_DEBUG
 public:
   // Formerly nsIFrameDebug
   NS_IMETHOD  List(FILE* out, PRInt32 aIndent) const = 0;
   NS_IMETHOD  GetFrameName(nsAString& aResult) const = 0;
   NS_IMETHOD_(nsFrameState)  GetDebugStateBits() const = 0;