Bug 1475217 - Remove scroll position assertion in ScrollFrameTo. r?botond
There is a race between when APZ is made aware of a main thread change to a
frame's overflow property and when it decides whether to allow scrolling in
response to an input event. If the processing of the input event wins the race,
APZ can allow scrolling an element that has recently been made overflow:hidden.
The main thread previously asserted about the attempt to scroll an
overflow:hidden element, but since this can occur in practice, is relatively
benign, and is hard to avoid, we will now allow it.
MozReview-Commit-ID: IkH7xWyMOEl
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -14,25 +14,26 @@
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/TouchEvents.h"
+#include "nsContainerFrame.h"
#include "nsContentUtils.h"
-#include "nsContainerFrame.h"
+#include "nsIContent.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIDocument.h"
+#include "nsIInterfaceRequestorUtils.h"
#include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIContent.h"
-#include "nsIDocument.h"
-#include "nsIDOMWindow.h"
-#include "nsIDOMWindowUtils.h"
+#include "nsPrintfCString.h"
#include "nsRefreshDriver.h"
#include "nsString.h"
#include "nsView.h"
#include "Layers.h"
// #define APZCCH_LOGGING 1
#ifdef APZCCH_LOGGING
#define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
@@ -100,30 +101,48 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
// If the repaint request was triggered due to a previous main-thread scroll
// offset update sent to the APZ, then we don't need to do another scroll here
// and we can just return.
if (!aMetrics.GetScrollOffsetUpdated()) {
return geckoScrollPosition;
}
- // If this is the root content with overflow:hidden, then APZ should not
- // allow scrolling in such a way that moves the layout viewport.
+ // If this frame is overflow:hidden, then the expectation is that it was
+ // sized in a way that respects its scrollable boundaries. For the root
+ // frame, this means that it cannot be scrolled in such a way that it moves
+ // the layout viewport. For a non-root frame, this means that it cannot be
+ // scrolled at all.
//
- // If this is overflow:hidden, but not the root content, then
- // nsLayoutUtils::CalculateScrollableRectForFrame should have sized the
- // scrollable rect in a way that prevents APZ from scrolling it at all.
+ // In either case, |targetScrollPosition| should be the same as
+ // |geckoScrollPosition| here.
//
- // In either case, targetScrollPosition should be the same as
- // geckoScrollPosition here.
- if (aFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
- MOZ_ASSERT(targetScrollPosition.y == geckoScrollPosition.y);
+ // However, this is slightly racy. We query the overflow property of the
+ // scroll frame at the time the repaint request arrives at the main thread
+ // (i.e., right now), but APZ made the decision of whether or not to allow
+ // scrolling based on the information it had at the time it processed the
+ // scroll event. The overflow property could have changed at some time
+ // between the two events and so APZ may have computed a scrollable region
+ // that is larger than what is actually allowed.
+ //
+ // Currently, we allow the scroll position to change even though the frame is
+ // overflow:hidden (that is, we take |targetScrollPosition|). If this turns
+ // out to be problematic, an alternative solution would be to ignore the
+ // scroll position change (that is, use |geckoScrollPosition|).
+ if (aFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
+ targetScrollPosition.y != geckoScrollPosition.y) {
+ NS_WARNING(nsPrintfCString(
+ "APZCCH: targetScrollPosition.y (%f) != geckoScrollPosition.y (%f)",
+ targetScrollPosition.y, geckoScrollPosition.y).get());
}
- if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
- MOZ_ASSERT(targetScrollPosition.x == geckoScrollPosition.x);
+ if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
+ targetScrollPosition.x != geckoScrollPosition.x) {
+ NS_WARNING(nsPrintfCString(
+ "APZCCH: targetScrollPosition.x (%f) != geckoScrollPosition.x (%f)",
+ targetScrollPosition.x, geckoScrollPosition.x).get());
}
// 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 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 = APZCCallbackHelper::IsScrollInProgress(aFrame);