Bug 1242172. Invalidate intrinsic ISizes that depend on viewport BSize when the viewport is resized. r=dbaron
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 29 Feb 2016 18:49:34 +1300
changeset 342011 c8f66b7c4be3491d872e993a0bc51c3e99303f38
parent 342010 3f0499738b89a6b7cf2a964112398bc838c53e2e
child 342012 8aed0bcad3886dadcc2c696630a2f119c2f7cdfd
push id13342
push userbmo:james@hoppipolla.co.uk
push dateFri, 18 Mar 2016 09:55:58 +0000
reviewersdbaron
bugs1242172
milestone48.0a1
Bug 1242172. Invalidate intrinsic ISizes that depend on viewport BSize when the viewport is resized. r=dbaron MozReview-Commit-ID: INEHo7ghGyz
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/reftests/bugs/1242172-1-ref.html
layout/reftests/bugs/1242172-1-subdoc.html
layout/reftests/bugs/1242172-1.html
layout/reftests/bugs/1242172-2-ref.html
layout/reftests/bugs/1242172-2.html
layout/reftests/bugs/reftest.list
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5156,16 +5156,41 @@ nsLayoutUtils::MarkDescendantsDirty(nsIF
           stack.AppendElement(kid);
         }
       }
     } while (stack.Length() != 0);
   } while (subtrees.Length() != 0);
 }
 
 /* static */
+void
+nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame)
+{
+  AutoTArray<nsIFrame*, 32> stack;
+  stack.AppendElement(aFrame);
+
+  do {
+    nsIFrame* f = stack.ElementAt(stack.Length() - 1);
+    stack.RemoveElementAt(stack.Length() - 1);
+
+    if (!f->HasAnyStateBits(
+        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+      continue;
+    }
+    f->MarkIntrinsicISizesDirty();
+
+    for (nsIFrame::ChildListIterator lists(f); !lists.IsDone(); lists.Next()) {
+      for (nsIFrame* kid : lists.CurrentList()) {
+        stack.AppendElement(kid);
+      }
+    }
+  } while (stack.Length() != 0);
+}
+
+/* static */
 LogicalSize
 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
                    nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
                    const IntrinsicSize& aIntrinsicSize,
                    nsSize aIntrinsicRatio,
                    const mozilla::LogicalSize& aCBSize,
                    const mozilla::LogicalSize& aMargin,
                    const mozilla::LogicalSize& aBorder,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1485,16 +1485,18 @@ public:
             aCoord.GetPercentValue() == 0.0f) ||
            (aCoord.IsCalcUnit() &&
             nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) == 0 &&
             nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) == 0);
   }
 
   static void MarkDescendantsDirty(nsIFrame *aSubtreeRoot);
 
+  static void MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame);
+
   /*
    * Calculate the used values for 'width' and 'height' for a replaced element.
    *
    *   http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
    */
   static mozilla::LogicalSize
   ComputeSizeWithIntrinsicDimensions(mozilla::WritingMode aWM,
                     nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1809,16 +1809,19 @@ PresShell::ResizeReflowIgnoreOverride(ns
   // ignore them altogether.
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
     // We can't do the work needed for SizeToContent without a root
     // frame, and we want to return before setting the visible area.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  const bool isHeightChanging =
+    (mPresContext->GetVisibleArea().height != aHeight);
+
   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
 
   // There isn't anything useful we can do if the initial reflow hasn't happened.
   if (!rootFrame) {
     return NS_OK;
   }
 
   RefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
@@ -1836,16 +1839,24 @@ PresShell::ResizeReflowIgnoreOverride(ns
       mPresContext->RestyleManager()->ProcessPendingRestyles();
     }
 
     rootFrame = mFrameConstructor->GetRootFrame();
     if (!mIsDestroying && rootFrame) {
       // XXX Do a full invalidate at the beginning so that invalidates along
       // the way don't have region accumulation issues?
 
+      if (isHeightChanging) {
+        // For BSize changes driven by style, RestyleManager handles this.
+        // For height:auto BSizes (i.e. layout-controlled), descendant
+        // intrinsic sizes can't depend on them. So the only other case is
+        // viewport-controlled BSizes which we handle here.
+        nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
+      }
+
       {
         nsAutoCauseReflowNotifier crNotifier(this);
         WillDoReflow();
 
         // Kick off a top-down reflow
         AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
         nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -321,16 +321,20 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
       (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
     NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
     return false;
   }
 
   if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
       (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar &&
        ScrolledContentDependsOnHeight(aState))) {
+    if (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar) {
+      nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
+          mHelper.mScrolledFrame);
+    }
     ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll, aKidMetrics,
                         false);
   }
 
   nsSize vScrollbarMinSize(0, 0);
   nsSize vScrollbarPrefSize(0, 0);
   if (mHelper.mVScrollbarBox) {
     GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
@@ -424,17 +428,18 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
 // XXX Height/BSize mismatch needs to be addressed here; check the caller!
 // Currently this will only behave as expected for horizontal writing modes.
 // (See bug 1175509.)
 bool
 nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState* aState)
 {
   // Return true if ReflowScrolledFrame is going to do something different
   // based on the presence of a horizontal scrollbar.
-  return (mHelper.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) ||
+  return mHelper.mScrolledFrame->HasAnyStateBits(
+      NS_FRAME_CONTAINS_RELATIVE_BSIZE | NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE) ||
     aState->mReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
     aState->mReflowState.ComputedMinBSize() > 0 ||
     aState->mReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE;
 }
 
 void
 nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState,
                                        bool aAssumeHScroll,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1242172-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<iframe src="1242172-1-subdoc.html" style="width:800px; height:200px; border:1px solid black"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1242172-1-subdoc.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html style="overflow:hidden">
+<body>
+<div style='position:absolute; height:100%; display:flex; flex-direction:row'>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1242172-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="doTest()">
+<iframe id="f" src="1242172-1-subdoc.html" style="width:800px; height:100px; border:1px solid black"></iframe>
+<script>
+function doTest() {
+  f.contentDocument.body.offsetTop;
+  f.style.height = "200px";
+}
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1242172-2-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<!-- The test forces scrollbars to appear. Using overflow:scroll here avoids the
+     iterative overflow:auto reflow logic. -->
+<html style="overflow:scroll">
+<body>
+<div style='position:absolute; height:100%; display:flex; flex-direction:row'>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1242172-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style='position:absolute; height:100%; display:flex; flex-direction:row'>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+  <div style="height:100%"><img src="repeatable-diagonal-gradient.png" style='height:100%'></img></div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1939,8 +1939,10 @@ fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 
 == 1209994-1.html 1209994-1-ref.html
 == 1209994-2.html 1209994-2-ref.html
 == 1209994-3.html 1209994-3-ref.html
 == 1209994-4.html 1209994-4-ref.html
 == 1222226-1.html 1222226-1-ref.html
 pref(layout.css.overflow-clip-box.enabled,true) == 1226278.html 1226278-ref.html
 == 1230466.html about:blank
 fuzzy(100,2000) == 1239564.html 1239564-ref.html
+== 1242172-1.html 1242172-1-ref.html
+== 1242172-2.html 1242172-2-ref.html