Bug 375304. Make nsHTMLScrollFrame handle situations where the child content height depends on our height. patch by Eli Friedman, r+sr=roc,dbaron
authorroc+@cs.cmu.edu
Tue, 04 Dec 2007 19:05:57 -0800
changeset 8731 9bfc46fa736e8643bcf6a9e614b1423ae2f1d878
parent 8730 5212be1951930a2ff18bf60c033a5ed93adc402f
child 8732 9e1cc5a693c05d2dd01635a63b9783cdffb24fd1
push idunknown
push userunknown
push dateunknown
bugs375304
milestone1.9b2pre
Bug 375304. Make nsHTMLScrollFrame handle situations where the child content height depends on our height. patch by Eli Friedman, r+sr=roc,dbaron
layout/generic/nsBlockFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsHTMLReflowState.cpp
layout/reftests/reftest.list
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -779,22 +779,18 @@ nsBlockFrame::ComputeTightBounds(gfxCont
 
 static nsSize
 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
                                          nsSize aFrameSize)
 {
   // The issue here is that for a 'height' of 'auto' the reflow state
   // code won't know how to calculate the containing block height
   // because it's calculated bottom up. So we use our own computed
-  // size as the dimensions. We don't really want to do this for the
-  // initial containing block
+  // size as the dimensions.
   nsIFrame* frame = aReflowState.frame;
-  if (nsLayoutUtils::IsInitialContainingBlock(frame)) {
-    return nsSize(-1, -1);
-  }
 
   nsSize cbSize(aFrameSize);
     // Containing block is relative to the padding edge
   const nsMargin& border = aReflowState.mStyleBorder->GetBorder();
   cbSize.width -= border.left + border.right;
   cbSize.height -= border.top + border.bottom;
 
   if (frame->GetParent()->GetContent() == frame->GetContent()) {
@@ -820,23 +816,19 @@ CalculateContainingBlockSizeForAbsolutes
       // padding-edge. We need better APIs for getting the various boxes from a frame.
       nsIScrollableFrame* scrollFrame;
       CallQueryInterface(aLastRS->frame, &scrollFrame);
       nsMargin scrollbars(0,0,0,0);
       if (scrollFrame) {
         nsBoxLayoutState dummyState(aLastRS->frame->PresContext(),
                                     aLastRS->rendContext);
         scrollbars = scrollFrame->GetDesiredScrollbarSizes(&dummyState);
-        // XXX We should account for the horizontal scrollbar too --- but currently
-        // nsGfxScrollFrame assumes nothing depends on the presence (or absence) of
-        // a horizontal scrollbar, so accounting for it would create incremental
-        // reflow bugs.
-        //if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
+        if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
           scrollbars.top = scrollbars.bottom = 0;
-        //}
+        }
         if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
           scrollbars.left = scrollbars.right = 0;
         }
       }
       // We found a reflow state for the outermost wrapping frame, so use
       // its computed metrics if available
       if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
         cbSize.width = PR_MAX(0,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -222,28 +222,29 @@ nsIAtom*
 nsHTMLScrollFrame::GetType() const
 {
   return nsGkAtoms::scrollFrame; 
 }
 
 /**
  HTML scrolling implementation
 
- We rely on the fact that if height is 'auto', changing the height of
- the element does not require reflowing the contents.
-
  All other things being equal, we prefer layouts with fewer scrollbars showing.
 */
 
 struct ScrollReflowState {
   const nsHTMLReflowState& mReflowState;
   nsBoxLayoutState mBoxState;
   nsGfxScrollFrameInner::ScrollbarStyles mStyles;
   nsMargin mComputedBorder;
 
+  // === Filled in by ReflowScrolledFrame ===
+  PRPackedBool mReflowedContentsWithHScrollbar;
+  PRPackedBool mReflowedContentsWithVScrollbar;
+
   // === Filled in when TryLayout succeeds ===
   // The area of the scrollport, in coordinates relative to the scrollframe
   nsRect mScrollPortRect;
   // The size of the inside-border area
   nsSize mInsideBorderSize;
   // Whether we decided to show the horizontal scrollbar
   PRPackedBool mShowHScrollbar;
   // Whether we decided to show the vertical scrollbar
@@ -317,26 +318,39 @@ GetScrollbarMetrics(nsBoxLayoutState& aS
  * (i.e., the scrollbar fits horizontally)
  * 4) the style is SCROLL, or the kid's overflow-area XMost is
  * greater than the scrollport width
  *
  * @param aForce if PR_TRUE, then we just assume the layout is consistent.
  */
 PRBool
 nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState,
-                             const nsHTMLReflowMetrics& aKidMetrics,
-                             PRBool aAssumeVScroll, PRBool aAssumeHScroll,
-                             PRBool aForce)
+                             nsHTMLReflowMetrics* aKidMetrics,
+                             PRBool aAssumeHScroll, PRBool aAssumeVScroll,
+                             PRBool aForce, nsresult* aResult)
 {
+  *aResult = NS_OK;
+
   if ((aState->mStyles.mVertical == NS_STYLE_OVERFLOW_HIDDEN && aAssumeVScroll) ||
       (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
     NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
     return PR_FALSE;
   }
-  
+
+  if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
+      ((mInner.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) &&
+       aAssumeHScroll != aState->mReflowedContentsWithHScrollbar)) {
+    nsresult rv = ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll,
+                                      aKidMetrics, PR_FALSE);
+    if (NS_FAILED(rv)) {
+      *aResult = rv;
+      return PR_FALSE;
+    }
+  }
+
   nsSize vScrollbarMinSize(0, 0);
   nsSize vScrollbarPrefSize(0, 0);
   if (mInner.mVScrollbarBox) {
     GetScrollbarMetrics(aState->mBoxState, mInner.mVScrollbarBox,
                         &vScrollbarMinSize,
                         aAssumeVScroll ? &vScrollbarPrefSize : nsnull, PR_TRUE);
   }
   nscoord vScrollbarDesiredWidth = aAssumeVScroll ? vScrollbarPrefSize.width : 0;
@@ -351,19 +365,19 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
   }
   nscoord hScrollbarDesiredHeight = aAssumeHScroll ? hScrollbarPrefSize.height : 0;
   nscoord hScrollbarDesiredWidth = aAssumeHScroll ? hScrollbarPrefSize.width : 0;
 
   // First, compute our inside-border size and scrollport size
   // XXXldb Can we depend more on ComputeSize here?
   nsSize desiredInsideBorderSize;
   desiredInsideBorderSize.width = vScrollbarDesiredWidth +
-    PR_MAX(aKidMetrics.width, hScrollbarDesiredWidth);
+    PR_MAX(aKidMetrics->width, hScrollbarDesiredWidth);
   desiredInsideBorderSize.height = hScrollbarDesiredHeight +
-    PR_MAX(aKidMetrics.height, vScrollbarDesiredHeight);
+    PR_MAX(aKidMetrics->height, vScrollbarDesiredHeight);
   aState->mInsideBorderSize =
     ComputeInsideBorderSize(aState, desiredInsideBorderSize);
   nsSize scrollPortSize = nsSize(PR_MAX(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
                                  PR_MAX(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
                                                                                 
   if (!aForce) {
     nsRect scrolledRect = mInner.GetScrolledRect(scrollPortSize);
 
@@ -403,51 +417,67 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
   if (!mInner.IsScrollbarOnRight()) {
     scrollPortOrigin.x += vScrollbarActualWidth;
   }
   aState->mScrollPortRect = nsRect(scrollPortOrigin, scrollPortSize);
   return PR_TRUE;
 }
 
 nsresult
-nsHTMLScrollFrame::ReflowScrolledFrame(const ScrollReflowState& aState,
+nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState,
                                        PRBool aAssumeHScroll,
                                        PRBool aAssumeVScroll,
                                        nsHTMLReflowMetrics* aMetrics,
                                        PRBool aFirstPass)
 {
   // these could be NS_UNCONSTRAINEDSIZE ... PR_MIN arithmetic should
   // be OK
-  nscoord paddingLR = aState.mReflowState.mComputedPadding.LeftRight();
+  nscoord paddingLR = aState->mReflowState.mComputedPadding.LeftRight();
+
+  nscoord availWidth = aState->mReflowState.ComputedWidth() + paddingLR;
 
-  nscoord availWidth = aState.mReflowState.ComputedWidth() + paddingLR;
-
+  nscoord computedHeight = aState->mReflowState.ComputedHeight();
+  nscoord computedMinHeight = aState->mReflowState.mComputedMinHeight;
+  nscoord computedMaxHeight = aState->mReflowState.mComputedMaxHeight;
+  if (aAssumeHScroll) {
+    nsSize hScrollbarPrefSize = 
+      mInner.mHScrollbarBox->GetPrefSize(const_cast<nsBoxLayoutState&>(aState->mBoxState));
+    if (computedHeight != NS_UNCONSTRAINEDSIZE)
+      computedHeight = PR_MAX(0, computedHeight - hScrollbarPrefSize.height);
+    computedMinHeight = PR_MAX(0, computedMinHeight - hScrollbarPrefSize.height);
+    if (computedMaxHeight != NS_UNCONSTRAINEDSIZE)
+      computedMaxHeight = PR_MAX(0, computedMaxHeight - hScrollbarPrefSize.height);
+  }
+  
   if (aAssumeVScroll) {
     nsSize vScrollbarPrefSize = 
-      mInner.mVScrollbarBox->GetPrefSize(const_cast<nsBoxLayoutState&>(aState.mBoxState));
+      mInner.mVScrollbarBox->GetPrefSize(const_cast<nsBoxLayoutState&>(aState->mBoxState));
     availWidth = PR_MAX(0, availWidth - vScrollbarPrefSize.width);
   }
 
   // We're forcing the padding on our scrolled frame, so let it know what that
   // padding is.
   mInner.mScrolledFrame->
     SetProperty(nsGkAtoms::usedPaddingProperty,
-                new nsMargin(aState.mReflowState.mComputedPadding),
+                new nsMargin(aState->mReflowState.mComputedPadding),
                 nsCSSOffsetState::DestroyMarginFunc);  
   
   nsPresContext* presContext = PresContext();
   // Pass PR_FALSE for aInit so we can pass in the correct padding
-  nsHTMLReflowState kidReflowState(presContext, aState.mReflowState,
+  nsHTMLReflowState kidReflowState(presContext, aState->mReflowState,
                                    mInner.mScrolledFrame,
                                    nsSize(availWidth, NS_UNCONSTRAINEDSIZE),
                                    -1, -1, PR_FALSE);
   kidReflowState.Init(presContext, -1, -1, nsnull,
-                      &aState.mReflowState.mComputedPadding);
+                      &aState->mReflowState.mComputedPadding);
   kidReflowState.mFlags.mAssumingHScrollbar = aAssumeHScroll;
   kidReflowState.mFlags.mAssumingVScrollbar = aAssumeVScroll;
+  kidReflowState.SetComputedHeight(computedHeight);
+  kidReflowState.mComputedMinHeight = computedMinHeight;
+  kidReflowState.mComputedMaxHeight = computedMaxHeight;
 
   nsReflowStatus status;
   nsresult rv = ReflowChild(mInner.mScrolledFrame, presContext, *aMetrics,
                             kidReflowState, 0, 0,
                             NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW, status);
   // Don't resize or position the view because we're going to resize
   // it to the correct size anyway in PlaceScrollArea. Allowing it to
   // resize here would size it to the natural height of the frame,
@@ -462,16 +492,19 @@ nsHTMLScrollFrame::ReflowScrolledFrame(c
   // 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
   // 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;
 }
 
 PRBool
 nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowState& aState)
 {
   if (aState.mStyles.mVertical != NS_STYLE_OVERFLOW_AUTO)
     // no guessing required
@@ -521,23 +554,20 @@ nsHTMLScrollFrame::InInitialReflow() con
   // assumption, because our initial reflow is no longer synchronous).
   return !mInner.mIsRoot && (GetStateBits() & NS_FRAME_FIRST_REFLOW);
 }
 
 nsresult
 nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
                                   const nsHTMLReflowMetrics& aDesiredSize)
 {
-  PRBool currentlyUsingVScrollbar = GuessVScrollbarNeeded(*aState);
   nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags);
-  nsresult rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
-                                    &kidDesiredSize, PR_TRUE);
-  if (NS_FAILED(rv))
-    return rv;
-  PRBool didUseScrollbar = currentlyUsingVScrollbar;
+  nsresult rv = ReflowScrolledFrame(aState, mInner.mHasHorizontalScrollbar,
+      GuessVScrollbarNeeded(*aState), &kidDesiredSize, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // There's an important special case ... if the child appears to fit
   // in the inside-border rect (but overflows the scrollport), we
   // should try laying it out without a vertical scrollbar. It will
   // usually fit because making the available-width wider will not
   // normally make the child taller. (The only situation I can think
   // of is when you have a line containing %-width inline replaced
   // elements whose percentages sum to more than 100%, so increasing
@@ -545,76 +575,66 @@ nsHTMLScrollFrame::ReflowContents(Scroll
   // before.) If we don't treat this case specially, then we will
   // decide that showing scrollbars is OK because the content
   // overflows when we're showing scrollbars and we won't try to
   // remove the vertical scrollbar.
 
   // Detecting when we enter this special case is important for when
   // people design layouts that exactly fit the container "most of the
   // time".
-  if (currentlyUsingVScrollbar &&
+
+  // XXX Is this check really sufficient to catch all the incremental cases
+  // where the ideal case doesn't have a scrollbar?
+  if ((aState->mReflowedContentsWithHScrollbar || aState->mReflowedContentsWithVScrollbar) &&
       aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
       aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
     nsSize insideBorderSize =
       ComputeInsideBorderSize(aState,
                               nsSize(kidDesiredSize.width, kidDesiredSize.height));
     nsRect scrolledRect = mInner.GetScrolledRect(insideBorderSize);
     if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
-      // Let's pretend we had no vertical scrollbar coming in here
-      currentlyUsingVScrollbar = PR_FALSE;
-      rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
+      // Let's pretend we had no scrollbars coming in here
+      rv = ReflowScrolledFrame(aState, PR_FALSE, PR_FALSE,
                                &kidDesiredSize, PR_FALSE);
-      if (NS_FAILED(rv))
-        return rv;
-      didUseScrollbar = PR_FALSE;
+      NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  // First try a layout without a horizontal scrollbar, then with.
-  if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_FALSE, PR_FALSE))
+  // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
+  // Do this first because changing the vertical scrollbar setting is expensive,
+  // forcing a reflow always.
+
+  // First try a layout without a horizontal scrollbar, then with, except that
+  // if RELATIVE_HEIGHT is set then for performance we should try the status quo
+  // first. If RELATIVE_HEIGHT is not set then trying no horizontal scrollbar
+  // first is almost free.
+  PRBool firstHScrollbarState =
+    (mInner.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
+        ? aState->mReflowedContentsWithHScrollbar : PR_FALSE;
+  if (TryLayout(aState, &kidDesiredSize, firstHScrollbarState,
+                aState->mReflowedContentsWithVScrollbar, PR_FALSE, &rv))
     return NS_OK;
-  // XXX Adding a horizontal scrollbar could cause absolute children positioned
-  // relative to the bottom padding-edge to need to be reflowed. But we don't,
-  // because that would be slow.
-  if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_TRUE, PR_FALSE))
+  if (TryLayout(aState, &kidDesiredSize, !firstHScrollbarState,
+                aState->mReflowedContentsWithVScrollbar, PR_FALSE, &rv))
     return NS_OK;
 
-  PRBool canHaveVerticalScrollbar =
-    aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
-  // That didn't work. Try the other setting for the vertical scrollbar.
-  // But don't try to show a scrollbar if we know there can't be one.
-  if (currentlyUsingVScrollbar || canHaveVerticalScrollbar) {
-    nsHTMLReflowMetrics kidRetrySize(aDesiredSize.mFlags);
-    rv = ReflowScrolledFrame(*aState, PR_FALSE, !currentlyUsingVScrollbar,
-                             &kidRetrySize, PR_FALSE);
-    if (NS_FAILED(rv))
-      return rv;
-    didUseScrollbar = !currentlyUsingVScrollbar;
-    // XXX Adding a horizontal scrollbar could cause absolute children positioned
-    // relative to the bottom padding-edge to need to be reflowed. But we don't,
-    // because that would be slow.
-    if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_FALSE, PR_FALSE))
-      return NS_OK;
-    if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_TRUE, PR_FALSE))
-      return NS_OK;
+  // OK, now try toggling the vertical scrollbar. The performance advantage
+  // of trying the status-quo horizontal scrollbar state for RELATIVE_HEIGHT cases
+  // does not exist here (we'll have to reflow due to the vertical scrollbar
+  // change), so always try no horizontal scrollbar first.
+  PRBool newVScrollbarState = !aState->mReflowedContentsWithVScrollbar;
+  if (TryLayout(aState, &kidDesiredSize, PR_FALSE, newVScrollbarState, PR_FALSE, &rv))
+    return NS_OK;
+  if (TryLayout(aState, &kidDesiredSize, PR_TRUE, newVScrollbarState, PR_FALSE, &rv))
+    return NS_OK;
 
-    NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
-  } else {
-    NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
-  }
-
-  // Fall back to no scrollbars --- even if NS_STYLE_OVERFLOW_SCROLL is
-  // in effect. They might not fit anyway.
-  if (didUseScrollbar) {
-    rv = ReflowScrolledFrame(*aState, PR_FALSE, PR_FALSE, &kidDesiredSize, PR_FALSE);
-    if (NS_FAILED(rv))
-      return rv;
-  }
-  TryLayout(aState, kidDesiredSize, PR_FALSE, PR_FALSE, PR_TRUE);
-  return NS_OK;
+  // OK, we're out of ideas. Try again with both scrollbars and force the layout
+  // to stick even if it's inconsistent. This just happens sometimes.
+  TryLayout(aState, &kidDesiredSize, PR_TRUE, PR_TRUE, PR_TRUE, &rv);
+  return rv;
 }
 
 void
 nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState)
 {
   nsIView* scrollView = mInner.mScrollableView->View();
   nsIViewManager* vm = scrollView->GetViewManager();
   vm->MoveViewTo(scrollView, aState.mScrollPortRect.x, aState.mScrollPortRect.y);
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -257,20 +257,20 @@ public:
 
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists) {
     return mInner.BuildDisplayList(aBuilder, aDirtyRect, aLists);
   }
 
   PRBool TryLayout(ScrollReflowState* aState,
-                   const nsHTMLReflowMetrics& aKidMetrics,
+                   nsHTMLReflowMetrics* aKidMetrics,
                    PRBool aAssumeVScroll, PRBool aAssumeHScroll,
-                   PRBool aForce);
-  nsresult ReflowScrolledFrame(const ScrollReflowState& aState,
+                   PRBool aForce, nsresult* aResult);
+  nsresult ReflowScrolledFrame(ScrollReflowState* aState,
                                PRBool aAssumeHScroll,
                                PRBool aAssumeVScroll,
                                nsHTMLReflowMetrics* aMetrics,
                                PRBool aFirstPass);
   nsresult ReflowContents(ScrollReflowState* aState,
                           const nsHTMLReflowMetrics& aDesiredSize);
   void PlaceScrollArea(const ScrollReflowState& aState);
 
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -357,26 +357,17 @@ void nsHTMLReflowState::InitCBReflowStat
  * this function as well
  */
 static PRBool
 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs) 
 {
   nsIAtom* frameType = rs->frame->GetType();
   if (nsGkAtoms::blockFrame == frameType ||
       nsGkAtoms::areaFrame == frameType ||
-      nsGkAtoms::scrollFrame == frameType) {  
-
-    if (nsGkAtoms::areaFrame == frameType) {
-      // Skip over scrolled-content area frames
-      if (rs->frame->GetStyleContext()->GetPseudoType() ==
-          nsCSSAnonBoxes::scrolledContent) {
-        return PR_FALSE;
-      }
-    }
-    
+      nsGkAtoms::scrollFrame == frameType) {
     // Note: This next condition could change due to a style change,
     // but that would cause a style reflow anyway, which means we're ok.
     if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
       if (!rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
         return PR_FALSE;
       }
     }
   }
@@ -419,17 +410,17 @@ nsHTMLReflowState::InitResizeFlags(nsPre
                         mComputedHeight + mComputedBorderPadding.TopBottom();
   }
 
   const PRBool dependsOnCBHeight =
     mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent ||
     mStylePosition->mMinHeight.GetUnit() == eStyleUnit_Percent ||
     mStylePosition->mMaxHeight.GetUnit() == eStyleUnit_Percent ||
     mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Percent ||
-    mStylePosition->mOffset.GetBottomUnit() == eStyleUnit_Percent ||
+    mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto ||
     frame->IsBoxFrame() ||
     frame->GetIntrinsicSize().height.GetUnit() == eStyleUnit_Percent;
 
   // If we're the child of a table cell that performs special height
   // reflows and we could be the child that requires them, always set
   // the vertical resize in case this is the first pass before the
   // special height reflow.
   if (!mFlags.mVResize && mCBReflowState &&
@@ -1412,24 +1403,16 @@ CalcQuirkContainingBlockHeight(const nsH
   const nsHTMLReflowState* rs = aCBReflowState;
   for (; rs; rs = (nsHTMLReflowState *)(rs->parentReflowState)) { 
     nsIAtom* frameType = rs->frame->GetType();
     // if the ancestor is auto height then skip it and continue up if it 
     // is the first block/area frame and possibly the body/html
     if (nsGkAtoms::blockFrame == frameType ||
         nsGkAtoms::areaFrame == frameType ||
         nsGkAtoms::scrollFrame == frameType) {
-      
-      if (nsGkAtoms::areaFrame == frameType) {
-        // Skip over scrolled-content area frames
-        if (rs->frame->GetStyleContext()->GetPseudoType() ==
-            nsCSSAnonBoxes::scrolledContent) {
-          continue;
-        }
-      }
 
       secondAncestorRS = firstAncestorRS;
       firstAncestorRS = (nsHTMLReflowState*)rs;
 
       // If the current frame we're looking at is positioned, we don't want to
       // go any further (see bug 221784).  The behavior we want here is: 1) If
       // not auto-height, use this as the percentage base.  2) If auto-height,
       // keep looking, unless the frame is positioned.
@@ -1437,22 +1420,17 @@ CalcQuirkContainingBlockHeight(const nsH
         if (rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
           break;
         } else {
           continue;
         }
       }
     }
     else if (nsGkAtoms::canvasFrame == frameType) {
-      // Use scroll frames' computed height if we have one, this will
-      // allow us to get viewport height for native scrollbars.
-      nsHTMLReflowState* scrollState = (nsHTMLReflowState *)rs->parentReflowState;
-      if (nsGkAtoms::scrollFrame == scrollState->frame->GetType()) {
-        rs = scrollState;
-      }
+      // Always continue on to the height calculation
     }
     else if (nsGkAtoms::pageContentFrame == frameType) {
       nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
       // only use the page content frame for a height basis if it is the first in flow
       if (prevInFlow) 
         break;
     }
     else {
@@ -1663,34 +1641,24 @@ nsHTMLReflowState::InitConstraints(nsPre
     nsFrame::ListTag(stdout, frame); printf(": cb=");
     nsFrame::ListTag(stdout, cbrs->frame); printf(" size=%d,%d\n", aContainingBlockWidth, aContainingBlockHeight);
 #endif
 
     // See if the containing block height is based on the size of its
     // content
     nsIAtom* fType;
     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
-      // See if the containing block is (1) a scrolled frame, i.e. its
-      // parent is a scroll frame. The presence of the intervening
-      // frame (that the scroll frame scrolls) needs to be hidden from
-      // the containingBlockHeight calcuation, or (2) a cell frame which needs
+      // See if the containing block is a cell frame which needs
       // to use the mComputedHeight of the cell instead of what the cell block passed in.
+      // XXX It seems like this could lead to bugs with min-height and friends
       if (cbrs->parentReflowState) {
-        nsIFrame* f = cbrs->parentReflowState->frame;
-        fType = f->GetType();
-        if (nsGkAtoms::scrollFrame == fType) {
-          // Use the scroll frame's computed height instead
-          aContainingBlockHeight = cbrs->parentReflowState->mComputedHeight;
-        }
-        else {
-          fType = cbrs->frame->GetType();
-          if (IS_TABLE_CELL(fType)) {
-            // use the cell's computed height 
-            aContainingBlockHeight = cbrs->mComputedHeight;
-          }
+        fType = cbrs->frame->GetType();
+        if (IS_TABLE_CELL(fType)) {
+          // use the cell's computed height 
+          aContainingBlockHeight = cbrs->mComputedHeight;
         }
       }
     }
 
     InitOffsets(aContainingBlockWidth, aBorder, aPadding);
 
     nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
 
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -75,8 +75,12 @@ include columns/reftest.list
 # image-region/
 include image-region/reftest.list
 
 # block-inside-inline splits
 include ib-split/reftest.list
 
 # line-breaking
 include line-breaking/reftest.list
+
+# percent-overflow-sizing
+include percent-overflow-sizing/reftest.list
+