Bug 1475217 - Remove scroll position assertion in ScrollFrameTo. r?botond draft
authorKashav Madan <kmadan@mozilla.com>
Mon, 16 Jul 2018 12:40:59 -0400
changeset 818878 33f10bb573b1f37843f1f50043fab68d1a70c75f
parent 818682 2ed1506d1dc7db3d70a3feed95f1456bce05bbee
push id116387
push userbmo:kmadan@mozilla.com
push dateMon, 16 Jul 2018 19:35:59 +0000
reviewersbotond
bugs1475217
milestone63.0a1
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
gfx/layers/apz/util/APZCCallbackHelper.cpp
--- 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);