Bug 1826136 [wpt PR 39338] - Update wpt metadata, a=testonly
[gecko.git] / dom / events / WheelHandlingHelper.h
blob95e0848f085aba63a348709243905eb7201099c4
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"
12 #include "nsCoord.h"
13 #include "nsIFrame.h" // for AutoWeakFrame only
14 #include "nsPoint.h"
16 class nsIFrame;
17 class nsIScrollableFrame;
18 class nsITimer;
20 namespace mozilla {
22 class EventStateManager;
24 /**
25 * DeltaValues stores two delta values which are along X and Y axis. This is
26 * useful for arguments and results of some methods.
29 struct DeltaValues {
30 DeltaValues() : deltaX(0.0), deltaY(0.0) {}
32 DeltaValues(double aDeltaX, double aDeltaY)
33 : deltaX(aDeltaX), deltaY(aDeltaY) {}
35 explicit DeltaValues(WidgetWheelEvent* aEvent);
37 double deltaX;
38 double deltaY;
41 /**
42 * WheelHandlingUtils provides some static methods which are useful at handling
43 * wheel events.
46 class WheelHandlingUtils {
47 public:
48 /**
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).
52 * Otherwise, false.
54 static bool CanScrollOn(nsIFrame* aFrame, double aDirectionX,
55 double aDirectionY);
56 /**
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,
61 double aDirectionY);
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);
69 private:
70 static bool CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax,
71 double aDirection);
74 /**
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 {
82 public:
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);
93 protected:
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 {
120 public:
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
131 * scrollable frame.
133 static nsIFrame* GetEventTargetFrame() { return sEventTargetFrame; }
134 static void EndTransaction();
136 * WillHandleDefaultAction() is called before handling aWheelEvent on
137 * aScrollTargetWeakFrame given the event target aEventTargetWeakFrame.
139 * @return false if the caller cannot continue to handle the default
140 * action. Otherwise, true.
142 static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
143 AutoWeakFrame& aScrollTargetWeakFrame,
144 AutoWeakFrame& aEventTargetWeakFrame);
145 static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
146 nsIFrame* aScrollTargetFrame,
147 nsIFrame* aEventTargetFrame) {
148 AutoWeakFrame scrollTargetWeakFrame(aScrollTargetFrame);
149 AutoWeakFrame eventTargetWeakFrame(aEventTargetFrame);
150 return WillHandleDefaultAction(aWheelEvent, scrollTargetWeakFrame,
151 eventTargetWeakFrame);
153 static void OnEvent(WidgetEvent* aEvent);
154 static void OnRemoveElement(nsIContent* aContent);
155 static void Shutdown();
157 static void OwnScrollbars(bool aOwn);
159 static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent);
161 protected:
162 static void BeginTransaction(nsIFrame* aScrollTargetFrame,
163 nsIFrame* aEventTargetFrame,
164 const WidgetWheelEvent* aEvent);
165 // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
166 // frame might be destroyed in the event handler.
167 static bool UpdateTransaction(const WidgetWheelEvent* aEvent);
168 static void MayEndTransaction();
170 static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
171 static void OnFailToScrollTarget();
172 static void OnTimeout(nsITimer* aTimer, void* aClosure);
173 static void SetTimeout();
174 static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
175 static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
176 static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
179 * The scrollable element the current wheel event group is bound to.
181 static AutoWeakFrame sScrollTargetFrame;
183 * The initial target of the first wheel event in the wheel event group.
184 * This frame is typically a child of the scrollable element. The wheel
185 * event should target the topmost-event-target. For a wheel event
186 * group, we'll use this target for the entire group.
188 * See https://w3c.github.io/uievents/#topmost-event-target and
189 * https://w3c.github.io/uievents/#event-type-wheel for details.
191 * Note: this is only populated if dom.event.wheel-event-groups.enabled is
192 * set.
194 static AutoWeakFrame sEventTargetFrame;
195 static uint32_t sTime; // in milliseconds
196 static uint32_t sMouseMoved; // in milliseconds
197 static nsITimer* sTimer;
198 static int32_t sScrollSeriesCounter;
199 static bool sOwnScrollbars;
202 // For some kinds of scrollings, the delta values of WidgetWheelEvent are
203 // possbile to be adjusted. For example, the user has configured the pref to let
204 // [vertical wheel + Shift key] to perform horizontal scrolling instead of
205 // vertical scrolling.
206 // The values in this enumeration list all kinds of scrollings whose delta
207 // values are possible to be adjusted.
208 enum class WheelDeltaAdjustmentStrategy : uint8_t {
209 // There is no strategy, don't adjust delta values in any cases.
210 eNone,
211 // This strategy means we're receiving a horizontalized scroll, so we should
212 // apply horizontalization strategy for its delta values.
213 // Horizontalized scrolling means treating vertical wheel scrolling as
214 // horizontal scrolling by adjusting delta values.
215 // It's important to keep in mind with the percise concept of horizontalized
216 // scrolling: Delta values are *ONLY* going to be adjusted during the process
217 // of its default action handling; in views of any programmes other than the
218 // default action handler, such as a DOM event listener or a plugin, delta
219 // values are never going to be adjusted, they will still retrive original
220 // delta values when horizontalization occured for default actions.
221 eHorizontalize,
222 // The following two strategies mean we're receving an auto-dir scroll, so we
223 // should apply auto-dir adjustment to the delta of the wheel event if needed.
224 // Auto-dir is a feature which treats any single-wheel scroll as a scroll in
225 // the only one scrollable direction if the target has only one scrollable
226 // direction. For example, if the user scrolls a vertical wheel inside a
227 // target which is horizontally scrollable but vertical unscrollable, then the
228 // vertical scroll is converted to a horizontal scroll for that target.
229 // So why do we need two different strategies for auto-dir scrolling? That's
230 // because when a wheel scroll is converted due to auto-dir, there is one
231 // thing called "honoured target" which decides which side the converted
232 // scroll goes towards. If the content of the honoured target horizontally
233 // is RTL content, then an upward scroll maps to a rightward scroll and a
234 // downward scroll maps to a leftward scroll; otherwise, an upward scroll maps
235 // to a leftward scroll and a downward scroll maps to a rightward scroll.
236 // |eAutoDir| considers the scrolling target as the honoured target.
237 // |eAutoDirWithRootHonour| takes the root element of the document with the
238 // scrolling element, and considers that as the honoured target. But note that
239 // there's one exception: for targets in an HTML document, the real root
240 // element(I.e. the <html> element) is typically not considered as a root
241 // element, but the <body> element is typically considered as a root element.
242 // If there is no <body> element, then consider the <html> element instead.
243 // And also note that like |eHorizontalize|, delta values are *ONLY* going to
244 // be adjusted during the process of its default action handling; in views of
245 // any programmes other than the default action handler, such as a DOM event
246 // listener or a plugin, delta values are never going to be adjusted.
247 eAutoDir,
248 eAutoDirWithRootHonour,
249 // Not an actual strategy. This is just used as an upper bound for
250 // ContiguousEnumSerializer.
251 eSentinel,
255 * When a *pure* vertical wheel event should be treated as if it was a
256 * horizontal scroll because the user wants to horizontalize the wheel scroll,
257 * an instance of this class will adjust the delta values upon calling
258 * Horizontalize(). And the horizontalized delta values will be restored
259 * automatically when the instance of this class is being destructed. Or you can
260 * restore them in advance by calling CancelHorizontalization().
262 class MOZ_STACK_CLASS WheelDeltaHorizontalizer final {
263 public:
265 * @param aWheelEvent A wheel event whose delta values will be adjusted
266 * upon calling Horizontalize().
268 explicit WheelDeltaHorizontalizer(WidgetWheelEvent& aWheelEvent)
269 : mWheelEvent(aWheelEvent),
270 mOldDeltaX(0.0),
271 mOldDeltaZ(0.0),
272 mOldOverflowDeltaX(0.0),
273 mOldLineOrPageDeltaX(0),
274 mHorizontalized(false) {}
276 * Converts vertical scrolling into horizontal scrolling by adjusting the
277 * its delta values.
279 void Horizontalize();
280 ~WheelDeltaHorizontalizer();
281 void CancelHorizontalization();
283 private:
284 WidgetWheelEvent& mWheelEvent;
285 double mOldDeltaX;
286 double mOldDeltaZ;
287 double mOldOverflowDeltaX;
288 int32_t mOldLineOrPageDeltaX;
289 bool mHorizontalized;
293 * This class is used to adjust the delta values for wheel scrolling with the
294 * auto-dir functionality.
295 * A traditional wheel scroll only allows the user use the wheel in the same
296 * scrollable direction as that of the scrolling target to scroll the target,
297 * whereas an auto-dir scroll lets the user use any wheel(either a vertical
298 * wheel or a horizontal tilt wheel) to scroll a frame which is scrollable in
299 * only one direction. For detailed information on auto-dir scrolling,
300 * @see mozilla::WheelDeltaAdjustmentStrategy.
302 class MOZ_STACK_CLASS AutoDirWheelDeltaAdjuster {
303 protected:
305 * @param aDeltaX DeltaX for a wheel event whose delta values will
306 * be adjusted upon calling Adjust() when
307 * ShouldBeAdjusted() returns true.
308 * @param aDeltaY DeltaY for a wheel event, like DeltaX.
310 AutoDirWheelDeltaAdjuster(double& aDeltaX, double& aDeltaY)
311 : mDeltaX(aDeltaX),
312 mDeltaY(aDeltaY),
313 mCheckedIfShouldBeAdjusted(false),
314 mShouldBeAdjusted(false) {}
316 public:
318 * Gets whether the values of the delta should be adjusted for auto-dir
319 * scrolling. Note that if Adjust() has been called, this function simply
320 * returns false.
322 * @return true if the delta should be adjusted; otherwise false.
324 bool ShouldBeAdjusted();
326 * Adjusts the values of the delta values for auto-dir scrolling when
327 * ShouldBeAdjusted() returns true. If you call it when ShouldBeAdjusted()
328 * returns false, this function will simply do nothing.
330 void Adjust();
332 private:
334 * Called by Adjust() if Adjust() successfully adjusted the delta values.
336 virtual void OnAdjusted() {}
338 virtual bool CanScrollAlongXAxis() const = 0;
339 virtual bool CanScrollAlongYAxis() const = 0;
340 virtual bool CanScrollUpwards() const = 0;
341 virtual bool CanScrollDownwards() const = 0;
342 virtual bool CanScrollLeftwards() const = 0;
343 virtual bool CanScrollRightwards() const = 0;
346 * Gets whether the horizontal content starts at rightside.
348 * @return If the content is in vertical-RTL writing mode(E.g. "writing-mode:
349 * vertical-rl" in CSS), or if it's in horizontal-RTL writing-mode
350 * (E.g. "writing-mode: horizontal-tb; direction: rtl;" in CSS), then
351 * this function returns true. From the representation perspective,
352 * frames whose horizontal contents start at rightside also cause
353 * their horizontal scrollbars, if any, initially start at rightside.
354 * So we can also learn about the initial side of the horizontal
355 * scrollbar for the frame by calling this function.
357 virtual bool IsHorizontalContentRightToLeft() const = 0;
359 protected:
360 double& mDeltaX;
361 double& mDeltaY;
363 private:
364 bool mCheckedIfShouldBeAdjusted;
365 bool mShouldBeAdjusted;
369 * This is the implementation of AutoDirWheelDeltaAdjuster for EventStateManager
371 * Detailed comments about some member functions are given in the base class
372 * AutoDirWheelDeltaAdjuster.
374 class MOZ_STACK_CLASS ESMAutoDirWheelDeltaAdjuster final
375 : public AutoDirWheelDeltaAdjuster {
376 public:
378 * @param aEvent The auto-dir wheel scroll event.
379 * @param aScrollFrame The scroll target for the event.
380 * @param aHonoursRoot If set to true, the honoured frame is the root
381 * frame in the same document where the target is;
382 * If false, the honoured frame is the scroll
383 * target. For the concept of an honoured target,
384 * @see mozilla::WheelDeltaAdjustmentStrategy
386 ESMAutoDirWheelDeltaAdjuster(WidgetWheelEvent& aEvent, nsIFrame& aScrollFrame,
387 bool aHonoursRoot);
389 private:
390 virtual void OnAdjusted() override;
391 virtual bool CanScrollAlongXAxis() const override;
392 virtual bool CanScrollAlongYAxis() const override;
393 virtual bool CanScrollUpwards() const override;
394 virtual bool CanScrollDownwards() const override;
395 virtual bool CanScrollLeftwards() const override;
396 virtual bool CanScrollRightwards() const override;
397 virtual bool IsHorizontalContentRightToLeft() const override;
399 nsIScrollableFrame* mScrollTargetFrame;
400 bool mIsHorizontalContentRightToLeft;
402 int32_t& mLineOrPageDeltaX;
403 int32_t& mLineOrPageDeltaY;
404 double& mOverflowDeltaX;
405 double& mOverflowDeltaY;
409 * This class is used for restoring the delta in an auto-dir wheel.
411 * An instance of this calss monitors auto-dir adjustment which may happen
412 * during its lifetime. If the delta values is adjusted during its lifetime, the
413 * instance will restore the adjusted delta when it's being destrcuted.
415 class MOZ_STACK_CLASS ESMAutoDirWheelDeltaRestorer final {
416 public:
418 * @param aEvent The wheel scroll event to be monitored.
420 explicit ESMAutoDirWheelDeltaRestorer(WidgetWheelEvent& aEvent);
421 ~ESMAutoDirWheelDeltaRestorer();
423 private:
424 WidgetWheelEvent& mEvent;
425 double mOldDeltaX;
426 double mOldDeltaY;
427 int32_t mOldLineOrPageDeltaX;
428 int32_t mOldLineOrPageDeltaY;
429 double mOldOverflowDeltaX;
430 double mOldOverflowDeltaY;
433 } // namespace mozilla
435 #endif // mozilla_WheelHandlingHelper_h_