Bug 1358017 - Part 6: Implements the auto-dir scrolling feature(without the "honour root" functionality) in non-APZ r=masayuki
authorZhang Junzhi <zjz@zjz.name>
Mon, 19 Mar 2018 17:05:45 +0800
changeset 413086 f33f52fc4797e2246ae7bc0cdaf40bea6a648749
parent 413085 5ce6e1682420b489773156bd85caae8446a48e7b
child 413087 bc6c4a16c3d058949a04a652218aaf11ea02009b
push id33833
push useraiakab@mozilla.com
push dateFri, 13 Apr 2018 09:41:15 +0000
treeherdermozilla-central@260e4c83c8a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1358017
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1358017 - Part 6: Implements the auto-dir scrolling feature(without the "honour root" functionality) in non-APZ r=masayuki This commit implements the auto-dir scrolling functionality in non-APZ, based on part 1 to part 3. However, the functionality of mousewheel.autodir.honourroot is unimplemented in this commit. MozReview-Commit-ID: 2vYABOx4RkK
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
dom/events/WheelHandlingHelper.cpp
dom/events/WheelHandlingHelper.h
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2327,18 +2327,18 @@ EventStateManager::DispatchLegacyMouseSc
 
   if (!aTargetFrame || *aStatus == nsEventStatus_eConsumeNoDefault) {
     return;
   }
 
   // Ignore mouse wheel transaction for computing legacy mouse wheel
   // events' delta value.
   nsIFrame* scrollFrame =
-    ComputeScrollTarget(aTargetFrame, aEvent,
-                        COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
+    ComputeScrollTargetAndMayAdjustWheelEvent(
+      aTargetFrame, aEvent, COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET);
 
   nsIScrollableFrame* scrollTarget = do_QueryFrame(scrollFrame);
   nsPresContext* pc =
     scrollFrame ? scrollFrame->PresContext() : aTargetFrame->PresContext();
 
   // DOM event's delta vales are computed from CSS pixels.
   nsSize scrollAmount = GetScrollAmount(pc, aEvent, scrollTarget);
   nsIntSize scrollAmountInCSSPixels(
@@ -2518,39 +2518,70 @@ EventStateManager::SendPixelScrollEvent(
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
                             &event, nullptr, &status);
   aState.mDefaultPrevented =
     event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
   aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
 }
 
 nsIFrame*
-EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
-                                       WidgetWheelEvent* aEvent,
-                                       ComputeScrollTargetOptions aOptions)
+EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
+                     nsIFrame* aTargetFrame,
+                     WidgetWheelEvent* aEvent,
+                     ComputeScrollTargetOptions aOptions)
 {
-  return ComputeScrollTarget(aTargetFrame, aEvent->mDeltaX, aEvent->mDeltaY,
-                             aEvent, aOptions);
+  return ComputeScrollTargetAndMayAdjustWheelEvent(aTargetFrame,
+                                                   aEvent->mDeltaX,
+                                                   aEvent->mDeltaY,
+                                                   aEvent, aOptions);
 }
 
-// Overload ComputeScrollTarget method to allow passing "test" dx and dy when looking
-// for which scrollbarmediators to activate when two finger down on trackpad
-// and before any actual motion
+// Overload ComputeScrollTargetAndMayAdjustWheelEvent method to allow passing
+// "test" dx and dy when looking for which scrollbarmediators to activate when
+// two finger down on trackpad and before any actual motion
 nsIFrame*
-EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
-                                       double aDirectionX,
-                                       double aDirectionY,
-                                       WidgetWheelEvent* aEvent,
-                                       ComputeScrollTargetOptions aOptions)
+EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
+                     nsIFrame* aTargetFrame,
+                     double aDirectionX,
+                     double aDirectionY,
+                     WidgetWheelEvent* aEvent,
+                     ComputeScrollTargetOptions aOptions)
 {
   if ((aOptions & INCLUDE_PLUGIN_AS_TARGET) &&
       !WheelPrefs::WheelEventsEnabledOnPlugins()) {
     aOptions = RemovePluginFromTarget(aOptions);
   }
 
+  bool isAutoDir = false;
+  bool honoursRoot = false;
+  if (MAY_BE_ADJUSTED_BY_AUTO_DIR & aOptions) {
+    // If the scroll is respected as auto-dir, aDirection* should always be
+    // equivalent to the event's delta vlaues(Currently, there are only one case
+    // where aDirection*s have different values from the widget wheel event's
+    // original delta values and the only case isn't auto-dir, see
+    // ScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets).
+    MOZ_ASSERT(aDirectionX == aEvent->mDeltaX &&
+                 aDirectionY == aEvent->mDeltaY);
+
+    WheelDeltaAdjustmentStrategy strategy =
+      GetWheelDeltaAdjustmentStrategy(*aEvent);
+    switch (strategy) {
+      case WheelDeltaAdjustmentStrategy::eAutoDir:
+        isAutoDir = true;
+        honoursRoot = false;
+        break;
+      case WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour:
+        isAutoDir = true;
+        honoursRoot = true;
+        break;
+      default:
+        break;
+    }
+  }
+
   if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) {
     // If the user recently scrolled with the mousewheel, then they probably
     // want to scroll the same view as before instead of the view under the
     // cursor.  WheelTransaction tracks the frame currently being
     // scrolled with the mousewheel. We consider the transaction ended when the
     // mouse moves more than "mousewheel.transaction.ignoremovedelay"
     // milliseconds after the last scroll operation, or any time the mouse moves
     // out of the frame, or when more than "mousewheel.transaction.timeout"
@@ -2565,32 +2596,53 @@ EventStateManager::ComputeScrollTarget(n
           return lastScrollFrame;
         }
       }
       nsIScrollableFrame* scrollableFrame =
         lastScrollFrame->GetScrollTargetFrame();
       if (scrollableFrame) {
         nsIFrame* frameToScroll = do_QueryFrame(scrollableFrame);
         MOZ_ASSERT(frameToScroll);
+        if (isAutoDir) {
+          ESMAutoDirWheelDeltaAdjuster adjuster(*aEvent,
+                                                *lastScrollFrame,
+                                                honoursRoot);
+          // Note that calling this function will not always cause the delta to
+          // be adjusted, it only adjusts the delta when it should, because
+          // Adjust() internally calls ShouldBeAdjusted() before making
+          // adjustment.
+          adjuster.Adjust();
+        }
         return frameToScroll;
       }
     }
   }
 
   // If the event doesn't cause scroll actually, we cannot find scroll target
   // because we check if the event can cause scroll actually on each found
   // scrollable frame.
   if (!aDirectionX && !aDirectionY) {
     return nullptr;
   }
 
-  bool checkIfScrollableX =
-    aDirectionX && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
-  bool checkIfScrollableY =
-    aDirectionY && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
+  bool checkIfScrollableX;
+  bool checkIfScrollableY;
+  if (isAutoDir) {
+    // Always check the frame's scrollability in both the two directions for an
+    // auto-dir scroll. That is, for an auto-dir scroll,
+    // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS and
+    // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS should be ignored.
+    checkIfScrollableX = true;
+    checkIfScrollableY = true;
+  } else {
+    checkIfScrollableX =
+      aDirectionX && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
+    checkIfScrollableY =
+      aDirectionY && (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
+  }
 
   nsIFrame* scrollFrame =
     !(aOptions & START_FROM_PARENT) ? aTargetFrame :
                                       GetParentFrameToScroll(aTargetFrame);
   for (; scrollFrame; scrollFrame = GetParentFrameToScroll(scrollFrame)) {
     // Check whether the frame wants to provide us with a scrollable view.
     nsIScrollableFrame* scrollableFrame = scrollFrame->GetScrollTargetFrame();
     if (!scrollableFrame) {
@@ -2643,41 +2695,61 @@ EventStateManager::ComputeScrollTarget(n
     bool hiddenForV = (NS_STYLE_OVERFLOW_HIDDEN == ss.mVertical);
     bool hiddenForH = (NS_STYLE_OVERFLOW_HIDDEN == ss.mHorizontal);
     if ((hiddenForV && hiddenForH) ||
         (checkIfScrollableY && !checkIfScrollableX && hiddenForV) ||
         (checkIfScrollableX && !checkIfScrollableY && hiddenForH)) {
       continue;
     }
 
-    // For default action, we should climb up the tree if cannot scroll it
-    // by the event actually.
-    bool canScroll = WheelHandlingUtils::CanScrollOn(scrollableFrame,
-                                                     aDirectionX, aDirectionY);
+    // Computes whether the currently checked frame is scrollable by this wheel
+    // event.
+    bool canScroll = false;
+    if (isAutoDir) {
+      ESMAutoDirWheelDeltaAdjuster adjuster(*aEvent, *scrollFrame, honoursRoot);
+      if (adjuster.ShouldBeAdjusted()) {
+        adjuster.Adjust();
+        canScroll = true;
+      } else if (WheelHandlingUtils::CanScrollOn(scrollableFrame,
+                                                 aDirectionX, aDirectionY)) {
+        canScroll = true;
+      }
+    } else if (WheelHandlingUtils::CanScrollOn(scrollableFrame,
+                                               aDirectionX, aDirectionY)) {
+      canScroll = true;
+    }
+
     // Comboboxes need special care.
     nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
     if (comboBox) {
       if (comboBox->IsDroppedDown()) {
         // Don't propagate to parent when drop down menu is active.
         return canScroll ? frameToScroll : nullptr;
       }
       // Always propagate when not dropped down (even if focused).
       continue;
     }
 
     if (canScroll) {
       return frameToScroll;
     }
+
+    // Where we are at is the block ending in a for loop.
+    // The current frame has been checked to be unscrollable by this wheel
+    // event, continue the loop to check its parent, if any.
   }
 
   nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
       aTargetFrame->PresShell()->GetRootFrame());
   aOptions =
     static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
-  return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
+  if (!newFrame) {
+    return nullptr;
+  }
+  return ComputeScrollTargetAndMayAdjustWheelEvent(newFrame, aEvent, aOptions);
 }
 
 nsSize
 EventStateManager::GetScrollAmount(nsPresContext* aPresContext,
                                    WidgetWheelEvent* aEvent,
                                    nsIScrollableFrame* aScrollableFrame)
 {
   MOZ_ASSERT(aPresContext);
@@ -2832,24 +2904,26 @@ EventStateManager::DoScrollText(nsIScrol
   // should be same as delta* values since they may be used as gesture event by
   // widget.  However, if there is another scrollable element in the ancestor
   // along the axis, probably users don't want the operation to cause
   // additional action such as moving history.  In such case, overflowDelta
   // values should stay zero.
   if (scrollFrameWeak.IsAlive()) {
     if (aEvent->mDeltaX &&
         overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
-        !ComputeScrollTarget(scrollFrame, aEvent,
-                             COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS)) {
+        !ComputeScrollTargetAndMayAdjustWheelEvent(
+           scrollFrame, aEvent,
+           COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS_WITH_AUTO_DIR)) {
       aEvent->mOverflowDeltaX = aEvent->mDeltaX;
     }
     if (aEvent->mDeltaY &&
         overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
-        !ComputeScrollTarget(scrollFrame, aEvent,
-                             COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS)) {
+        !ComputeScrollTargetAndMayAdjustWheelEvent(
+           scrollFrame, aEvent,
+           COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS_WITH_AUTO_DIR)) {
       aEvent->mOverflowDeltaY = aEvent->mDeltaY;
     }
   }
 
   NS_ASSERTION(aEvent->mOverflowDeltaX == 0 ||
     (aEvent->mOverflowDeltaX > 0) == (aEvent->mDeltaX > 0),
     "The sign of mOverflowDeltaX is different from the scroll direction");
   NS_ASSERTION(aEvent->mOverflowDeltaY == 0 ||
@@ -3363,18 +3437,20 @@ EventStateManager::PostHandleEvent(nsPre
     }
     break;
   case eWheelOperationEnd:
     {
       MOZ_ASSERT(aEvent->IsTrusted());
       ScrollbarsForWheel::MayInactivate();
       WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
       nsIScrollableFrame* scrollTarget =
-        do_QueryFrame(ComputeScrollTarget(mCurrentTarget, wheelEvent,
-                                          COMPUTE_DEFAULT_ACTION_TARGET));
+        do_QueryFrame(
+          ComputeScrollTargetAndMayAdjustWheelEvent(
+            mCurrentTarget, wheelEvent,
+            COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR));
       if (scrollTarget) {
         scrollTarget->ScrollSnap();
       }
     }
     break;
   case eWheel:
   case eWheelOperationStart:
     {
@@ -3403,22 +3479,31 @@ EventStateManager::PostHandleEvent(nsPre
       // If horizontalized, the delta values will be restored and its overflow
       // deltaX will become 0 when the WheelDeltaHorizontalizer instance is
       // being destroyed.
       WheelDeltaHorizontalizer horizontalizer(*wheelEvent);
       if (WheelDeltaAdjustmentStrategy::eHorizontalize == strategy) {
         horizontalizer.Horizontalize();
       }
 
+      // Since ComputeScrollTargetAndMayAdjustWheelEvent() may adjust the delta
+      // if the event is auto-dir. So we use |ESMAutoDirWheelDeltaRestorer|
+      // here.
+      // An instance of |ESMAutoDirWheelDeltaRestorer| is used to monitor
+      // auto-dir adjustment which may happen during its lifetime. If the delta
+      // values is adjusted during its lifetime, the instance will restore the
+      // adjusted delta when it's being destrcuted.
+      ESMAutoDirWheelDeltaRestorer restorer(*wheelEvent);
       // Check if the frame to scroll before checking the default action
       // because if the scroll target is a plugin, the default action should be
       // chosen by the plugin rather than by our prefs.
       nsIFrame* frameToScroll =
-        ComputeScrollTarget(mCurrentTarget, wheelEvent,
-                            COMPUTE_DEFAULT_ACTION_TARGET);
+        ComputeScrollTargetAndMayAdjustWheelEvent(
+          mCurrentTarget, wheelEvent,
+          COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR);
       nsPluginFrame* pluginFrame = do_QueryFrame(frameToScroll);
       if (pluginFrame) {
         MOZ_ASSERT(pluginFrame->WantsToHandleWheelEventAsDefaultAction());
         // Plugins should receive original values instead of adjusted values.
         horizontalizer.CancelHorizontalization();
         action = WheelPrefs::ACTION_SEND_TO_PLUGIN;
       }
 
@@ -3500,18 +3585,18 @@ EventStateManager::PostHandleEvent(nsPre
         default:
           bool allDeltaOverflown = false;
           if (wheelEvent->mFlags.mHandledByAPZ) {
             if (wheelEvent->mCanTriggerSwipe) {
               // For events that can trigger swipes, APZ needs to know whether
               // scrolling is possible in the requested direction. It does this
               // by looking at the scroll overflow values on mCanTriggerSwipe
               // events after they have been processed.
-              allDeltaOverflown =
-                !ComputeScrollTarget(mCurrentTarget, wheelEvent,
+              allDeltaOverflown = !ComputeScrollTarget(
+                                     mCurrentTarget, wheelEvent,
                                      COMPUTE_DEFAULT_ACTION_TARGET);
             }
           } else {
             // The event was processed neither by APZ nor by us, so all of the
             // delta values must be overflown delta values.
             allDeltaOverflown = true;
           }
 
@@ -6155,17 +6240,16 @@ EventStateManager::GetWheelDeltaAdjustme
           return WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour;
         }
         return WheelDeltaAdjustmentStrategy::eAutoDir;
       }
       return WheelDeltaAdjustmentStrategy::eNone;
     case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL:
       return WheelDeltaAdjustmentStrategy::eHorizontalize;
     default:
-      // Prevent compilation errors generated by -Werror=switch
       break;
   }
   return WheelDeltaAdjustmentStrategy::eNone;
 }
 
 void
 EventStateManager::GetUserPrefsForWheelEvent(const WidgetWheelEvent* aEvent,
                                              double* aOutMultiplierX,
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -763,34 +763,37 @@ protected:
    */
   void SendPixelScrollEvent(nsIFrame* aTargetFrame,
                             WidgetWheelEvent* aEvent,
                             EventState& aState,
                             int32_t aPixelDelta,
                             DeltaDirection aDeltaDirection);
 
   /**
-   * ComputeScrollTarget() returns the scrollable frame which should be
-   * scrolled.
+   * ComputeScrollTargetAndMayAdjustWheelEvent() returns the scrollable frame
+   * which should be scrolled.
    *
    * @param aTargetFrame        The event target of the wheel event.
    * @param aEvent              The handling mouse wheel event.
    * @param aOptions            The options for finding the scroll target.
    *                            Callers should use COMPUTE_*.
    * @return                    The scrollable frame which should be scrolled.
    */
-  // These flags are used in ComputeScrollTarget(). Callers should use
-  // COMPUTE_*.
+  // These flags are used in ComputeScrollTargetAndMayAdjustWheelEvent().
+  // Callers should use COMPUTE_*.
   enum
   {
     PREFER_MOUSE_WHEEL_TRANSACTION               = 0x00000001,
     PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS = 0x00000002,
     PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS = 0x00000004,
     START_FROM_PARENT                            = 0x00000008,
-    INCLUDE_PLUGIN_AS_TARGET                     = 0x00000010
+    INCLUDE_PLUGIN_AS_TARGET                     = 0x00000010,
+    // Indicates the wheel scroll event being computed is an auto-dir scroll, so
+    // its delta may be adjusted after being computed.
+    MAY_BE_ADJUSTED_BY_AUTO_DIR                  = 0x00000020,
   };
   enum ComputeScrollTargetOptions
   {
     // At computing scroll target for legacy mouse events, we should return
     // first scrollable element even when it's not scrollable to the direction.
     COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET     = 0,
     // Default action prefers the scrolled element immediately before if it's
     // still under the mouse cursor.  Otherwise, it prefers the nearest
@@ -799,51 +802,94 @@ protected:
       (PREFER_MOUSE_WHEEL_TRANSACTION |
        PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS |
        PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS),
     // When this is specified, the result may be nsPluginFrame.  In such case,
     // the frame doesn't have nsIScrollableFrame interface.
     COMPUTE_DEFAULT_ACTION_TARGET                =
       (COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN |
        INCLUDE_PLUGIN_AS_TARGET),
+    COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR_EXCEPT_PLUGIN =
+      (COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN |
+       MAY_BE_ADJUSTED_BY_AUTO_DIR),
+    COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR =
+      (COMPUTE_DEFAULT_ACTION_TARGET |
+       MAY_BE_ADJUSTED_BY_AUTO_DIR),
     // Look for the nearest scrollable ancestor which can be scrollable with
     // aEvent.
     COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS     =
       (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | START_FROM_PARENT),
     COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS     =
-      (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | START_FROM_PARENT)
+      (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | START_FROM_PARENT),
+    COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS_WITH_AUTO_DIR =
+      (COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS | MAY_BE_ADJUSTED_BY_AUTO_DIR),
+    COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS_WITH_AUTO_DIR =
+      (COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS | MAY_BE_ADJUSTED_BY_AUTO_DIR),
   };
   static ComputeScrollTargetOptions RemovePluginFromTarget(
                                       ComputeScrollTargetOptions aOptions)
   {
     switch (aOptions) {
       case COMPUTE_DEFAULT_ACTION_TARGET:
         return COMPUTE_DEFAULT_ACTION_TARGET_EXCEPT_PLUGIN;
+      case COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR:
+        return COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR_EXCEPT_PLUGIN;
       default:
         MOZ_ASSERT(!(aOptions & INCLUDE_PLUGIN_AS_TARGET));
         return aOptions;
     }
   }
+
+  // Compute the scroll target.
+  // The delta values in the wheel event may be changed if the event is for
+  // auto-dir scrolling. For information on auto-dir,
+  // @see mozilla::WheelDeltaAdjustmentStrategy
+  nsIFrame* ComputeScrollTargetAndMayAdjustWheelEvent(
+              nsIFrame* aTargetFrame,
+              WidgetWheelEvent* aEvent,
+              ComputeScrollTargetOptions aOptions);
+
+  nsIFrame* ComputeScrollTargetAndMayAdjustWheelEvent(
+              nsIFrame* aTargetFrame,
+              double aDirectionX,
+              double aDirectionY,
+              WidgetWheelEvent* aEvent,
+              ComputeScrollTargetOptions aOptions);
+
   nsIFrame* ComputeScrollTarget(nsIFrame* aTargetFrame,
                                 WidgetWheelEvent* aEvent,
-                                ComputeScrollTargetOptions aOptions);
+                                ComputeScrollTargetOptions aOptions)
+  {
+    MOZ_ASSERT(!(aOptions & MAY_BE_ADJUSTED_BY_AUTO_DIR),
+               "aEvent may be modified by auto-dir");
+    return ComputeScrollTargetAndMayAdjustWheelEvent(aTargetFrame, aEvent,
+                                                     aOptions);
+  }
 
   nsIFrame* ComputeScrollTarget(nsIFrame* aTargetFrame,
                                 double aDirectionX,
                                 double aDirectionY,
                                 WidgetWheelEvent* aEvent,
-                                ComputeScrollTargetOptions aOptions);
+                                ComputeScrollTargetOptions aOptions)
+  {
+    MOZ_ASSERT(!(aOptions & MAY_BE_ADJUSTED_BY_AUTO_DIR),
+               "aEvent may be modified by auto-dir");
+    return ComputeScrollTargetAndMayAdjustWheelEvent(aTargetFrame,
+                                                     aDirectionX, aDirectionY,
+                                                     aEvent, aOptions);
+  }
 
   /**
    * GetScrollAmount() returns the scroll amount in app uints of one line or
    * one page.  If the wheel event scrolls a page, returns the page width and
    * height.  Otherwise, returns line height for both its width and height.
    *
    * @param aScrollableFrame    A frame which will be scrolled by the event.
-   *                            The result of ComputeScrollTarget() is
+   *                            The result of
+   *                            ComputeScrollTargetAndMayAdjustWheelEvent() is
    *                            expected for this value.
    *                            This can be nullptr if there is no scrollable
    *                            frame.  Then, this method uses root frame's
    *                            line height or visible area's width and height.
    */
   nsSize GetScrollAmount(nsPresContext* aPresContext,
                          WidgetWheelEvent* aEvent,
                          nsIScrollableFrame* aScrollableFrame);
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -526,17 +526,18 @@ ScrollbarsForWheel::TemporarilyActivateA
                       nsIFrame* aTargetFrame,
                       WidgetWheelEvent* aEvent)
 {
   for (size_t i = 0; i < kNumberOfTargets; i++) {
     const DeltaValues *dir = &directions[i];
     AutoWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
     MOZ_ASSERT(!*scrollTarget, "scroll target still temporarily activated!");
     nsIScrollableFrame* target = do_QueryFrame(
-      aESM->ComputeScrollTarget(aTargetFrame, dir->deltaX, dir->deltaY, aEvent,
+      aESM->ComputeScrollTarget(
+              aTargetFrame, dir->deltaX, dir->deltaY, aEvent,
               EventStateManager::COMPUTE_DEFAULT_ACTION_TARGET));
     nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(target);
     if (scrollbarMediator) {
       nsIFrame* targetFrame = do_QueryFrame(target);
       *scrollTarget = targetFrame;
       scrollbarMediator->ScrollbarActivityStarted();
     }
   }
@@ -701,9 +702,193 @@ AutoDirWheelDeltaAdjuster::Adjust()
   if (IsHorizontalContentRightToLeft()) {
     mDeltaX *= -1;
     mDeltaY *= -1;
   }
   mShouldBeAdjusted = false;
   OnAdjusted();
 }
 
+/******************************************************************/
+/* mozilla::ESMAutoDirWheelDeltaAdjuster                          */
+/******************************************************************/
+
+ESMAutoDirWheelDeltaAdjuster::ESMAutoDirWheelDeltaAdjuster(
+                                WidgetWheelEvent& aEvent,
+                                nsIFrame& aScrollFrame,
+                                bool aHonoursRoot)
+  : AutoDirWheelDeltaAdjuster(aEvent.mDeltaX, aEvent.mDeltaY)
+  , mLineOrPageDeltaX(aEvent.mLineOrPageDeltaX)
+  , mLineOrPageDeltaY(aEvent.mLineOrPageDeltaY)
+  , mOverflowDeltaX(aEvent.mOverflowDeltaX)
+  , mOverflowDeltaY(aEvent.mOverflowDeltaY)
+{
+  mScrollTargetFrame = aScrollFrame.GetScrollTargetFrame();
+  MOZ_ASSERT(mScrollTargetFrame);
+
+  // TODO Currently, the honoured target is always the current scrolling frame.
+  nsIFrame* honouredFrame = &aScrollFrame;
+
+  WritingMode writingMode = honouredFrame->GetWritingMode();
+  WritingMode::BlockDir blockDir = writingMode.GetBlockDir();
+  WritingMode::InlineDir inlineDir = writingMode.GetInlineDir();
+  // Get whether the honoured frame's content in the horizontal direction starts
+  // from right to left(E.g. it's true either if "writing-mode: vertical-rl", or
+  // if "writing-mode: horizontal-tb; direction: rtl;" in CSS).
+  mIsHorizontalContentRightToLeft =
+    (blockDir == WritingMode::BlockDir::eBlockRL ||
+     (blockDir == WritingMode::BlockDir::eBlockTB &&
+      inlineDir == WritingMode::InlineDir::eInlineRTL));
+}
+
+void
+ESMAutoDirWheelDeltaAdjuster::OnAdjusted()
+{
+  // Adjust() only adjusted basic deltaX and deltaY, which are not enough for
+  // ESM, we should continue to adjust line-or-page and overflow values.
+  if (mDeltaX) {
+    // A vertical scroll was adjusted to be horizontal.
+    MOZ_ASSERT(0 == mDeltaY);
+
+    mLineOrPageDeltaX = mLineOrPageDeltaY;
+    mLineOrPageDeltaY = 0;
+    mOverflowDeltaX = mOverflowDeltaY;
+    mOverflowDeltaY = 0;
+  } else {
+    // A horizontal scroll was adjusted to be vertical.
+    MOZ_ASSERT(0 != mDeltaY);
+
+    mLineOrPageDeltaY = mLineOrPageDeltaX;
+    mLineOrPageDeltaX = 0;
+    mOverflowDeltaY = mOverflowDeltaX;
+    mOverflowDeltaX = 0;
+  }
+  if (mIsHorizontalContentRightToLeft) {
+    // If in RTL writing mode, reverse the side the scroll will go towards.
+    mLineOrPageDeltaX *= -1;
+    mLineOrPageDeltaY *= -1;
+    mOverflowDeltaX *= -1;
+    mOverflowDeltaY *= -1;
+  }
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollAlongXAxis() const
+{
+  return mScrollTargetFrame->GetPerceivedScrollingDirections() &
+           nsIScrollableFrame::HORIZONTAL;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollAlongYAxis() const
+{
+  return mScrollTargetFrame->GetPerceivedScrollingDirections() &
+           nsIScrollableFrame::VERTICAL;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollUpwards() const
+{
+  nsPoint scrollPt = mScrollTargetFrame->GetScrollPosition();
+  nsRect scrollRange = mScrollTargetFrame->GetScrollRange();
+  return static_cast<double>(scrollRange.y) < scrollPt.y;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollDownwards() const
+{
+  nsPoint scrollPt = mScrollTargetFrame->GetScrollPosition();
+  nsRect scrollRange = mScrollTargetFrame->GetScrollRange();
+  return static_cast<double>(scrollRange.YMost()) > scrollPt.y;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollLeftwards() const
+{
+  nsPoint scrollPt = mScrollTargetFrame->GetScrollPosition();
+  nsRect scrollRange = mScrollTargetFrame->GetScrollRange();
+  return static_cast<double>(scrollRange.x) < scrollPt.x;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::CanScrollRightwards() const
+{
+  nsPoint scrollPt = mScrollTargetFrame->GetScrollPosition();
+  nsRect scrollRange = mScrollTargetFrame->GetScrollRange();
+  return static_cast<double>(scrollRange.XMost()) > scrollPt.x;
+}
+
+bool
+ESMAutoDirWheelDeltaAdjuster::IsHorizontalContentRightToLeft() const
+{
+  return mIsHorizontalContentRightToLeft;
+}
+
+/******************************************************************/
+/* mozilla::ESMAutoDirWheelDeltaRestorer                          */
+/******************************************************************/
+
+/*explicit*/
+ESMAutoDirWheelDeltaRestorer::ESMAutoDirWheelDeltaRestorer(
+                                WidgetWheelEvent& aEvent)
+  : mEvent(aEvent)
+  , mOldDeltaX(aEvent.mDeltaX)
+  , mOldDeltaY(aEvent.mDeltaY)
+  , mOldLineOrPageDeltaX(aEvent.mLineOrPageDeltaX)
+  , mOldLineOrPageDeltaY(aEvent.mLineOrPageDeltaY)
+  , mOldOverflowDeltaX(aEvent.mOverflowDeltaX)
+  , mOldOverflowDeltaY(aEvent.mOverflowDeltaY)
+{
+}
+
+ESMAutoDirWheelDeltaRestorer::~ESMAutoDirWheelDeltaRestorer()
+{
+  if (mOldDeltaX == mEvent.mDeltaX || mOldDeltaY == mEvent.mDeltaY) {
+    // The delta of the event wasn't adjusted during the lifetime of this
+    // |ESMAutoDirWheelDeltaRestorer| instance. No need to restore it.
+    return;
+  }
+
+  bool forRTL = false;
+
+  // First, restore the basic deltaX and deltaY.
+  std::swap(mEvent.mDeltaX, mEvent.mDeltaY);
+  if (mOldDeltaX != mEvent.mDeltaX || mOldDeltaY != mEvent.mDeltaY) {
+    // If X and Y still don't equal to their original values after being
+    // swapped, then it must be because they were adjusted for RTL.
+    forRTL = true;
+    mEvent.mDeltaX *= -1;
+    mEvent.mDeltaY *= -1;
+    MOZ_ASSERT(mOldDeltaX == mEvent.mDeltaX && mOldDeltaY == mEvent.mDeltaY);
+  }
+
+  if (mEvent.mDeltaX) {
+    // A horizontal scroll was adjusted to be vertical during the lifetime of
+    // this instance.
+    MOZ_ASSERT(0 == mEvent.mDeltaY);
+
+    // Restore the line-or-page and overflow values to be horizontal.
+    mEvent.mOverflowDeltaX = mEvent.mOverflowDeltaY;
+    mEvent.mLineOrPageDeltaX = mEvent.mLineOrPageDeltaY;
+    if (forRTL) {
+      mEvent.mOverflowDeltaX *= -1;
+      mEvent.mLineOrPageDeltaX *= -1;
+    }
+    mEvent.mOverflowDeltaY = mOldOverflowDeltaY;
+    mEvent.mLineOrPageDeltaY = mOldLineOrPageDeltaY;
+  } else {
+    // A vertical scroll was adjusted to be horizontal during the lifetime of
+    // this instance.
+    MOZ_ASSERT(0 != mEvent.mDeltaY);
+
+    // Restore the line-or-page and overflow values to be vertical.
+    mEvent.mOverflowDeltaY = mEvent.mOverflowDeltaX;
+    mEvent.mLineOrPageDeltaY = mEvent.mLineOrPageDeltaX;
+    if (forRTL) {
+      mEvent.mOverflowDeltaY *= -1;
+      mEvent.mLineOrPageDeltaY *= -1;
+    }
+    mEvent.mOverflowDeltaX = mOldOverflowDeltaX;
+    mEvent.mLineOrPageDeltaX = mOldLineOrPageDeltaX;
+  }
+}
+
 } // namespace mozilla
--- a/dom/events/WheelHandlingHelper.h
+++ b/dom/events/WheelHandlingHelper.h
@@ -377,11 +377,79 @@ protected:
   double& mDeltaX;
   double& mDeltaY;
 
 private:
   bool mCheckedIfShouldBeAdjusted;
   bool mShouldBeAdjusted;
 };
 
+/**
+ * This is the implementation of AutoDirWheelDeltaAdjuster for EventStateManager
+ *
+ * Detailed comments about some member functions are given in the base class
+ * AutoDirWheelDeltaAdjuster.
+ */
+class MOZ_STACK_CLASS ESMAutoDirWheelDeltaAdjuster final
+                        : public AutoDirWheelDeltaAdjuster
+{
+public:
+  /**
+   * @param aEvent             The auto-dir wheel scroll event.
+   * @param aScrollFrame       The scroll target for the event.
+   * @param aHonoursRoot       If set to true, the honoured frame is the root
+   *                           frame in the same document where the target is;
+   *                           If false, the honoured frame is the scroll
+   *                           target. For the concept of an honoured target,
+   *                           @see mozilla::WheelDeltaAdjustmentStrategy
+   */
+  ESMAutoDirWheelDeltaAdjuster(WidgetWheelEvent& aEvent,
+                               nsIFrame& aScrollFrame,
+                               bool aHonoursRoot);
+
+private:
+  virtual void OnAdjusted() override;
+  virtual bool CanScrollAlongXAxis() const override;
+  virtual bool CanScrollAlongYAxis() const override;
+  virtual bool CanScrollUpwards() const override;
+  virtual bool CanScrollDownwards() const override;
+  virtual bool CanScrollLeftwards() const override;
+  virtual bool CanScrollRightwards() const override;
+  virtual bool IsHorizontalContentRightToLeft() const override;
+
+  nsIScrollableFrame* mScrollTargetFrame;
+  bool mIsHorizontalContentRightToLeft;
+
+  int32_t& mLineOrPageDeltaX;
+  int32_t& mLineOrPageDeltaY;
+  double& mOverflowDeltaX;
+  double& mOverflowDeltaY;
+};
+
+/**
+ * This class is used for restoring the delta in an auto-dir wheel.
+ *
+ * An instance of this calss monitors auto-dir adjustment which may happen
+ * during its lifetime. If the delta values is adjusted during its lifetime, the
+ * instance will restore the adjusted delta when it's being destrcuted.
+ */
+class MOZ_STACK_CLASS ESMAutoDirWheelDeltaRestorer final
+{
+public:
+  /**
+   * @param aEvent             The wheel scroll event to be monitored.
+   */
+  explicit ESMAutoDirWheelDeltaRestorer(WidgetWheelEvent& aEvent);
+  ~ESMAutoDirWheelDeltaRestorer();
+
+private:
+  WidgetWheelEvent& mEvent;
+  double mOldDeltaX;
+  double mOldDeltaY;
+  int32_t mOldLineOrPageDeltaX;
+  int32_t mOldLineOrPageDeltaY;
+  double mOldOverflowDeltaX;
+  double mOldOverflowDeltaY;
+};
+
 } // namespace mozilla
 
 #endif // mozilla_WheelHandlingHelper_h_