Bug 1393116 - Move double-reflow for sizing-to-content one level lower r=dbaron

After the fix to bug 1294442 and bug 1324499, ResizeReflow began to be called
twice for each DOM update in webext popups, and we also artificially re-set the
scroll outside of ResizeReflow to counter the DidDoReflow callback in
nsHTMLScrollFrame setting scrolltop to zero due to the first reflow, which is
done with unconstrained height.

Because of the scrollport being reset we get spurious DOM scroll events.
Replacing the scrollport also interrupts smooth scrolling.

Move the double-reflow down one level into PresShell, doing it before
DidDoReflow is called. The scrollport is no longer reset (causing a spurious
scroll event), and we don't need to replace it (interrupting smooth scrolling).

Also partially fixes bug 1396034.

MozReview-Commit-ID: HzYITyH4UeW

--HG--
extra : rebase_source : 567056300bc81c9e4c197783f48636caf67cde34
extra : intermediate-source : ef7322dfd99d79a403bb0804638a46c70ece1b45
extra : source : 4f1761bb955473026b4deba76a5e76e93b7ede35
This commit is contained in:
angelsl 2017-09-09 00:38:54 +08:00
parent 71c9765002
commit bdd58219ae
4 changed files with 94 additions and 51 deletions

View File

@ -1868,7 +1868,8 @@ PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
}
nsresult
PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight)
PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth,
nscoord aOldHeight, ResizeReflowOptions aOptions)
{
if (mZoomConstraintsClient) {
// If we have a ZoomConstraintsClient and the available screen area
@ -1884,39 +1885,58 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nsco
return NS_OK;
}
return ResizeReflowIgnoreOverride(aWidth, aHeight, aOldWidth, aOldHeight);
return ResizeReflowIgnoreOverride(aWidth, aHeight, aOldWidth,
aOldHeight, aOptions);
}
nsresult
PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight)
PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
nscoord aOldWidth, nscoord aOldHeight,
ResizeReflowOptions aOptions)
{
NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
// 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 || aWidth == 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;
}
mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
// There isn't anything useful we can do if the initial reflow hasn't happened.
if (!rootFrame) {
// 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.
if (aHeight == NS_UNCONSTRAINEDSIZE || aWidth == 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;
}
mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
// There isn't anything useful we can do if the initial reflow hasn't
// happened.
return NS_OK;
}
WritingMode wm = rootFrame->GetWritingMode();
NS_PRECONDITION((wm.IsVertical() ? aHeight : aWidth) != NS_UNCONSTRAINEDSIZE,
"shouldn't use unconstrained isize anymore");
const bool shrinkToFit = aOptions == ResizeReflowOptions::eBSizeLimit;
NS_PRECONDITION(shrinkToFit ||
(wm.IsVertical() ? aWidth : aHeight) !=
NS_UNCONSTRAINEDSIZE,
"unconstrained bsize only usable with eBSizeLimit");
NS_PRECONDITION((wm.IsVertical() ? aHeight : aWidth) !=
NS_UNCONSTRAINEDSIZE,
"unconstrained isize not allowed");
bool isBSizeChanging = wm.IsVertical() ? aOldWidth != aWidth
: aOldHeight != aHeight;
nscoord targetWidth = aWidth;
nscoord targetHeight = aHeight;
const bool isBSizeChanging = wm.IsVertical()
? aOldWidth != aWidth
: aOldHeight != aHeight;
if (shrinkToFit) {
if (wm.IsVertical()) {
targetWidth = NS_UNCONSTRAINEDSIZE;
} else {
targetHeight = NS_UNCONSTRAINEDSIZE;
}
isBSizeChanging = true;
}
mPresContext->SetVisibleArea(nsRect(0, 0, targetWidth, targetHeight));
RefPtr<nsViewManager> viewManager = mViewManager;
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
@ -1955,8 +1975,29 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord a
mDirtyRoots.RemoveElement(rootFrame);
DoReflow(rootFrame, true);
if (shrinkToFit) {
const bool reflowAgain = wm.IsVertical() ?
mPresContext->GetVisibleArea().width > aWidth :
mPresContext->GetVisibleArea().height > aHeight;
if (reflowAgain) {
mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
DoReflow(rootFrame, true);
}
}
}
// the first DoReflow above should've set our bsize if it was
// NS_UNCONSTRAINEDSIZE, and the isize shouldn't be NS_UNCONSTRAINEDSIZE
// anyway
NS_ASSERTION(
mPresContext->GetVisibleArea().width != NS_UNCONSTRAINEDSIZE,
"width should not be NS_UNCONSTRAINEDSIZE after reflow");
NS_ASSERTION(
mPresContext->GetVisibleArea().height != NS_UNCONSTRAINEDSIZE,
"height should not be NS_UNCONSTRAINEDSIZE after reflow");
DidDoReflow(true);
}
}
@ -1964,13 +2005,16 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord a
rootFrame = mFrameConstructor->GetRootFrame();
if (rootFrame) {
wm = rootFrame->GetWritingMode();
// reflow did not happen; if the reflow happened, our bsize should not be
// NS_UNCONSTRAINEDSIZE because DoReflow will fix it up to the same values
// as below
if (wm.IsVertical()) {
if (aWidth == NS_UNCONSTRAINEDSIZE) {
if (mPresContext->GetVisibleArea().width == NS_UNCONSTRAINEDSIZE) {
mPresContext->SetVisibleArea(
nsRect(0, 0, rootFrame->GetRect().width, aHeight));
}
} else {
if (aHeight == NS_UNCONSTRAINEDSIZE) {
if (mPresContext->GetVisibleArea().height == NS_UNCONSTRAINEDSIZE) {
mPresContext->SetVisibleArea(
nsRect(0, 0, aWidth, rootFrame->GetRect().height));
}

View File

@ -111,8 +111,14 @@ public:
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,
ResizeReflowOptions aOptions =
ResizeReflowOptions::eBSizeExact) override;
virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
nscoord aOldWidth, nscoord aOldHeight,
ResizeReflowOptions aOptions =
ResizeReflowOptions::eBSizeExact) override;
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
virtual nsCanvasFrame* GetCanvasFrame() const override;

View File

@ -3508,37 +3508,16 @@ nsDocumentViewer::GetContentSizeInternal(int32_t* aWidth, int32_t* aHeight,
prefWidth = aMaxWidth;
}
nsAutoPtr<nsPresState> frameState;
nsIScrollableFrame *scrollFrame = presShell->GetRootScrollFrameAsScrollable();
nsIStatefulFrame *statefulFrame = do_QueryFrame(scrollFrame);
if (statefulFrame) {
statefulFrame->SaveState(getter_Transfers(frameState));
}
nsresult rv = presShell->ResizeReflow(prefWidth, NS_UNCONSTRAINEDSIZE);
nsresult rv = presShell->ResizeReflow(prefWidth, aMaxHeight, 0, 0,
nsIPresShell::ResizeReflowOptions::eBSizeLimit);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsPresContext> presContext;
GetPresContext(getter_AddRefs(presContext));
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
// so how big is it?
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();
// the first reflow reset our scroll, now set it back
if (frameState && presShell->GetRootScrollFrameAsScrollable() == scrollFrame) {
statefulFrame->RestoreState(frameState);
scrollFrame->ScrollToRestoredPosition();
}
}
// Protect against bogus returns here
nsRect shellArea = presContext->GetVisibleArea();
NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
shellArea.height != NS_UNCONSTRAINEDSIZE,
NS_ERROR_FAILURE);

View File

@ -359,16 +359,30 @@ public:
*/
virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) = 0;
enum class ResizeReflowOptions : uint32_t {
// the resulting BSize should be exactly as given
eBSizeExact,
// the resulting BSize can be less than the given one, producing
// shrink-to-fit sizing in the block dimension
eBSizeLimit
};
/**
* 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,
ResizeReflowOptions aOptions =
ResizeReflowOptions::eBSizeExact) = 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,
ResizeReflowOptions aOptions =
ResizeReflowOptions::eBSizeExact) = 0;
/**
* Returns true if ResizeReflowOverride has been called.