diff options
Diffstat (limited to 'dom/events/WheelHandlingHelper.h')
-rw-r--r-- | dom/events/WheelHandlingHelper.h | 440 |
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_ |