diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 81ec7c33b56..ea23b50c0f5 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -1267,6 +1267,16 @@ APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid) } } +void +APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) +{ + MonitorAutoLock lock(mTreeLock); + RefPtr apzc = FindRootContentOrRootApzc(); + if (apzc) { + apzc->AdjustScrollForSurfaceShift(aShift); + } +} + void APZCTreeManager::ClearTree() { @@ -1742,6 +1752,33 @@ APZCTreeManager::FindRootContentApzcForLayersId(uint64_t aLayersId) const return resultNode ? resultNode->GetApzc() : nullptr; } +AsyncPanZoomController* +APZCTreeManager::FindRootContentOrRootApzc() const +{ + mTreeLock.AssertCurrentThreadOwns(); + + // Note: this is intended to find the same "root" that would be found + // by AsyncCompositionManager::ApplyAsyncContentTransformToTree inside + // the MOZ_ANDROID_APZ block. That is, it should find the RCD node if there + // is one, or the root APZC if there is not. + // Since BreadthFirstSearch is a pre-order search, we first do a search for + // the RCD, and then if we don't find one, we do a search for the root APZC. + HitTestingTreeNode* resultNode = BreadthFirstSearch(mRootNode.get(), + [](HitTestingTreeNode* aNode) { + AsyncPanZoomController* apzc = aNode->GetApzc(); + return apzc && apzc->IsRootContent(); + }); + if (resultNode) { + return resultNode->GetApzc(); + } + resultNode = BreadthFirstSearch(mRootNode.get(), + [](HitTestingTreeNode* aNode) { + AsyncPanZoomController* apzc = aNode->GetApzc(); + return (apzc != nullptr); + }); + return resultNode ? resultNode->GetApzc() : nullptr; +} + /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return some useful transformations that input events may need applied. This is best illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index d0227aa28b5..56e70ead86a 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -261,6 +261,14 @@ public: */ void CancelAnimation(const ScrollableLayerGuid &aGuid); + /** + * Adjusts the root APZC to compensate for a shift in the surface. See the + * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for + * some more details. This is only currently needed due to surface shifts + * caused by the dynamic toolbar on Android. + */ + void AdjustScrollForSurfaceShift(const ScreenPoint& aShift); + /** * Calls Destroy() on all APZC instances attached to the tree, and resets the * tree back to empty. This function may be called multiple times during the @@ -450,6 +458,7 @@ private: HitTestResult* aOutHitResult); AsyncPanZoomController* FindRootApzcForLayersId(uint64_t aLayersId) const; AsyncPanZoomController* FindRootContentApzcForLayersId(uint64_t aLayersId) const; + AsyncPanZoomController* FindRootContentOrRootApzc() const; already_AddRefed GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; already_AddRefed CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; already_AddRefed GetTouchInputBlockAPZC(const MultiTouchInput& aEvent, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index a20f4241bf9..97d12679b0a 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2639,6 +2639,19 @@ void AsyncPanZoomController::ShareFrameMetricsAcrossProcesses() { mSharingFrameMetricsAcrossProcesses = true; } +void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) +{ + ReentrantMonitorAutoEnter lock(mMonitor); + CSSPoint adjustment = + ViewAs(aShift, PixelCastJustification::ScreenIsParentLayerForRoot) + / mFrameMetrics.GetZoom(); + APZC_LOG("%p adjusting scroll position by %s for surface shift\n", + this, Stringify(adjustment).c_str()); + mFrameMetrics.ScrollBy(adjustment); + ScheduleCompositeAndMaybeRepaint(); + UpdateSharedCompositorFrameMetrics(); +} + void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) { mFrameMetrics.ScrollBy(aOffset); } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index ea936a74c21..5f2d0af6e1b 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -315,6 +315,15 @@ public: */ void CancelAnimation(CancelAnimationFlags aFlags = Default); + /** + * Adjusts the scroll position to compensate for a shift in the surface, such + * that the content appears to remain visually in the same position. i.e. if + * the surface moves up by 10 screenpixels, the scroll position should also + * move up by 10 pixels so that what used to be at the top of the surface is + * now 10 pixels down the surface. + */ + void AdjustScrollForSurfaceShift(const ScreenPoint& aShift); + /** * Clear any overscroll on this APZC. */ diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 23dd13e1790..d7531de9728 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -226,7 +226,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget } mViewportMetrics = mViewportMetrics.setViewportSize(width, height); if (scrollChange != null) { - mViewportMetrics = mViewportMetrics.offsetViewportByAndClamp(scrollChange.x, scrollChange.y); + mViewportMetrics = mPanZoomController.adjustScrollForSurfaceShift(mViewportMetrics, scrollChange); } if (mGeckoIsReady) { diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java index 04d3feb0e98..91152e03d0f 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java @@ -1467,4 +1467,9 @@ class JavaPanZoomController public void setOverscrollHandler(final Overscroll handler) { mOverscroll = handler; } + + @Override + public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) { + return aMetrics.offsetViewportByAndClamp(aShift.x, aShift.y); + } } diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java index c51a21dd019..f4042e7fac2 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -184,6 +184,15 @@ class NativePanZoomController extends JNIObject implements PanZoomController { } } + @WrapForJNI(stubName = "AdjustScrollForSurfaceShift") + private native void adjustScrollForSurfaceShift(float aX, float aY); + + @Override // PanZoomController + public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) { + adjustScrollForSurfaceShift(aShift.x, aShift.y); + return aMetrics; + } + @WrapForJNI(allowMultithread = true) private void updateOverscrollVelocity(final float x, final float y) { if (mOverscroll != null) { diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java index 9a8873448cb..8994fc404e2 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java @@ -51,4 +51,6 @@ public interface PanZoomController { public void setOverscrollHandler(final Overscroll controller); public void setIsLongpressEnabled(boolean isLongpressEnabled); + + public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift); } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 596f5ad2c2e..b626fbeb625 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -6044,8 +6044,10 @@ var ViewportHandler = { let scrollChange = JSON.parse(aData); let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); windowUtils.setNextPaintSyncId(scrollChange.id); - let win = BrowserApp.selectedTab.browser.contentWindow; - win.scrollBy(scrollChange.x, scrollChange.y); + if (!AppConstants.MOZ_ANDROID_APZ) { + let win = BrowserApp.selectedTab.browser.contentWindow; + win.scrollBy(scrollChange.x, scrollChange.y); + } } }, diff --git a/widget/android/GeneratedJNINatives.h b/widget/android/GeneratedJNINatives.h index 4e6afd0602e..da06b5a88bd 100644 --- a/widget/android/GeneratedJNINatives.h +++ b/widget/android/GeneratedJNINatives.h @@ -274,6 +274,10 @@ class NativePanZoomController::Natives : public mozilla::jni::NativeImpl( + mozilla::jni::NativeStub + ::template Wrap<&Impl::AdjustScrollForSurfaceShift>), + mozilla::jni::MakeNativeMethod( mozilla::jni::NativeStub ::template Wrap<&Impl::DisposeNative>), diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index 8cdd4f0d19d..bebd1b442ff 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1442,6 +1442,9 @@ auto LayerView::updateZoomedView(mozilla::jni::Object::Param a0) -> void constexpr char NativePanZoomController::name[]; +constexpr char NativePanZoomController::AdjustScrollForSurfaceShift_t::name[]; +constexpr char NativePanZoomController::AdjustScrollForSurfaceShift_t::signature[]; + constexpr char NativePanZoomController::Destroy_t::name[]; constexpr char NativePanZoomController::Destroy_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index ce96c3a289d..f95e923e68c 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3981,6 +3981,23 @@ public: protected: NativePanZoomController(jobject instance) : Class(instance) {} +public: + struct AdjustScrollForSurfaceShift_t { + typedef NativePanZoomController Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + float, + float> Args; + static constexpr char name[] = "adjustScrollForSurfaceShift"; + static constexpr char signature[] = + "(FF)V"; + static const bool isStatic = false; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + public: struct Destroy_t { typedef NativePanZoomController Owner; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index b8108c28642..58ec51013b7 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -490,6 +490,23 @@ public: } } + void AdjustScrollForSurfaceShift(float aX, float aY) + { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + + MutexAutoLock lock(mWindowLock); + if (!mWindow) { + // We already shut down. + return; + } + + RefPtr controller = mWindow->mAPZC; + if (controller) { + controller->AdjustScrollForSurfaceShift( + ScreenPoint(aX, aY)); + } + } + void SetIsLongpressEnabled(bool aIsLongpressEnabled) { MOZ_ASSERT(AndroidBridge::IsJavaUiThread());