Bug 1235899 - Don't allow frame reconstruction to clobber the APZ scroll offset. r=botond

MozReview-Commit-ID: HZMavMJNtmq
This commit is contained in:
Kartikaya Gupta 2016-02-10 16:31:20 -05:00
parent 5c388980b8
commit ce22853404
5 changed files with 39 additions and 10 deletions

View File

@ -104,7 +104,6 @@ GK_ATOM(applet, "applet")
GK_ATOM(applyImports, "apply-imports")
GK_ATOM(applyTemplates, "apply-templates")
GK_ATOM(mozapptype, "mozapptype")
GK_ATOM(apz, "apz")
GK_ATOM(archive, "archive")
GK_ATOM(area, "area")
GK_ATOM(arrow, "arrow")
@ -2270,13 +2269,20 @@ GK_ATOM(SendMail, "SendMail")
GK_ATOM(ForwardMail, "ForwardMail")
GK_ATOM(ReplyToMail, "ReplyToMail")
// Smooth scroll events origins
// Scroll origins (these are used in various scrolling functions in
// nsIScrollableFrame and ScrollFrameHelper). These are divided into two lists
// - origins in the first one have smooth-scrolling prefs associated with them,
// under the "general.smoothScroll.<origin>.*" pref branch. Origins in the
// second one do not.
GK_ATOM(mouseWheel, "mouseWheel") // For discrete wheel events (e.g. not OSX magic mouse)
GK_ATOM(pixels, "pixels")
GK_ATOM(lines, "lines")
GK_ATOM(pages, "pages")
GK_ATOM(scrollbars, "scrollbars")
GK_ATOM(other, "other")
// Scroll origins without smooth-scrolling prefs
GK_ATOM(apz, "apz")
GK_ATOM(restore, "restore")
#ifdef ACCESSIBILITY
GK_ATOM(alert, "alert")

View File

@ -92,11 +92,11 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const CSSPoint& aPoint, bool& aSuccess
// If the scrollable frame is currently in the middle of an async or smooth
// scroll then we don't want to interrupt it (see bug 961280).
// Also if the scrollable frame got a scroll request from something other than us
// Also if the scrollable frame got a scroll request from a higher priority origin
// since the last layers update, then we don't want to push our scroll request
// because we'll clobber that one, which is bad.
bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
|| (aFrame->LastScrollOrigin() && aFrame->LastScrollOrigin() != nsGkAtoms::apz)
|| nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
|| aFrame->LastSmoothScrollOrigin();
if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);

View File

@ -8581,6 +8581,14 @@ nsLayoutUtils::SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell,
MaybeReflowForInflationScreenSizeChange(presContext);
}
/* static */ bool
nsLayoutUtils::CanScrollOriginClobberApz(nsIAtom* aScrollOrigin)
{
return aScrollOrigin != nullptr
&& aScrollOrigin != nsGkAtoms::apz
&& aScrollOrigin != nsGkAtoms::restore;
}
/* static */ FrameMetrics
nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame,
nsIFrame* aScrollFrame,
@ -8637,11 +8645,10 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame,
nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
// If the frame was scrolled since the last layers update, and by
// something other than the APZ code, we want to tell the APZ to update
// If the frame was scrolled since the last layers update, and by something
// that is higher priority than APZ, we want to tell the APZ to update
// its scroll offset.
nsIAtom* lastScrollOrigin = scrollableFrame->LastScrollOrigin();
if (lastScrollOrigin && lastScrollOrigin != nsGkAtoms::apz) {
if (CanScrollOriginClobberApz(scrollableFrame->LastScrollOrigin())) {
metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
}
nsIAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();

View File

@ -2722,6 +2722,15 @@ public:
static void SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell,
CSSSize aSize);
/**
* Returns true if the given scroll origin is "higher priority" than APZ.
* In general any content programmatic scrolls (e.g. scrollTo calls) are
* higher priority, and take precedence over APZ scrolling. This function
* returns true for those, and returns false for other origins like APZ
* itself, or scroll position updates from the history restore code.
*/
static bool CanScrollOriginClobberApz(nsIAtom* aScrollOrigin);
static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame,
nsIFrame* aScrollFrame,
nsIContent* aContent,

View File

@ -1725,9 +1725,15 @@ private:
void
ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin)
{
if (!aOrigin){
if (!aOrigin || aOrigin == nsGkAtoms::restore) {
// We don't have special prefs for "restore", just treat it as "other".
// "restore" scrolls are (for now) always instant anyway so unless something
// changes we should never have aOrigin == nsGkAtoms::restore here.
aOrigin = nsGkAtoms::other;
}
// Likewise we should never get APZ-triggered scrolls here, and if that changes
// something is likely broken somewhere.
MOZ_ASSERT(aOrigin != nsGkAtoms::apz);
// Read preferences only on first iteration or for a different event origin.
if (!mIsFirstIteration && aOrigin == mOrigin) {
@ -3938,7 +3944,8 @@ ScrollFrameHelper::ScrollToRestoredPosition()
scrollToPos.x = mScrollPort.x -
(mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
nsWeakFrame weakFrame(mOuter);
ScrollTo(scrollToPos, nsIScrollableFrame::INSTANT);
ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
nsGkAtoms::restore, nullptr);
if (!weakFrame.IsAlive()) {
return;
}