Bug 1324499 - Constrain height before calling DidDoReflow callbacks draft
authorangelsl <angelsl@in04.sg>
Mon, 21 Aug 2017 02:24:45 +0800
changeset 649783 0017f83e9fbc47636c26826a786acfec8282aa97
parent 649541 5ca5691372cb432ec1fa4693ca608a30858226de
child 727193 d7dcb1f79a3f3e012bf2aca95c0774af9e1bbe4f
push id75154
push userbmo:angelsl@in04.sg
push dateMon, 21 Aug 2017 09:51:26 +0000
bugs1324499
milestone57.0a1
Bug 1324499 - Constrain height before calling DidDoReflow callbacks The bug is caused by the first call to ResizeReflow in nsDocumentViewer::GetContentSizeInternal, which reflows the content with unlimited height. ResizeReflow calls DidDoReflow, which calls a callback installed by nsHTMLScrollFrame that clamps the scroll port setting the scroll top to 0 and losing the original scroll top. When the content is reflowed again to the maximum height, the scroll top stays at 0. Do the height limiting in PresShell::ResizeReflowIgnoreOverride instead, by first calling DoReflow with unlimited height, then checking the new height and reflowing again, before calling the DidDoReflow callbacks, so the scroll top is not lost. MozReview-Commit-ID: I3YH0kyujKg
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1891,40 +1891,49 @@ PresShell::sPaintSuppressionCallback(nsI
 
 void
 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
 {
   static_cast<PresShell*>(aPresShell)->FireResizeEvent();
 }
 
 nsresult
-PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight)
+PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth,
+                        nscoord aOldHeight, bool aHeightIsLimit)
 {
   if (mZoomConstraintsClient) {
     // If we have a ZoomConstraintsClient and the available screen area
     // changed, then we might need to disable double-tap-to-zoom, so notify
     // the ZCC to update itself.
     mZoomConstraintsClient->ScreenSizeChanged();
   }
   if (mMobileViewportManager) {
     // If we have a mobile viewport manager, request a reflow from it. It can
     // recompute the final CSS viewport and trigger a call to
     // ResizeReflowIgnoreOverride if it changed.
     mMobileViewportManager->RequestReflow();
     return NS_OK;
   }
 
-  return ResizeReflowIgnoreOverride(aWidth, aHeight, aOldWidth, aOldHeight);
+  return ResizeReflowIgnoreOverride(aWidth, aHeight, aOldWidth,
+                                    aOldHeight, aHeightIsLimit);
 }
 
 nsresult
-PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight)
+PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
+                                      nscoord aOldWidth, nscoord aOldHeight,
+                                      bool aHeightIsLimit)
 {
   NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
 
+  nscoord actualHeight = aHeight;
+  if (aHeightIsLimit) {
+    aHeight = NS_UNCONSTRAINEDSIZE;
+  }
+
   // If we don't have a root frame yet, that means we haven't had our initial
   // reflow... If that's the case, and aWidth or aHeight is unconstrained,
   // 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;
@@ -1977,16 +1986,23 @@ PresShell::ResizeReflowIgnoreOverride(ns
         WillDoReflow();
 
         // Kick off a top-down reflow
         AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
         nsViewManager::AutoDisableRefresh refreshBlocker(viewManager);
 
         mDirtyRoots.RemoveElement(rootFrame);
         DoReflow(rootFrame, true);
+
+        if (aHeightIsLimit &&
+            mPresContext->GetVisibleArea().height > actualHeight) {
+          aHeight = actualHeight;
+          mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
+          DoReflow(rootFrame, true);
+        }
       }
 
       DidDoReflow(true);
     }
   }
 
   rootFrame = mFrameConstructor->GetRootFrame();
   if (rootFrame) {
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -106,18 +106,22 @@ public:
   NS_IMETHOD ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
                                      SelectionRegion aRegion,
                                      int16_t aFlags) override;
   NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
 
   virtual void BeginObservingDocument() override;
   virtual void EndObservingDocument() override;
   virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) override;
-  virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0, nscoord aOldHeight = 0) override;
-  virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight) override;
+  virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight,
+                                nscoord aOldWidth = 0, nscoord aOldHeight = 0,
+                                bool aHeightIsLimit = false) override;
+  virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
+                                              nscoord aOldWidth, nscoord aOldHeight,
+                                              bool aHeightIsLimit = false) override;
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
   virtual nsCanvasFrame* GetCanvasFrame() const override;
 
   virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
                                 nsFrameState aBitToAdd,
                                 ReflowRootHandling aRootHandling =
                                   eInferFromBitToAdd) override;
   virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) override;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3563,34 +3563,25 @@ nsDocumentViewer::GetContentSizeInternal
   {
     RefPtr<gfxContext> rcx(presShell->CreateReferenceRenderingContext());
     prefWidth = root->GetPrefISize(rcx);
   }
   if (prefWidth > aMaxWidth) {
     prefWidth = aMaxWidth;
   }
 
-  nsresult rv = presShell->ResizeReflow(prefWidth, NS_UNCONSTRAINEDSIZE);
+  nsresult rv = presShell->ResizeReflow(prefWidth, aMaxHeight, 0, 0, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<nsPresContext> presContext;
   GetPresContext(getter_AddRefs(presContext));
   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 
-  // so how big is it?
+  // Protect against bogus returns here
   nsRect shellArea = presContext->GetVisibleArea();
-  if (shellArea.height > aMaxHeight) {
-    // Reflow to max height if we would up too tall.
-    rv = presShell->ResizeReflow(prefWidth, aMaxHeight);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    shellArea = presContext->GetVisibleArea();
-  }
-
-  // Protect against bogus returns here
   NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
                  shellArea.height != NS_UNCONSTRAINEDSIZE,
                  NS_ERROR_FAILURE);
 
   *aWidth = presContext->AppUnitsToDevPixels(shellArea.width);
   *aHeight = presContext->AppUnitsToDevPixels(shellArea.height);
 
   return NS_OK;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -361,22 +361,26 @@ public:
    * Calling Initialize can execute arbitrary script.
    */
   virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) = 0;
 
   /**
    * Reflow the frame model into a new width and height.  The
    * coordinates for aWidth and aHeight must be in standard nscoord's.
    */
-  virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0, nscoord aOldHeight = 0) = 0;
+  virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight,
+                                nscoord aOldWidth = 0, nscoord aOldHeight = 0,
+                                bool aHeightIsLimit = false) = 0;
   /**
    * Do the same thing as ResizeReflow but even if ResizeReflowOverride was
    * called previously.
    */
-  virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight) = 0;
+  virtual nsresult ResizeReflowIgnoreOverride(
+                     nscoord aWidth, nscoord aHeight, nscoord aOldWidth,
+                     nscoord aOldHeight, bool aHeightIsLimit = false) = 0;
 
   /**
    * Returns true if ResizeReflowOverride has been called.
    */
   virtual bool GetIsViewportOverridden() = 0;
 
   /**
    * Return true if the presshell expects layout flush.