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
--- 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_