1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_WheelHandlingHelper_h_
8 #define mozilla_WheelHandlingHelper_h_
10 #include "mozilla/Attributes.h"
11 #include "mozilla/EventForwards.h"
13 #include "nsIFrame.h" // for AutoWeakFrame only
17 class nsIScrollableFrame
;
22 class EventStateManager
;
25 * DeltaValues stores two delta values which are along X and Y axis. This is
26 * useful for arguments and results of some methods.
30 DeltaValues() : deltaX(0.0), deltaY(0.0) {}
32 DeltaValues(double aDeltaX
, double aDeltaY
)
33 : deltaX(aDeltaX
), deltaY(aDeltaY
) {}
35 explicit DeltaValues(WidgetWheelEvent
* aEvent
);
42 * WheelHandlingUtils provides some static methods which are useful at handling
46 class WheelHandlingUtils
{
49 * Returns true if aFrame is a scrollable frame and it can be scrolled to
50 * either aDirectionX or aDirectionY along each axis. Or if aFrame is a
51 * plugin frame (in this case, aDirectionX and aDirectionY are ignored).
54 static bool CanScrollOn(nsIFrame
* aFrame
, double aDirectionX
,
57 * Returns true if the scrollable frame can be scrolled to either aDirectionX
58 * or aDirectionY along each axis. Otherwise, false.
60 static bool CanScrollOn(nsIScrollableFrame
* aScrollFrame
, double aDirectionX
,
63 // For more details about the concept of a disregarded direction, refer to the
64 // code in struct mozilla::layers::ScrollMetadata which defines
65 // mDisregardedDirection.
66 static Maybe
<layers::ScrollDirection
> GetDisregardedWheelScrollDirection(
67 const nsIFrame
* aFrame
);
70 static bool CanScrollInRange(nscoord aMin
, nscoord aValue
, nscoord aMax
,
75 * ScrollbarsForWheel manages scrollbars state during wheel operation.
76 * E.g., on some platforms, scrollbars should show only while user attempts to
77 * scroll. At that time, scrollbars which may be possible to scroll by
78 * operation of wheel at the point should show temporarily.
81 class ScrollbarsForWheel
{
83 static void PrepareToScrollText(EventStateManager
* aESM
,
84 nsIFrame
* aTargetFrame
,
85 WidgetWheelEvent
* aEvent
);
86 static void SetActiveScrollTarget(nsIScrollableFrame
* aScrollTarget
);
87 // Hide all scrollbars (both mActiveOwner's and mActivatedScrollTargets')
88 static void MayInactivate();
89 static void Inactivate();
90 static bool IsActive();
91 static void OwnWheelTransaction(bool aOwn
);
94 static const size_t kNumberOfTargets
= 4;
95 static const DeltaValues directions
[kNumberOfTargets
];
96 static AutoWeakFrame sActiveOwner
;
97 static AutoWeakFrame sActivatedScrollTargets
[kNumberOfTargets
];
98 static bool sHadWheelStart
;
99 static bool sOwnWheelTransaction
;
102 * These two methods are called upon eWheelOperationStart/eWheelOperationEnd
103 * events to show/hide the right scrollbars.
105 static void TemporarilyActivateAllPossibleScrollTargets(
106 EventStateManager
* aESM
, nsIFrame
* aTargetFrame
,
107 WidgetWheelEvent
* aEvent
);
108 static void DeactivateAllTemporarilyActivatedScrollTargets();
112 * WheelTransaction manages a series of wheel events as a transaction.
113 * While in a transaction, every wheel event should scroll the same scrollable
114 * element even if a different scrollable element is under the mouse cursor.
116 * Additionally, this class also manages wheel scroll speed acceleration.
119 class WheelTransaction
{
122 * Get the target scroll frame for this wheel transaction. This should
123 * the the scrollable fame that will scroll for all wheel events in
124 * this wheel transaction.
126 static nsIFrame
* GetScrollTargetFrame() { return sScrollTargetFrame
; }
128 * The event target to use for all wheel events in this wheel transaction.
129 * This should be the event target for all wheel events in this wheel
130 * transaction. Note that this frame will likely be a child of the
133 static nsIFrame
* GetEventTargetFrame() { return sEventTargetFrame
; }
134 static bool HandledByApz() { return sHandledByApz
; }
135 static void EndTransaction();
137 * WillHandleDefaultAction() is called before handling aWheelEvent on
138 * aScrollTargetWeakFrame given the event target aEventTargetWeakFrame.
140 * @return false if the caller cannot continue to handle the default
141 * action. Otherwise, true.
143 static bool WillHandleDefaultAction(WidgetWheelEvent
* aWheelEvent
,
144 AutoWeakFrame
& aScrollTargetWeakFrame
,
145 AutoWeakFrame
& aEventTargetWeakFrame
);
146 static bool WillHandleDefaultAction(WidgetWheelEvent
* aWheelEvent
,
147 nsIFrame
* aScrollTargetFrame
,
148 nsIFrame
* aEventTargetFrame
) {
149 AutoWeakFrame
scrollTargetWeakFrame(aScrollTargetFrame
);
150 AutoWeakFrame
eventTargetWeakFrame(aEventTargetFrame
);
151 return WillHandleDefaultAction(aWheelEvent
, scrollTargetWeakFrame
,
152 eventTargetWeakFrame
);
154 static void OnEvent(WidgetEvent
* aEvent
);
155 static void OnRemoveElement(nsIContent
* aContent
);
156 static void Shutdown();
158 static void OwnScrollbars(bool aOwn
);
160 static DeltaValues
AccelerateWheelDelta(WidgetWheelEvent
* aEvent
);
163 static void BeginTransaction(nsIFrame
* aScrollTargetFrame
,
164 nsIFrame
* aEventTargetFrame
,
165 const WidgetWheelEvent
* aEvent
);
166 // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
167 // frame might be destroyed in the event handler.
168 static bool UpdateTransaction(const WidgetWheelEvent
* aEvent
);
169 static void MayEndTransaction();
171 static LayoutDeviceIntPoint
GetScreenPoint(WidgetGUIEvent
* aEvent
);
172 static void OnFailToScrollTarget();
173 static void OnTimeout(nsITimer
* aTimer
, void* aClosure
);
174 static void SetTimeout();
175 static DeltaValues
OverrideSystemScrollSpeed(WidgetWheelEvent
* aEvent
);
176 static double ComputeAcceleratedWheelDelta(double aDelta
, int32_t aFactor
);
177 static bool OutOfTime(uint32_t aBaseTime
, uint32_t aThreshold
);
180 * The scrollable element the current wheel event group is bound to.
182 static AutoWeakFrame sScrollTargetFrame
;
184 * The initial target of the first wheel event in the wheel event group.
185 * This frame is typically a child of the scrollable element. The wheel
186 * event should target the topmost-event-target. For a wheel event
187 * group, we'll use this target for the entire group.
189 * See https://w3c.github.io/uievents/#topmost-event-target and
190 * https://w3c.github.io/uievents/#event-type-wheel for details.
192 * Note: this is only populated if dom.event.wheel-event-groups.enabled is
195 static AutoWeakFrame sEventTargetFrame
;
197 * The wheel events for this transaction are handled by APZ.
199 static bool sHandledByApz
;
200 static uint32_t sTime
; // in milliseconds
201 static uint32_t sMouseMoved
; // in milliseconds
202 static nsITimer
* sTimer
;
203 static int32_t sScrollSeriesCounter
;
204 static bool sOwnScrollbars
;
207 // For some kinds of scrollings, the delta values of WidgetWheelEvent are
208 // possbile to be adjusted. For example, the user has configured the pref to let
209 // [vertical wheel + Shift key] to perform horizontal scrolling instead of
210 // vertical scrolling.
211 // The values in this enumeration list all kinds of scrollings whose delta
212 // values are possible to be adjusted.
213 enum class WheelDeltaAdjustmentStrategy
: uint8_t {
214 // There is no strategy, don't adjust delta values in any cases.
216 // This strategy means we're receiving a horizontalized scroll, so we should
217 // apply horizontalization strategy for its delta values.
218 // Horizontalized scrolling means treating vertical wheel scrolling as
219 // horizontal scrolling by adjusting delta values.
220 // It's important to keep in mind with the percise concept of horizontalized
221 // scrolling: Delta values are *ONLY* going to be adjusted during the process
222 // of its default action handling; in views of any programmes other than the
223 // default action handler, such as a DOM event listener or a plugin, delta
224 // values are never going to be adjusted, they will still retrive original
225 // delta values when horizontalization occured for default actions.
227 // The following two strategies mean we're receving an auto-dir scroll, so we
228 // should apply auto-dir adjustment to the delta of the wheel event if needed.
229 // Auto-dir is a feature which treats any single-wheel scroll as a scroll in
230 // the only one scrollable direction if the target has only one scrollable
231 // direction. For example, if the user scrolls a vertical wheel inside a
232 // target which is horizontally scrollable but vertical unscrollable, then the
233 // vertical scroll is converted to a horizontal scroll for that target.
234 // So why do we need two different strategies for auto-dir scrolling? That's
235 // because when a wheel scroll is converted due to auto-dir, there is one
236 // thing called "honoured target" which decides which side the converted
237 // scroll goes towards. If the content of the honoured target horizontally
238 // is RTL content, then an upward scroll maps to a rightward scroll and a
239 // downward scroll maps to a leftward scroll; otherwise, an upward scroll maps
240 // to a leftward scroll and a downward scroll maps to a rightward scroll.
241 // |eAutoDir| considers the scrolling target as the honoured target.
242 // |eAutoDirWithRootHonour| takes the root element of the document with the
243 // scrolling element, and considers that as the honoured target. But note that
244 // there's one exception: for targets in an HTML document, the real root
245 // element(I.e. the <html> element) is typically not considered as a root
246 // element, but the <body> element is typically considered as a root element.
247 // If there is no <body> element, then consider the <html> element instead.
248 // And also note that like |eHorizontalize|, delta values are *ONLY* going to
249 // be adjusted during the process of its default action handling; in views of
250 // any programmes other than the default action handler, such as a DOM event
251 // listener or a plugin, delta values are never going to be adjusted.
253 eAutoDirWithRootHonour
,
254 // Not an actual strategy. This is just used as an upper bound for
255 // ContiguousEnumSerializer.
260 * When a *pure* vertical wheel event should be treated as if it was a
261 * horizontal scroll because the user wants to horizontalize the wheel scroll,
262 * an instance of this class will adjust the delta values upon calling
263 * Horizontalize(). And the horizontalized delta values will be restored
264 * automatically when the instance of this class is being destructed. Or you can
265 * restore them in advance by calling CancelHorizontalization().
267 class MOZ_STACK_CLASS WheelDeltaHorizontalizer final
{
270 * @param aWheelEvent A wheel event whose delta values will be adjusted
271 * upon calling Horizontalize().
273 explicit WheelDeltaHorizontalizer(WidgetWheelEvent
& aWheelEvent
)
274 : mWheelEvent(aWheelEvent
),
277 mOldOverflowDeltaX(0.0),
278 mOldLineOrPageDeltaX(0),
279 mHorizontalized(false) {}
281 * Converts vertical scrolling into horizontal scrolling by adjusting the
284 void Horizontalize();
285 ~WheelDeltaHorizontalizer();
286 void CancelHorizontalization();
289 WidgetWheelEvent
& mWheelEvent
;
292 double mOldOverflowDeltaX
;
293 int32_t mOldLineOrPageDeltaX
;
294 bool mHorizontalized
;
298 * This class is used to adjust the delta values for wheel scrolling with the
299 * auto-dir functionality.
300 * A traditional wheel scroll only allows the user use the wheel in the same
301 * scrollable direction as that of the scrolling target to scroll the target,
302 * whereas an auto-dir scroll lets the user use any wheel(either a vertical
303 * wheel or a horizontal tilt wheel) to scroll a frame which is scrollable in
304 * only one direction. For detailed information on auto-dir scrolling,
305 * @see mozilla::WheelDeltaAdjustmentStrategy.
307 class MOZ_STACK_CLASS AutoDirWheelDeltaAdjuster
{
310 * @param aDeltaX DeltaX for a wheel event whose delta values will
311 * be adjusted upon calling Adjust() when
312 * ShouldBeAdjusted() returns true.
313 * @param aDeltaY DeltaY for a wheel event, like DeltaX.
315 AutoDirWheelDeltaAdjuster(double& aDeltaX
, double& aDeltaY
)
318 mCheckedIfShouldBeAdjusted(false),
319 mShouldBeAdjusted(false) {}
323 * Gets whether the values of the delta should be adjusted for auto-dir
324 * scrolling. Note that if Adjust() has been called, this function simply
327 * @return true if the delta should be adjusted; otherwise false.
329 bool ShouldBeAdjusted();
331 * Adjusts the values of the delta values for auto-dir scrolling when
332 * ShouldBeAdjusted() returns true. If you call it when ShouldBeAdjusted()
333 * returns false, this function will simply do nothing.
339 * Called by Adjust() if Adjust() successfully adjusted the delta values.
341 virtual void OnAdjusted() {}
343 virtual bool CanScrollAlongXAxis() const = 0;
344 virtual bool CanScrollAlongYAxis() const = 0;
345 virtual bool CanScrollUpwards() const = 0;
346 virtual bool CanScrollDownwards() const = 0;
347 virtual bool CanScrollLeftwards() const = 0;
348 virtual bool CanScrollRightwards() const = 0;
351 * Gets whether the horizontal content starts at rightside.
353 * @return If the content is in vertical-RTL writing mode(E.g. "writing-mode:
354 * vertical-rl" in CSS), or if it's in horizontal-RTL writing-mode
355 * (E.g. "writing-mode: horizontal-tb; direction: rtl;" in CSS), then
356 * this function returns true. From the representation perspective,
357 * frames whose horizontal contents start at rightside also cause
358 * their horizontal scrollbars, if any, initially start at rightside.
359 * So we can also learn about the initial side of the horizontal
360 * scrollbar for the frame by calling this function.
362 virtual bool IsHorizontalContentRightToLeft() const = 0;
369 bool mCheckedIfShouldBeAdjusted
;
370 bool mShouldBeAdjusted
;
374 * This is the implementation of AutoDirWheelDeltaAdjuster for EventStateManager
376 * Detailed comments about some member functions are given in the base class
377 * AutoDirWheelDeltaAdjuster.
379 class MOZ_STACK_CLASS ESMAutoDirWheelDeltaAdjuster final
380 : public AutoDirWheelDeltaAdjuster
{
383 * @param aEvent The auto-dir wheel scroll event.
384 * @param aScrollFrame The scroll target for the event.
385 * @param aHonoursRoot If set to true, the honoured frame is the root
386 * frame in the same document where the target is;
387 * If false, the honoured frame is the scroll
388 * target. For the concept of an honoured target,
389 * @see mozilla::WheelDeltaAdjustmentStrategy
391 ESMAutoDirWheelDeltaAdjuster(WidgetWheelEvent
& aEvent
, nsIFrame
& aScrollFrame
,
395 virtual void OnAdjusted() override
;
396 virtual bool CanScrollAlongXAxis() const override
;
397 virtual bool CanScrollAlongYAxis() const override
;
398 virtual bool CanScrollUpwards() const override
;
399 virtual bool CanScrollDownwards() const override
;
400 virtual bool CanScrollLeftwards() const override
;
401 virtual bool CanScrollRightwards() const override
;
402 virtual bool IsHorizontalContentRightToLeft() const override
;
404 nsIScrollableFrame
* mScrollTargetFrame
;
405 bool mIsHorizontalContentRightToLeft
;
407 int32_t& mLineOrPageDeltaX
;
408 int32_t& mLineOrPageDeltaY
;
409 double& mOverflowDeltaX
;
410 double& mOverflowDeltaY
;
414 * This class is used for restoring the delta in an auto-dir wheel.
416 * An instance of this calss monitors auto-dir adjustment which may happen
417 * during its lifetime. If the delta values is adjusted during its lifetime, the
418 * instance will restore the adjusted delta when it's being destrcuted.
420 class MOZ_STACK_CLASS ESMAutoDirWheelDeltaRestorer final
{
423 * @param aEvent The wheel scroll event to be monitored.
425 explicit ESMAutoDirWheelDeltaRestorer(WidgetWheelEvent
& aEvent
);
426 ~ESMAutoDirWheelDeltaRestorer();
429 WidgetWheelEvent
& mEvent
;
432 int32_t mOldLineOrPageDeltaX
;
433 int32_t mOldLineOrPageDeltaY
;
434 double mOldOverflowDeltaX
;
435 double mOldOverflowDeltaY
;
438 } // namespace mozilla
440 #endif // mozilla_WheelHandlingHelper_h_