summaryrefslogtreecommitdiffstats
path: root/dom/events/WheelHandlingHelper.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/WheelHandlingHelper.h')
-rw-r--r--dom/events/WheelHandlingHelper.h440
1 files changed, 440 insertions, 0 deletions
diff --git a/dom/events/WheelHandlingHelper.h b/dom/events/WheelHandlingHelper.h
new file mode 100644
index 0000000000..d9b73c2ddd
--- /dev/null
+++ b/dom/events/WheelHandlingHelper.h
@@ -0,0 +1,440 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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_WheelHandlingHelper_h_
+#define mozilla_WheelHandlingHelper_h_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "nsCoord.h"
+#include "nsIFrame.h" // for AutoWeakFrame only
+#include "nsPoint.h"
+
+class nsIFrame;
+class nsIScrollableFrame;
+class nsITimer;
+
+namespace mozilla {
+
+class EventStateManager;
+
+/**
+ * DeltaValues stores two delta values which are along X and Y axis. This is
+ * useful for arguments and results of some methods.
+ */
+
+struct DeltaValues {
+ DeltaValues() : deltaX(0.0), deltaY(0.0) {}
+
+ DeltaValues(double aDeltaX, double aDeltaY)
+ : deltaX(aDeltaX), deltaY(aDeltaY) {}
+
+ explicit DeltaValues(WidgetWheelEvent* aEvent);
+
+ double deltaX;
+ double deltaY;
+};
+
+/**
+ * WheelHandlingUtils provides some static methods which are useful at handling
+ * wheel events.
+ */
+
+class WheelHandlingUtils {
+ public:
+ /**
+ * Returns true if aFrame is a scrollable frame and it can be scrolled to
+ * either aDirectionX or aDirectionY along each axis. Or if aFrame is a
+ * plugin frame (in this case, aDirectionX and aDirectionY are ignored).
+ * Otherwise, false.
+ */
+ static bool CanScrollOn(nsIFrame* aFrame, double aDirectionX,
+ double aDirectionY);
+ /**
+ * Returns true if the scrollable frame can be scrolled to either aDirectionX
+ * or aDirectionY along each axis. Otherwise, false.
+ */
+ static bool CanScrollOn(nsIScrollableFrame* aScrollFrame, double aDirectionX,
+ double aDirectionY);
+
+ // For more details about the concept of a disregarded direction, refer to the
+ // code in struct mozilla::layers::ScrollMetadata which defines
+ // mDisregardedDirection.
+ static Maybe<layers::ScrollDirection> GetDisregardedWheelScrollDirection(
+ const nsIFrame* aFrame);
+
+ private:
+ static bool CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax,
+ double aDirection);
+};
+
+/**
+ * ScrollbarsForWheel manages scrollbars state during wheel operation.
+ * E.g., on some platforms, scrollbars should show only while user attempts to
+ * scroll. At that time, scrollbars which may be possible to scroll by
+ * operation of wheel at the point should show temporarily.
+ */
+
+class ScrollbarsForWheel {
+ public:
+ static void PrepareToScrollText(EventStateManager* aESM,
+ nsIFrame* aTargetFrame,
+ WidgetWheelEvent* aEvent);
+ static void SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget);
+ // Hide all scrollbars (both mActiveOwner's and mActivatedScrollTargets')
+ static void MayInactivate();
+ static void Inactivate();
+ static bool IsActive();
+ static void OwnWheelTransaction(bool aOwn);
+
+ protected:
+ static const size_t kNumberOfTargets = 4;
+ static const DeltaValues directions[kNumberOfTargets];
+ static AutoWeakFrame sActiveOwner;
+ static AutoWeakFrame sActivatedScrollTargets[kNumberOfTargets];
+ static bool sHadWheelStart;
+ static bool sOwnWheelTransaction;
+
+ /**
+ * These two methods are called upon eWheelOperationStart/eWheelOperationEnd
+ * events to show/hide the right scrollbars.
+ */
+ static void TemporarilyActivateAllPossibleScrollTargets(
+ EventStateManager* aESM, nsIFrame* aTargetFrame,
+ WidgetWheelEvent* aEvent);
+ static void DeactivateAllTemporarilyActivatedScrollTargets();
+};
+
+/**
+ * WheelTransaction manages a series of wheel events as a transaction.
+ * While in a transaction, every wheel event should scroll the same scrollable
+ * element even if a different scrollable element is under the mouse cursor.
+ *
+ * Additionally, this class also manages wheel scroll speed acceleration.
+ */
+
+class WheelTransaction {
+ public:
+ /**
+ * Get the target scroll frame for this wheel transaction. This should
+ * the the scrollable fame that will scroll for all wheel events in
+ * this wheel transaction.
+ */
+ static nsIFrame* GetScrollTargetFrame() { return sScrollTargetFrame; }
+ /*
+ * The event target to use for all wheel events in this wheel transaction.
+ * This should be the event target for all wheel events in this wheel
+ * transaction. Note that this frame will likely be a child of the
+ * scrollable frame.
+ */
+ static nsIFrame* GetEventTargetFrame() { return sEventTargetFrame; }
+ static bool HandledByApz() { return sHandledByApz; }
+ static void EndTransaction();
+ /**
+ * WillHandleDefaultAction() is called before handling aWheelEvent on
+ * aScrollTargetWeakFrame given the event target aEventTargetWeakFrame.
+ *
+ * @return false if the caller cannot continue to handle the default
+ * action. Otherwise, true.
+ */
+ static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
+ AutoWeakFrame& aScrollTargetWeakFrame,
+ AutoWeakFrame& aEventTargetWeakFrame);
+ static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
+ nsIFrame* aScrollTargetFrame,
+ nsIFrame* aEventTargetFrame) {
+ AutoWeakFrame scrollTargetWeakFrame(aScrollTargetFrame);
+ AutoWeakFrame eventTargetWeakFrame(aEventTargetFrame);
+ return WillHandleDefaultAction(aWheelEvent, scrollTargetWeakFrame,
+ eventTargetWeakFrame);
+ }
+ static void OnEvent(WidgetEvent* aEvent);
+ static void OnRemoveElement(nsIContent* aContent);
+ static void Shutdown();
+
+ static void OwnScrollbars(bool aOwn);
+
+ static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent);
+
+ protected:
+ static void BeginTransaction(nsIFrame* aScrollTargetFrame,
+ nsIFrame* aEventTargetFrame,
+ const WidgetWheelEvent* aEvent);
+ // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
+ // frame might be destroyed in the event handler.
+ static bool UpdateTransaction(const WidgetWheelEvent* aEvent);
+ static void MayEndTransaction();
+
+ static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
+ static void OnFailToScrollTarget();
+ static void OnTimeout(nsITimer* aTimer, void* aClosure);
+ static void SetTimeout();
+ static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
+ static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
+ static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
+
+ /**
+ * The scrollable element the current wheel event group is bound to.
+ */
+ static AutoWeakFrame sScrollTargetFrame;
+ /**
+ * The initial target of the first wheel event in the wheel event group.
+ * This frame is typically a child of the scrollable element. The wheel
+ * event should target the topmost-event-target. For a wheel event
+ * group, we'll use this target for the entire group.
+ *
+ * See https://w3c.github.io/uievents/#topmost-event-target and
+ * https://w3c.github.io/uievents/#event-type-wheel for details.
+ *
+ * Note: this is only populated if dom.event.wheel-event-groups.enabled is
+ * set.
+ */
+ static AutoWeakFrame sEventTargetFrame;
+ /**
+ * The wheel events for this transaction are handled by APZ.
+ */
+ static bool sHandledByApz;
+ static uint32_t sTime; // in milliseconds
+ static uint32_t sMouseMoved; // in milliseconds
+ static nsITimer* sTimer;
+ static int32_t sScrollSeriesCounter;
+ static bool sOwnScrollbars;
+};
+
+// For some kinds of scrollings, the delta values of WidgetWheelEvent are
+// possbile to be adjusted. For example, the user has configured the pref to let
+// [vertical wheel + Shift key] to perform horizontal scrolling instead of
+// vertical scrolling.
+// The values in this enumeration list all kinds of scrollings whose delta
+// values are possible to be adjusted.
+enum class WheelDeltaAdjustmentStrategy : uint8_t {
+ // There is no strategy, don't adjust delta values in any cases.
+ eNone,
+ // This strategy means we're receiving a horizontalized scroll, so we should
+ // apply horizontalization strategy for its delta values.
+ // Horizontalized scrolling means treating vertical wheel scrolling as
+ // horizontal scrolling by adjusting delta values.
+ // It's important to keep in mind with the percise concept of horizontalized
+ // scrolling: Delta values are *ONLY* going to be adjusted during the process
+ // of its default action handling; in views of any programmes other than the
+ // default action handler, such as a DOM event listener or a plugin, delta
+ // values are never going to be adjusted, they will still retrive original
+ // delta values when horizontalization occured for default actions.
+ eHorizontalize,
+ // The following two strategies mean we're receving an auto-dir scroll, so we
+ // should apply auto-dir adjustment to the delta of the wheel event if needed.
+ // Auto-dir is a feature which treats any single-wheel scroll as a scroll in
+ // the only one scrollable direction if the target has only one scrollable
+ // direction. For example, if the user scrolls a vertical wheel inside a
+ // target which is horizontally scrollable but vertical unscrollable, then the
+ // vertical scroll is converted to a horizontal scroll for that target.
+ // So why do we need two different strategies for auto-dir scrolling? That's
+ // because when a wheel scroll is converted due to auto-dir, there is one
+ // thing called "honoured target" which decides which side the converted
+ // scroll goes towards. If the content of the honoured target horizontally
+ // is RTL content, then an upward scroll maps to a rightward scroll and a
+ // downward scroll maps to a leftward scroll; otherwise, an upward scroll maps
+ // to a leftward scroll and a downward scroll maps to a rightward scroll.
+ // |eAutoDir| considers the scrolling target as the honoured target.
+ // |eAutoDirWithRootHonour| takes the root element of the document with the
+ // scrolling element, and considers that as the honoured target. But note that
+ // there's one exception: for targets in an HTML document, the real root
+ // element(I.e. the <html> element) is typically not considered as a root
+ // element, but the <body> element is typically considered as a root element.
+ // If there is no <body> element, then consider the <html> element instead.
+ // And also note that like |eHorizontalize|, delta values are *ONLY* going to
+ // be adjusted during the process of its default action handling; in views of
+ // any programmes other than the default action handler, such as a DOM event
+ // listener or a plugin, delta values are never going to be adjusted.
+ eAutoDir,
+ eAutoDirWithRootHonour,
+ // Not an actual strategy. This is just used as an upper bound for
+ // ContiguousEnumSerializer.
+ eSentinel,
+};
+
+/**
+ * When a *pure* vertical wheel event should be treated as if it was a
+ * horizontal scroll because the user wants to horizontalize the wheel scroll,
+ * an instance of this class will adjust the delta values upon calling
+ * Horizontalize(). And the horizontalized delta values will be restored
+ * automatically when the instance of this class is being destructed. Or you can
+ * restore them in advance by calling CancelHorizontalization().
+ */
+class MOZ_STACK_CLASS WheelDeltaHorizontalizer final {
+ public:
+ /**
+ * @param aWheelEvent A wheel event whose delta values will be adjusted
+ * upon calling Horizontalize().
+ */
+ explicit WheelDeltaHorizontalizer(WidgetWheelEvent& aWheelEvent)
+ : mWheelEvent(aWheelEvent),
+ mOldDeltaX(0.0),
+ mOldDeltaZ(0.0),
+ mOldOverflowDeltaX(0.0),
+ mOldLineOrPageDeltaX(0),
+ mHorizontalized(false) {}
+ /**
+ * Converts vertical scrolling into horizontal scrolling by adjusting the
+ * its delta values.
+ */
+ void Horizontalize();
+ ~WheelDeltaHorizontalizer();
+ void CancelHorizontalization();
+
+ private:
+ WidgetWheelEvent& mWheelEvent;
+ double mOldDeltaX;
+ double mOldDeltaZ;
+ double mOldOverflowDeltaX;
+ int32_t mOldLineOrPageDeltaX;
+ bool mHorizontalized;
+};
+
+/**
+ * This class is used to adjust the delta values for wheel scrolling with the
+ * auto-dir functionality.
+ * A traditional wheel scroll only allows the user use the wheel in the same
+ * scrollable direction as that of the scrolling target to scroll the target,
+ * whereas an auto-dir scroll lets the user use any wheel(either a vertical
+ * wheel or a horizontal tilt wheel) to scroll a frame which is scrollable in
+ * only one direction. For detailed information on auto-dir scrolling,
+ * @see mozilla::WheelDeltaAdjustmentStrategy.
+ */
+class MOZ_STACK_CLASS AutoDirWheelDeltaAdjuster {
+ protected:
+ /**
+ * @param aDeltaX DeltaX for a wheel event whose delta values will
+ * be adjusted upon calling Adjust() when
+ * ShouldBeAdjusted() returns true.
+ * @param aDeltaY DeltaY for a wheel event, like DeltaX.
+ */
+ AutoDirWheelDeltaAdjuster(double& aDeltaX, double& aDeltaY)
+ : mDeltaX(aDeltaX),
+ mDeltaY(aDeltaY),
+ mCheckedIfShouldBeAdjusted(false),
+ mShouldBeAdjusted(false) {}
+
+ public:
+ /**
+ * Gets whether the values of the delta should be adjusted for auto-dir
+ * scrolling. Note that if Adjust() has been called, this function simply
+ * returns false.
+ *
+ * @return true if the delta should be adjusted; otherwise false.
+ */
+ bool ShouldBeAdjusted();
+ /**
+ * Adjusts the values of the delta values for auto-dir scrolling when
+ * ShouldBeAdjusted() returns true. If you call it when ShouldBeAdjusted()
+ * returns false, this function will simply do nothing.
+ */
+ void Adjust();
+
+ private:
+ /**
+ * Called by Adjust() if Adjust() successfully adjusted the delta values.
+ */
+ virtual void OnAdjusted() {}
+
+ virtual bool CanScrollAlongXAxis() const = 0;
+ virtual bool CanScrollAlongYAxis() const = 0;
+ virtual bool CanScrollUpwards() const = 0;
+ virtual bool CanScrollDownwards() const = 0;
+ virtual bool CanScrollLeftwards() const = 0;
+ virtual bool CanScrollRightwards() const = 0;
+
+ /**
+ * Gets whether the horizontal content starts at rightside.
+ *
+ * @return If the content is in vertical-RTL writing mode(E.g. "writing-mode:
+ * vertical-rl" in CSS), or if it's in horizontal-RTL writing-mode
+ * (E.g. "writing-mode: horizontal-tb; direction: rtl;" in CSS), then
+ * this function returns true. From the representation perspective,
+ * frames whose horizontal contents start at rightside also cause
+ * their horizontal scrollbars, if any, initially start at rightside.
+ * So we can also learn about the initial side of the horizontal
+ * scrollbar for the frame by calling this function.
+ */
+ virtual bool IsHorizontalContentRightToLeft() const = 0;
+
+ 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_