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
--- 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.