Bug 1257269 - Panning up in a scrollable element should not hide the toolbar r=kats,jchen
☠☠ backed out by 3ed189d26cad ☠ ☠
authorRandall Barker <rbarker@mozilla.com>
Thu, 24 Mar 2016 15:00:27 -0700
changeset 291416 95e61ede373da7ae3755ca149da42ff873e05a42
parent 291415 5620c5785d8e80ce0f50fa557d14a134741e1c02
child 291417 cb2023f50288a63554771510ff603df10c403180
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats, jchen
bugs1257269
milestone48.0a1
Bug 1257269 - Panning up in a scrollable element should not hide the toolbar r=kats,jchen
gfx/layers/apz/public/GeckoContentController.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java
widget/android/AndroidContentController.cpp
widget/android/AndroidContentController.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
widget/android/nsWindow.h
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -147,17 +147,18 @@ public:
   {}
 
   /**
    * Notify content that the repaint requests have been flushed.
    */
   virtual void NotifyFlushComplete() = 0;
 
   virtual void UpdateOverscrollVelocity(const float aX, const float aY) {}
-  virtual void UpdateOverscrollOffset(const float aX,const  float aY) {}
+  virtual void UpdateOverscrollOffset(const float aX, const float aY) {}
+  virtual void SetScrollingRootContent(const bool isRootContent) {}
 
   GeckoContentController() {}
   virtual void ChildAdopted() {}
   /**
    * Needs to be called on the main thread.
    */
   virtual void Destroy() {}
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1314,16 +1314,21 @@ nsEventStatus AsyncPanZoomController::On
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-end in state %d\n", this, mState);
 
+  RefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
+    controller->SetScrollingRootContent(false);
+  }
+
   OnTouchEndOrCancel();
 
   // In case no touch behavior triggered previously we can avoid sending
   // scroll events or requesting content repaint. This condition is added
   // to make tests consistent - in case touch-action is NONE (and therefore
   // no pans/zooms can be performed) we expected neither scroll or repaint
   // events.
   if (mState != NOTHING) {
@@ -2378,17 +2383,23 @@ bool AsyncPanZoomController::AttemptScro
     bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
 
     if (xChanged || yChanged) {
       ScheduleComposite();
     }
 
     if (!IsZero(adjustedDisplacement)) {
       ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
-      if (InputBlockState* block = CurrentInputBlock()) {
+      if (CancelableBlockState* block = CurrentInputBlock()) {
+        if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) {
+          RefPtr<GeckoContentController> controller = GetGeckoContentController();
+          if (controller) {
+            controller->SetScrollingRootContent(IsRootContent());
+          }
+        }
         block->SetScrolledApzc(this);
       }
       ScheduleCompositeAndMaybeRepaint();
       UpdateSharedCompositorFrameMetrics();
     }
 
     // Adjust the start point to reflect the consumed portion of the scroll.
     aStartPoint = aEndPoint + overscroll;
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -1,15 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.gfx;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.graphics.PointF;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.animation.DecelerateInterpolator;
@@ -89,30 +90,38 @@ public class DynamicToolbarAnimator {
     /* The task that handles showing/hiding toolbar */
     private DynamicToolbarAnimationTask mAnimationTask;
 
     /* The start point of a drag, used for scroll-based dynamic toolbar
      * behaviour. */
     private PointF mTouchStart;
     private float mLastTouch;
 
+    /* Set to true when root content is being scrolled */
+    private boolean mScrollingRootContent;
+
     public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
         mTarget = aTarget;
         mListeners = new ArrayList<LayerView.DynamicToolbarListener>();
 
         mInterpolator = new DecelerateInterpolator();
 
         // Listen to the dynamic toolbar pref
         mPrefObserver = new PrefsHelper.PrefHandlerBase() {
             @Override
             public void prefValue(String pref, int value) {
                 SCROLL_TOOLBAR_THRESHOLD = value / 100.0f;
             }
         };
         PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver);
+
+        // JPZ doesn't notify when scrolling root content. This maintains existing behaviour.
+        if (!AppConstants.MOZ_ANDROID_APZ) {
+            mScrollingRootContent = true;
+        }
     }
 
     public void destroy() {
         PrefsHelper.removeObserver(mPrefObserver);
     }
 
     public void addTranslationListener(LayerView.DynamicToolbarListener aListener) {
         mListeners.add(aListener);
@@ -180,16 +189,20 @@ public class DynamicToolbarAnimator {
     public void showToolbar(boolean immediately) {
         animateToolbar(true, immediately);
     }
 
     public void hideToolbar(boolean immediately) {
         animateToolbar(false, immediately);
     }
 
+    public void setScrollingRootContent(boolean isRootContent) {
+        mScrollingRootContent = isRootContent;
+    }
+
     private void animateToolbar(final boolean showToolbar, boolean immediately) {
         ThreadUtils.assertOnUiThread();
 
         if (mAnimationTask != null) {
             mTarget.getView().removeRenderTask(mAnimationTask);
             mAnimationTask = null;
         }
 
@@ -334,21 +347,22 @@ public class DynamicToolbarAnimator {
         if (translation < 0) { // finger moving upwards
             translation = shrinkAbs(translation, aMetrics.getOverscroll().top);
 
             // If the toolbar is in a state between fully hidden and fully shown
             // (i.e. the user is actively translating it), then we want the
             // translation to take effect right away. Or if the user has moved
             // their finger past the required threshold (and is not trying to
             // scroll past the bottom of the page) then also we want the touch
-            // to cause translation.
+            // to cause translation. If the toolbar is fully visible, we only
+            // want the toolbar to hide if the user is scrolling the root content.
             boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation);
             boolean reachedThreshold = -aTouchTravelDistance >= exposeThreshold;
             boolean atBottomOfPage = aMetrics.viewportRectBottom() >= aMetrics.pageRectBottom;
-            if (inBetween || (reachedThreshold && !atBottomOfPage)) {
+            if (inBetween || (mScrollingRootContent && reachedThreshold && !atBottomOfPage)) {
                 return translation;
             }
         } else {    // finger moving downwards
             translation = shrinkAbs(translation, aMetrics.getOverscroll().bottom);
 
             // Ditto above comment, but in this case if they reached the top and
             // the toolbar is not shown, then we do want to allow translation
             // right away.
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -1081,16 +1081,21 @@ class GeckoLayerClient implements LayerV
         // the current Gecko coordinate in CSS pixels.
         PointF layerPoint = new PointF(
                 ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom),
                 ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom));
 
         return layerPoint;
     }
 
+    @Override
+    public void setScrollingRootContent(boolean isRootContent) {
+        mToolbarAnimator.setScrollingRootContent(isRootContent);
+    }
+
     public void addDrawListener(DrawListener listener) {
         mDrawListeners.add(listener);
     }
 
     public void removeDrawListener(DrawListener listener) {
         mDrawListeners.remove(listener);
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -319,16 +319,21 @@ class NativePanZoomController extends JN
                         mOverscroll.setDistance(x, Overscroll.Axis.X);
                         mOverscroll.setDistance(y, Overscroll.Axis.Y);
                     }
                 });
             }
         }
     }
 
+    @WrapForJNI
+    private void setScrollingRootContent(final boolean isRootContent) {
+        mTarget.setScrollingRootContent(isRootContent);
+    }
+
     /**
      * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned
      * to avoid unwanted scroll interactions.
      */
     @WrapForJNI
     private void onSelectionDragState(boolean state) {
         mView.getDynamicToolbarAnimator().setPinned(state, PinReason.CARET_DRAG);
     }
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java
@@ -23,9 +23,10 @@ public interface PanZoomTarget {
     /** This triggers an (asynchronous) viewport update/redraw. */
     public void forceRedraw(DisplayPortMetrics displayPort);
 
     public boolean post(Runnable action);
     public void postRenderTask(RenderTask task);
     public void removeRenderTask(RenderTask task);
     public Object getLock();
     public PointF convertViewPointToLayerPoint(PointF viewPoint);
+    public void setScrollingRootContent(boolean isRootContent);
 }
--- a/widget/android/AndroidContentController.cpp
+++ b/widget/android/AndroidContentController.cpp
@@ -95,24 +95,32 @@ void
 AndroidContentController::UpdateOverscrollVelocity(const float aX, const float aY)
 {
   if (mAndroidWindow) {
     mAndroidWindow->UpdateOverscrollVelocity(aX, aY);
   }
 }
 
 void
-AndroidContentController::UpdateOverscrollOffset(const float aX,const  float aY)
+AndroidContentController::UpdateOverscrollOffset(const float aX, const float aY)
 {
   if (mAndroidWindow) {
     mAndroidWindow->UpdateOverscrollOffset(aX, aY);
   }
 }
 
 void
+AndroidContentController::SetScrollingRootContent(const bool isRootContent)
+{
+  if (mAndroidWindow) {
+    mAndroidWindow->SetScrollingRootContent(isRootContent);
+  }
+}
+
+void
 AndroidContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                                APZStateChange aChange,
                                                int aArg)
 {
   // This function may get invoked twice, if the first invocation is not on
   // the main thread then the ChromeProcessController version of this function
   // will redispatch to the main thread. We want to make sure that our handling
   // only happens on the main thread.
--- a/widget/android/AndroidContentController.h
+++ b/widget/android/AndroidContentController.h
@@ -34,17 +34,18 @@ public:
 
     // ChromeProcessController methods
     virtual void Destroy() override;
     void HandleSingleTap(const CSSPoint& aPoint,
                          Modifiers aModifiers,
                          const ScrollableLayerGuid& aGuid) override;
     void PostDelayedTask(Task* aTask, int aDelayMs) override;
     void UpdateOverscrollVelocity(const float aX, const float aY) override;
-    void UpdateOverscrollOffset(const float aX,const  float aY) override;
+    void UpdateOverscrollOffset(const float aX, const float aY) override;
+    void SetScrollingRootContent(const bool isRootContent) override;
     void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                               APZStateChange aChange,
                               int aArg) override;
 
     static void NotifyDefaultPrevented(mozilla::layers::APZCTreeManager* aManager,
                                        uint64_t aInputBlockId, bool aDefaultPrevented);
 private:
     nsWindow* mAndroidWindow;
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1570,16 +1570,24 @@ auto NativePanZoomController::OnSelectio
 constexpr char NativePanZoomController::RequestContentRepaintWrapper_t::name[];
 constexpr char NativePanZoomController::RequestContentRepaintWrapper_t::signature[];
 
 auto NativePanZoomController::RequestContentRepaintWrapper(float a0, float a1, float a2, float a3, float a4) const -> void
 {
     return mozilla::jni::Method<RequestContentRepaintWrapper_t>::Call(NativePanZoomController::mCtx, nullptr, a0, a1, a2, a3, a4);
 }
 
+constexpr char NativePanZoomController::SetScrollingRootContent_t::name[];
+constexpr char NativePanZoomController::SetScrollingRootContent_t::signature[];
+
+auto NativePanZoomController::SetScrollingRootContent(bool a0) const -> void
+{
+    return mozilla::jni::Method<SetScrollingRootContent_t>::Call(NativePanZoomController::mCtx, nullptr, a0);
+}
+
 constexpr char NativePanZoomController::UpdateOverscrollOffset_t::name[];
 constexpr char NativePanZoomController::UpdateOverscrollOffset_t::signature[];
 
 auto NativePanZoomController::UpdateOverscrollOffset(float a0, float a1) const -> void
 {
     return mozilla::jni::Method<UpdateOverscrollOffset_t>::Call(NativePanZoomController::mCtx, nullptr, a0, a1);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -3776,16 +3776,32 @@ public:
                 "(FFFFF)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     auto RequestContentRepaintWrapper(float, float, float, float, float) const -> void;
 
+    struct SetScrollingRootContent_t {
+        typedef NativePanZoomController Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                bool> Args;
+        static constexpr char name[] = "setScrollingRootContent";
+        static constexpr char signature[] =
+                "(Z)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    auto SetScrollingRootContent(bool) const -> void;
+
     struct UpdateOverscrollOffset_t {
         typedef NativePanZoomController Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 float,
                 float> Args;
         static constexpr char name[] = "updateOverscrollOffset";
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -812,16 +812,21 @@ public:
         mNPZC->UpdateOverscrollVelocity(x, y);
     }
 
     void UpdateOverscrollOffset(const float x, const float y)
     {
         mNPZC->UpdateOverscrollOffset(x, y);
     }
 
+    void SetScrollingRootContent(const bool isRootContent)
+    {
+        mNPZC->SetScrollingRootContent(isRootContent);
+    }
+
     void SetSelectionDragState(const bool aState)
     {
         mNPZC->OnSelectionDragState(aState);
     }
 };
 
 /**
  * GLController has some unique requirements for its native calls, so make it
@@ -1872,16 +1877,27 @@ void
 nsWindow::UpdateOverscrollOffset(const float aX, const float aY)
 {
     if (mNPZCSupport) {
         mNPZCSupport->UpdateOverscrollOffset(aX, aY);
     }
 }
 
 void
+nsWindow::SetScrollingRootContent(const bool isRootContent)
+{
+    // On Android, the Controller thread and UI thread are the same.
+    MOZ_ASSERT(APZThreadUtils::IsControllerThread(), "nsWindow::SetScrollingRootContent must be called from the controller thread");
+
+    if (mNPZCSupport) {
+        mNPZCSupport->SetScrollingRootContent(isRootContent);
+    }
+}
+
+void
 nsWindow::SetSelectionDragState(bool aState)
 {
     if (mNPZCSupport) {
         mNPZCSupport->SetSelectionDragState(aState);
     }
 }
 
 void *
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -74,16 +74,17 @@ public:
 
     void OnSizeChanged(const mozilla::gfx::IntSize& aSize);
 
     void InitEvent(mozilla::WidgetGUIEvent& event,
                    LayoutDeviceIntPoint* aPoint = 0);
 
     void UpdateOverscrollVelocity(const float aX, const float aY);
     void UpdateOverscrollOffset(const float aX, const float aY);
+    void SetScrollingRootContent(const bool isRootContent);
 
     //
     // nsIWidget
     //
 
     using nsBaseWidget::Create; // for Create signature not overridden here
     NS_IMETHOD Create(nsIWidget* aParent,
                       nsNativeWidget aNativeParent,