Bug 1229462 - Use Android OverScroller class for fling animation. r=botond, a=sylvestre
authorRandall Barker <rbarker@mozilla.com>
Mon, 23 May 2016 15:58:00 +0200
changeset 333203 2d453fe195446be7ee2a26c05bd7deca5e0cd170
parent 333202 bb81bf646c3d92abb37eb6b5b5498cd2dab9175f
child 333204 1668d75e0df414b129ed82d93d20a8bb1d02c389
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond, sylvestre
bugs1229462
milestone48.0a2
Bug 1229462 - Use Android OverScroller class for fling animation. r=botond, a=sylvestre
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/FlingOverScrollerAnimation.cpp
gfx/layers/apz/src/FlingOverScrollerAnimation.h
gfx/layers/client/SingleTiledContentClient.cpp
gfx/layers/moz.build
mobile/android/app/mobile.js
mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/bindings/OverScroller-classes.txt
widget/android/bindings/moz.build
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -67,16 +67,20 @@
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"              // for nsTimingFunction
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "prsystem.h"                   // for PR_GetPhysicalMemorySize
 #include "SharedMemoryBasic.h"          // for SharedMemoryBasic
 #include "ScrollSnap.h"                 // for ScrollSnapUtils
 #include "WheelScrollAnimation.h"
+#if defined(MOZ_ANDROID_APZ)
+#include "GeneratedJNIWrappers.h"
+#include "FlingOverScrollerAnimation.h"
+#endif // defined(MOZ_ANDROID_APZ)
 
 #define ENABLE_APZC_LOGGING 0
 // #define ENABLE_APZC_LOGGING 1
 
 #if ENABLE_APZC_LOGGING
 #  define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
 #  define APZC_LOG_FM(fm, prefix, ...) \
     { std::stringstream ss; \
@@ -216,31 +220,34 @@ using mozilla::gfx::PointTyped;
  * Some example values for these prefs can be found at\n
  * http://mxr.mozilla.org/mozilla-central/source/layout/style/nsStyleStruct.cpp?rev=21282be9ad95#2462
  *
  * \li\b apz.fling_friction
  * Amount of friction applied during flings. This is used in the following
  * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
  * for a new sample, v(t0) is the velocity at the previous sample, f is the
  * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
- * that has elapsed between the two samples.
+ * that has elapsed between the two samples.\n
+ * NOTE: Not currently used in Android fling calculations.
  *
  * \li\b apz.fling_stop_on_tap_threshold
  * When flinging, if the velocity is above this number, then a tap on the
  * screen will stop the fling without dispatching a tap to content. If the
  * velocity is below this threshold a tap will also be dispatched.
  * Note: when modifying this pref be sure to run the APZC gtests as some of
  * them depend on the value of this pref.\n
  * Units: screen pixels per millisecond
  *
  * \li\b apz.fling_stopped_threshold
  * When flinging, if the velocity goes below this number, we just stop the
  * animation completely. This is to prevent asymptotically approaching 0
  * velocity and rerendering unnecessarily.\n
- * Units: screen pixels per millisecond
+ * Units: screen pixels per millisecond.\n
+ * NOTE: Should not be set to anything
+ * other than 0.0 for Android except for tests to disable flings.
  *
  * \li\b apz.max_velocity_inches_per_ms
  * Maximum velocity.  Velocity will be capped at this value if a faster fling
  * occurs.  Negative values indicate unlimited velocity.\n
  * Units: (real-world, i.e. screen) inches per millisecond
  *
  * \li\b apz.max_velocity_queue_size
  * Maximum size of velocity queue. The queue contains last N velocity records.
@@ -432,16 +439,17 @@ public:
     mApzc->DispatchStateChangeNotification(mInitialState, newState);
   }
 
 private:
   AsyncPanZoomController* mApzc;
   AsyncPanZoomController::PanZoomState mInitialState;
 };
 
+#if !defined(MOZ_ANDROID_APZ)
 class FlingAnimation: public AsyncPanZoomAnimation {
 public:
   FlingAnimation(AsyncPanZoomController& aApzc,
                  const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
                  bool aApplyAcceleration,
                  const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
     : mApzc(aApzc)
     , mOverscrollHandoffChain(aOverscrollHandoffChain)
@@ -598,16 +606,17 @@ private:
     return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
          + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
   }
 
   AsyncPanZoomController& mApzc;
   RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
   RefPtr<const AsyncPanZoomController> mScrolledApzc;
 };
+#endif
 
 class ZoomAnimation: public AsyncPanZoomAnimation {
 public:
   ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale2D aStartZoom,
                 CSSPoint aEndOffset, CSSToParentLayerScale2D aEndZoom)
     : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
     , mStartOffset(aStartOffset)
     , mStartZoom(aStartZoom)
@@ -2602,20 +2611,35 @@ void AsyncPanZoomController::AcceptFling
   }
 
   // If there's a scroll snap point near the predicted fling destination,
   // scroll there using a smooth scroll animation. Otherwise, start a
   // fling animation.
   ScrollSnapToDestination();
   if (mState != SMOOTH_SCROLL) {
     SetState(FLING);
+#if defined(MOZ_ANDROID_APZ)
+    if (!mOverScroller) {
+      widget::sdk::OverScroller::LocalRef scroller;
+      if (widget::sdk::OverScroller::New(widget::GeckoAppShell::GetApplicationContext(), &scroller) != NS_OK) {
+        APZC_LOG("%p Failed to create Android OverScroller\n", this);
+        return;
+      }
+      mOverScroller = scroller;
+    }
+    FlingOverScrollerAnimation *fling = new FlingOverScrollerAnimation(*this,
+        mOverScroller,
+        aHandoffState.mChain,
+        aHandoffState.mScrolledApzc);
+#else
     FlingAnimation *fling = new FlingAnimation(*this,
         aHandoffState.mChain,
         !aHandoffState.mIsHandoff,  // only apply acceleration if this is an initial fling
         aHandoffState.mScrolledApzc);
+#endif
     StartAnimation(fling);
   }
 }
 
 bool AsyncPanZoomController::AttemptFling(FlingHandoffState& aHandoffState) {
   // If we are pannable, take over the fling ourselves.
   if (IsPannable()) {
     AcceptFling(aHandoffState);
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -24,16 +24,19 @@
 #include "APZUtils.h"
 #include "Layers.h"                     // for Layer::ScrollDirection
 #include "LayersTypes.h"
 #include "mozilla/gfx/Matrix.h"
 #include "nsIScrollableFrame.h"
 #include "nsRegion.h"
 #include "nsTArray.h"
 #include "PotentialCheckerboardDurationTracker.h"
+#if defined(MOZ_ANDROID_APZ)
+#include "OverScroller.h"
+#endif // defined(MOZ_ANDROID_APZ)
 
 #include "base/message_loop.h"
 
 namespace mozilla {
 
 namespace ipc {
 
 class SharedMemoryBasic;
@@ -44,17 +47,21 @@ namespace layers {
 
 class AsyncDragMetrics;
 struct ScrollableLayerGuid;
 class CompositorBridgeParent;
 class GestureEventListener;
 class PCompositorBridgeParent;
 struct AsyncTransform;
 class AsyncPanZoomAnimation;
+#if defined(MOZ_ANDROID_APZ)
+class FlingOverScrollerAnimation;
+#else
 class FlingAnimation;
+#endif
 class InputBlockState;
 class TouchBlockState;
 class PanGestureBlockState;
 class OverscrollHandoffChain;
 class StateChangeNotificationBlocker;
 class CheckerboardEvent;
 
 /**
@@ -859,17 +866,21 @@ public:
    * unused, residual velocity.
    * |aHandoffState.mIsHandoff| should be true iff. the fling was handed off
    * from a previous APZC, and determines whether acceleration is applied
    * to the fling.
    */
   bool AttemptFling(FlingHandoffState& aHandoffState);
 
 private:
+#if defined(MOZ_ANDROID_APZ)
+  friend class FlingOverScrollerAnimation;
+#else
   friend class FlingAnimation;
+#endif
   friend class OverscrollAnimation;
   friend class SmoothScrollAnimation;
   friend class WheelScrollAnimation;
 
   // The initial velocity of the most recent fling.
   ParentLayerPoint mLastFlingVelocity;
   // The time at which the most recent fling started.
   TimeStamp mLastFlingTime;
@@ -1167,14 +1178,17 @@ private:
 
   // Find a snap point near |aDestination| that we should snap to.
   // Returns the snap point if one was found, or an empty Maybe otherwise.
   // |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
   // GetSnapPointForDestination). It should generally be determined by the
   // type of event that's triggering the scroll.
   Maybe<CSSPoint> FindSnapPointNear(const CSSPoint& aDestination,
                                     nsIScrollableFrame::ScrollUnit aUnit);
+#if defined(MOZ_ANDROID_APZ)
+  widget::sdk::OverScroller::GlobalRef mOverScroller;
+#endif // defined(MOZ_ANDROID_APZ)
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_PanZoomController_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FlingOverScrollerAnimation.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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/. */
+
+#include "AsyncPanZoomController.h"
+#include "FlingOverScrollerAnimation.h"
+#include "GeneratedJNIWrappers.h"
+#include "gfxPrefs.h"
+#include "OverscrollHandoffState.h"
+#include "OverScroller.h"
+
+namespace mozilla {
+namespace layers {
+
+// Value used in boundary detection. Due to round off error,
+// assume getting within 5 pixels of the boundary is close enough.
+static const float BOUNDS_EPSILON = 5.0f;
+// Maximum number of times the animator can calculate the same offset
+// before the animation is aborted. This is due to a bug in the Android
+// OverScroller class where under certain conditions the OverScroller
+// will overflow some internal value and begin scrolling beyond the bounds
+// of the page. Since we are clamping the results from the OverScroller,
+// if the offset does not change over the past 30 frames, we assume the
+// OverScroller has overflowed.
+static const int32_t MAX_OVERSCROLL_COUNT = 30;
+
+FlingOverScrollerAnimation::FlingOverScrollerAnimation(AsyncPanZoomController& aApzc,
+                                                       widget::sdk::OverScroller::GlobalRef aOverScroller,
+                                                       const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+                                                       const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+  : mApzc(aApzc)
+  , mOverScroller(aOverScroller)
+  , mOverscrollHandoffChain(aOverscrollHandoffChain)
+  , mScrolledApzc(aScrolledApzc)
+  , mSentBounceX(false)
+  , mSentBounceY(false)
+  , mOverScrollCount(0)
+{
+  MOZ_ASSERT(mOverscrollHandoffChain);
+  MOZ_ASSERT(mOverScroller);
+
+  // Drop any velocity on axes where we don't have room to scroll anyways
+  // (in this APZC, or an APZC further in the handoff chain).
+  // This ensures that we don't take the 'overscroll' path in Sample()
+  // on account of one axis which can't scroll having a velocity.
+  if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
+    ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+    mApzc.mX.SetVelocity(0);
+  }
+  if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
+    ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+    mApzc.mY.SetVelocity(0);
+  }
+
+  ParentLayerPoint velocity = mApzc.GetVelocityVector();
+  float length = velocity.Length();
+  if (length > 0.0f) {
+    mFlingDirection = velocity / length;
+  }
+
+  mStartOffset.x = mPreviousOffset.x = mApzc.mX.GetOrigin().value;
+  mStartOffset.y = mPreviousOffset.y = mApzc.mY.GetOrigin().value;
+  mOverScroller->Fling((int32_t)mStartOffset.x, (int32_t)mStartOffset.y,
+                       // Android needs the velocity in pixels per second and it is in pixels per ms.
+                       (int32_t)(velocity.x * 1000.0f), (int32_t)(velocity.y * 1000.0f),
+                       (int32_t)mApzc.mX.GetPageStart().value, (int32_t)(mApzc.mX.GetPageEnd() - mApzc.mX.GetCompositionLength()).value,
+                       (int32_t)mApzc.mY.GetPageStart().value, (int32_t)(mApzc.mY.GetPageEnd() - mApzc.mY.GetCompositionLength()).value,
+                       0, 0);
+}
+
+/**
+ * Advances a fling by an interpolated amount based on the Android OverScroller.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the fling animation should be advanced by one frame,
+ * or false if there is no fling or the fling has ended.
+ */
+bool
+FlingOverScrollerAnimation::DoSample(FrameMetrics& aFrameMetrics,
+                                     const TimeDuration& aDelta)
+{
+  bool shouldContinueFling = true;
+  mOverScroller->ComputeScrollOffset(&shouldContinueFling);
+
+  float speed = 0.0f;
+  mOverScroller->GetCurrVelocity(&speed);
+  speed = speed * 0.001f; // convert from pixels/sec to pixels/ms
+
+  // gfxPrefs::APZFlingStoppedThreshold is only used in tests.
+  if (!shouldContinueFling || (speed < gfxPrefs::APZFlingStoppedThreshold())) {
+    if (shouldContinueFling) {
+      // The OverScroller thinks it should continue but the speed is below
+      // the stopping threshold so abort the animation.
+      mOverScroller->AbortAnimation();
+    }
+    mApzc.mX.SetVelocity(0);
+    mApzc.mY.SetVelocity(0);
+    return false;
+  }
+
+  int32_t currentX = 0;
+  int32_t currentY = 0;
+  mOverScroller->GetCurrX(&currentX);
+  mOverScroller->GetCurrY(&currentY);
+  ParentLayerPoint offset((float)currentX, (float)currentY);
+  ParentLayerPoint velocity = mFlingDirection * speed;
+
+  bool hitBoundX = CheckBounds(mApzc.mX, offset.x, &(offset.x));
+  bool hitBoundY = CheckBounds(mApzc.mY, offset.y, &(offset.y));
+
+  if (IsZero(mPreviousOffset - offset)) {
+    mOverScrollCount++;
+  } else {
+    mOverScrollCount = 0;
+  }
+
+  // If the offset hasn't changed in over MAX_OVERSCROLL_COUNT we have overflowed
+  // the OverScroller and it needs to be aborted.
+  if (mOverScrollCount > MAX_OVERSCROLL_COUNT) {
+    velocity.x = velocity.y = 0.0f;
+    mOverScroller->AbortAnimation();
+  }
+
+  mPreviousOffset = offset;
+
+  mApzc.SetVelocityVector(velocity);
+  aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());
+
+  if (hitBoundX || hitBoundY) {
+    ParentLayerPoint bounceVelocity = mFlingDirection * speed;
+
+    if (!mSentBounceX && hitBoundX && fabsf(offset.x - mStartOffset.x) > BOUNDS_EPSILON) {
+      mSentBounceX = true;
+    } else {
+      bounceVelocity.x = 0.0f;
+    }
+
+    if (!mSentBounceY && hitBoundY && fabsf(offset.y - mStartOffset.y) > BOUNDS_EPSILON) {
+      mSentBounceY = true;
+    } else {
+      bounceVelocity.y = 0.0f;
+    }
+    if (!IsZero(bounceVelocity)) {
+      mDeferredTasks.AppendElement(
+            NewRunnableMethod(&mApzc,
+                              &AsyncPanZoomController::HandleFlingOverscroll,
+                              bounceVelocity,
+                              mOverscrollHandoffChain,
+                              mScrolledApzc));
+    }
+  }
+
+  return true;
+}
+
+bool
+FlingOverScrollerAnimation::CheckBounds(Axis& aAxis, float aValue, float* aClamped)
+{
+  bool result = false;
+  if ((aValue - BOUNDS_EPSILON) <= aAxis.GetPageStart().value) {
+    result = true;
+    if (aClamped) {
+      *aClamped = aAxis.GetPageStart().value;
+    }
+  } else if ((aValue + BOUNDS_EPSILON) >= (aAxis.GetPageEnd() - aAxis.GetCompositionLength()).value) {
+    result = true;
+    if (aClamped) {
+      *aClamped = (aAxis.GetPageEnd() - aAxis.GetCompositionLength()).value;
+    }
+  }
+  return result;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FlingOverScrollerAnimation.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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/. */
+
+#ifndef mozilla_layers_FlingOverScrollerAnimation_h_
+#define mozilla_layers_FlingOverScrollerAnimation_h_
+
+#include "AsyncPanZoomAnimation.h"
+#include "OverScroller.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+
+class FlingOverScrollerAnimation: public AsyncPanZoomAnimation {
+public:
+  FlingOverScrollerAnimation(AsyncPanZoomController& aApzc,
+                 widget::sdk::OverScroller::GlobalRef aOverScroller,
+                 const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+                 const RefPtr<const AsyncPanZoomController>& aScrolledApzc);
+  virtual bool DoSample(FrameMetrics& aFrameMetrics,
+                        const TimeDuration& aDelta) override;
+private:
+  // Returns true if value is on or outside of axis bounds.
+  bool CheckBounds(Axis& aAxis, float aValue, float* aClamped);
+
+  AsyncPanZoomController& mApzc;
+  widget::sdk::OverScroller::GlobalRef mOverScroller;
+  RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+  RefPtr<const AsyncPanZoomController> mScrolledApzc;
+  bool mSentBounceX;
+  bool mSentBounceY;
+  ParentLayerPoint mStartOffset;
+  ParentLayerPoint mPreviousOffset;
+  // Unit vector in the direction of the fling.
+  ParentLayerPoint mFlingDirection;
+  int32_t mOverScrollCount;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_FlingOverScrollerAnimation_h_
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -182,17 +182,17 @@ ClientSingleTiledLayerBuffer::PaintThebe
   if (dtOnWhite) {
     dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
     dtOnWhite = nullptr;
   }
 
   {
     RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(dt);
     if (!ctx) {
-      gfxDevCrash(LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+      gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
       return;
     }
     ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
 
     aCallback(mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
   }
 
   // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -249,16 +249,21 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
              ]
         ]
     SOURCES += [
         'ipc/GonkNativeHandle.cpp',
         'ipc/GonkNativeHandleUtils.cpp',
         'ipc/ShadowLayerUtilsGralloc.cpp',
     ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+    UNIFIED_SOURCES += [
+        'apz/src/FlingOverScrollerAnimation.cpp',
+    ]
+
 UNIFIED_SOURCES += [
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
     'apz/src/CheckerboardEvent.cpp',
     'apz/src/DragTracker.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/HitTestingTreeNode.cpp',
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -599,18 +599,19 @@ pref("apz.touch_start_tolerance", "0.06"
 pref("apz.axis_lock.breakout_angle", "0.7853982");    // PI / 4 (45 degrees)
 // APZ physics settings reviewed by UX
 pref("apz.axis_lock.mode", 1); // Use "strict" axis locking
 pref("apz.fling_curve_function_x1", "0.59");
 pref("apz.fling_curve_function_y1", "0.46");
 pref("apz.fling_curve_function_x2", "0.05");
 pref("apz.fling_curve_function_y2", "1.00");
 pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
+// apz.fling_friction and apz.fling_stopped_threshold are currently ignored by Fennec.
 pref("apz.fling_friction", "0.004");
-pref("apz.fling_stopped_threshold", "0.1");
+pref("apz.fling_stopped_threshold", "0.0");
 pref("apz.max_velocity_inches_per_ms", "0.07");
 pref("apz.fling_accel_interval_ms", 750);
 pref("apz.overscroll.enabled", true);
 #endif
 
 pref("layers.progressive-paint", true);
 pref("layers.low-precision-buffer", true);
 pref("layers.low-precision-resolution", "0.25");
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
@@ -1666,16 +1666,17 @@ public class GeckoAppShell
     public static Context getContext() {
         return sContextGetter.getContext();
     }
 
     public static void setContextGetter(ContextGetter cg) {
         sContextGetter = cg;
     }
 
+    @WrapForJNI(allowMultithread = true)
     public static Context getApplicationContext() {
         return sApplicationContext;
     }
 
     public static void setApplicationContext(final Context context) {
         sApplicationContext = context;
     }
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -237,16 +237,24 @@ auto GeckoAppShell::EnableSensor(int32_t
 constexpr char GeckoAppShell::GamepadAdded_t::name[];
 constexpr char GeckoAppShell::GamepadAdded_t::signature[];
 
 auto GeckoAppShell::GamepadAdded(int32_t a0, int32_t a1) -> void
 {
     return mozilla::jni::Method<GamepadAdded_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1);
 }
 
+constexpr char GeckoAppShell::GetApplicationContext_t::name[];
+constexpr char GeckoAppShell::GetApplicationContext_t::signature[];
+
+auto GeckoAppShell::GetApplicationContext() -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<GetApplicationContext_t>::Call(GeckoAppShell::Context(), nullptr);
+}
+
 constexpr char GeckoAppShell::GetConnection_t::name[];
 constexpr char GeckoAppShell::GetConnection_t::signature[];
 
 auto GeckoAppShell::GetConnection(mozilla::jni::String::Param a0) -> mozilla::jni::Object::LocalRef
 {
     return mozilla::jni::Method<GetConnection_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -522,16 +522,31 @@ public:
                 "(II)V";
         static const bool isStatic = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto GamepadAdded(int32_t, int32_t) -> void;
 
+    struct GetApplicationContext_t {
+        typedef GeckoAppShell Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "getApplicationContext";
+        static constexpr char signature[] =
+                "()Landroid/content/Context;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    static auto GetApplicationContext() -> mozilla::jni::Object::LocalRef;
+
     struct GetConnection_t {
         typedef GeckoAppShell Owner;
         typedef mozilla::jni::Object::LocalRef ReturnType;
         typedef mozilla::jni::Object::Param SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param> Args;
         static constexpr char name[] = "getConnection";
         static constexpr char signature[] =
new file mode 100644
--- /dev/null
+++ b/widget/android/bindings/OverScroller-classes.txt
@@ -0,0 +1,1 @@
+android.widget.OverScroller
--- a/widget/android/bindings/moz.build
+++ b/widget/android/bindings/moz.build
@@ -7,16 +7,17 @@
 # List of stems to generate .cpp and .h files for.  To add a stem, add it to
 # this list and ensure that $(stem)-classes.txt exists in this directory.
 generated = [
     'AndroidRect',
     'Bundle',
     'KeyEvent',
     'MediaCodec',
     'MotionEvent',
+    'OverScroller',
     'SurfaceTexture'
 ]
 
 SOURCES += ['!%s.cpp' % stem for stem in generated]
 
 EXPORTS += ['!%s.h' % stem for stem in generated]
 
 # We'd like to add these to a future GENERATED_EXPORTS list, but for now we mark