/src/mozilla-central/dom/events/WheelHandlingHelper.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef mozilla_WheelHandlingHelper_h_ |
8 | | #define mozilla_WheelHandlingHelper_h_ |
9 | | |
10 | | #include "mozilla/Attributes.h" |
11 | | #include "mozilla/EventForwards.h" |
12 | | #include "nsCoord.h" |
13 | | #include "nsIFrame.h" |
14 | | #include "nsPoint.h" |
15 | | |
16 | | class nsIScrollableFrame; |
17 | | class nsITimer; |
18 | | |
19 | | namespace mozilla { |
20 | | |
21 | | class EventStateManager; |
22 | | |
23 | | /** |
24 | | * DeltaValues stores two delta values which are along X and Y axis. This is |
25 | | * useful for arguments and results of some methods. |
26 | | */ |
27 | | |
28 | | struct DeltaValues |
29 | | { |
30 | | DeltaValues() |
31 | | : deltaX(0.0) |
32 | | , deltaY(0.0) |
33 | | { |
34 | | } |
35 | | |
36 | | DeltaValues(double aDeltaX, double aDeltaY) |
37 | | : deltaX(aDeltaX) |
38 | | , deltaY(aDeltaY) |
39 | 12 | { |
40 | 12 | } |
41 | | |
42 | | explicit DeltaValues(WidgetWheelEvent* aEvent); |
43 | | |
44 | | double deltaX; |
45 | | double deltaY; |
46 | | }; |
47 | | |
48 | | /** |
49 | | * WheelHandlingUtils provides some static methods which are useful at handling |
50 | | * wheel events. |
51 | | */ |
52 | | |
53 | | class WheelHandlingUtils |
54 | | { |
55 | | public: |
56 | | /** |
57 | | * Returns true if aFrame is a scrollable frame and it can be scrolled to |
58 | | * either aDirectionX or aDirectionY along each axis. Or if aFrame is a |
59 | | * plugin frame (in this case, aDirectionX and aDirectionY are ignored). |
60 | | * Otherwise, false. |
61 | | */ |
62 | | static bool CanScrollOn(nsIFrame* aFrame, |
63 | | double aDirectionX, double aDirectionY); |
64 | | /** |
65 | | * Returns true if the scrollable frame can be scrolled to either aDirectionX |
66 | | * or aDirectionY along each axis. Otherwise, false. |
67 | | */ |
68 | | static bool CanScrollOn(nsIScrollableFrame* aScrollFrame, |
69 | | double aDirectionX, double aDirectionY); |
70 | | |
71 | | // For more details about the concept of a disregarded direction, refer to the |
72 | | // code in struct mozilla::layers::ScrollMetadata which defines |
73 | | // mDisregardedDirection. |
74 | | static Maybe<layers::ScrollDirection> |
75 | | GetDisregardedWheelScrollDirection(const nsIFrame* aFrame); |
76 | | |
77 | | private: |
78 | | static bool CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax, |
79 | | double aDirection); |
80 | | }; |
81 | | |
82 | | /** |
83 | | * ScrollbarsForWheel manages scrollbars state during wheel operation. |
84 | | * E.g., on some platforms, scrollbars should show only while user attempts to |
85 | | * scroll. At that time, scrollbars which may be possible to scroll by |
86 | | * operation of wheel at the point should show temporarily. |
87 | | */ |
88 | | |
89 | | class ScrollbarsForWheel |
90 | | { |
91 | | public: |
92 | | static void PrepareToScrollText(EventStateManager* aESM, |
93 | | nsIFrame* aTargetFrame, |
94 | | WidgetWheelEvent* aEvent); |
95 | | static void SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget); |
96 | | // Hide all scrollbars (both mActiveOwner's and mActivatedScrollTargets') |
97 | | static void MayInactivate(); |
98 | | static void Inactivate(); |
99 | | static bool IsActive(); |
100 | | static void OwnWheelTransaction(bool aOwn); |
101 | | |
102 | | protected: |
103 | | static const size_t kNumberOfTargets = 4; |
104 | | static const DeltaValues directions[kNumberOfTargets]; |
105 | | static AutoWeakFrame sActiveOwner; |
106 | | static AutoWeakFrame sActivatedScrollTargets[kNumberOfTargets]; |
107 | | static bool sHadWheelStart; |
108 | | static bool sOwnWheelTransaction; |
109 | | |
110 | | |
111 | | /** |
112 | | * These two methods are called upon eWheelOperationStart/eWheelOperationEnd |
113 | | * events to show/hide the right scrollbars. |
114 | | */ |
115 | | static void TemporarilyActivateAllPossibleScrollTargets( |
116 | | EventStateManager* aESM, |
117 | | nsIFrame* aTargetFrame, |
118 | | WidgetWheelEvent* aEvent); |
119 | | static void DeactivateAllTemporarilyActivatedScrollTargets(); |
120 | | }; |
121 | | |
122 | | /** |
123 | | * WheelTransaction manages a series of wheel events as a transaction. |
124 | | * While in a transaction, every wheel event should scroll the same scrollable |
125 | | * element even if a different scrollable element is under the mouse cursor. |
126 | | * |
127 | | * Additionally, this class also manages wheel scroll speed acceleration. |
128 | | */ |
129 | | |
130 | | class WheelTransaction |
131 | | { |
132 | | public: |
133 | 0 | static nsIFrame* GetTargetFrame() { return sTargetFrame; } |
134 | | static void EndTransaction(); |
135 | | /** |
136 | | * WillHandleDefaultAction() is called before handling aWheelEvent on |
137 | | * aTargetFrame. |
138 | | * |
139 | | * @return false if the caller cannot continue to handle the default |
140 | | * action. Otherwise, true. |
141 | | */ |
142 | | static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent, |
143 | | AutoWeakFrame& aTargetWeakFrame); |
144 | | static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent, |
145 | | nsIFrame* aTargetFrame) |
146 | 0 | { |
147 | 0 | AutoWeakFrame targetWeakFrame(aTargetFrame); |
148 | 0 | return WillHandleDefaultAction(aWheelEvent, targetWeakFrame); |
149 | 0 | } |
150 | | static void OnEvent(WidgetEvent* aEvent); |
151 | | static void Shutdown(); |
152 | | static uint32_t GetTimeoutTime() |
153 | 0 | { |
154 | 0 | return Prefs::sMouseWheelTransactionTimeout; |
155 | 0 | } |
156 | | |
157 | | static void OwnScrollbars(bool aOwn); |
158 | | |
159 | | static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent, |
160 | | bool aAllowScrollSpeedOverride); |
161 | | static void InitializeStatics() |
162 | 0 | { |
163 | 0 | Prefs::InitializeStatics(); |
164 | 0 | } |
165 | | |
166 | | protected: |
167 | | static void BeginTransaction(nsIFrame* aTargetFrame, |
168 | | const WidgetWheelEvent* aEvent); |
169 | | // Be careful, UpdateTransaction may fire a DOM event, therefore, the target |
170 | | // frame might be destroyed in the event handler. |
171 | | static bool UpdateTransaction(const WidgetWheelEvent* aEvent); |
172 | | static void MayEndTransaction(); |
173 | | |
174 | | static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent); |
175 | | static void OnFailToScrollTarget(); |
176 | | static void OnTimeout(nsITimer* aTimer, void* aClosure); |
177 | | static void SetTimeout(); |
178 | | static uint32_t GetIgnoreMoveDelayTime() |
179 | 0 | { |
180 | 0 | return Prefs::sMouseWheelTransactionIgnoreMoveDelay; |
181 | 0 | } |
182 | | static int32_t GetAccelerationStart() |
183 | 0 | { |
184 | 0 | return Prefs::sMouseWheelAccelerationStart; |
185 | 0 | } |
186 | | static int32_t GetAccelerationFactor() |
187 | 0 | { |
188 | 0 | return Prefs::sMouseWheelAccelerationFactor; |
189 | 0 | } |
190 | | static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent); |
191 | | static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor); |
192 | | static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold); |
193 | | |
194 | | static AutoWeakFrame sTargetFrame; |
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; |
200 | | |
201 | | class Prefs |
202 | | { |
203 | | public: |
204 | | static void InitializeStatics(); |
205 | | static int32_t sMouseWheelAccelerationStart; |
206 | | static int32_t sMouseWheelAccelerationFactor; |
207 | | static uint32_t sMouseWheelTransactionTimeout; |
208 | | static uint32_t sMouseWheelTransactionIgnoreMoveDelay; |
209 | | static bool sTestMouseScroll; |
210 | | }; |
211 | | }; |
212 | | |
213 | | // For some kinds of scrollings, the delta values of WidgetWheelEvent are |
214 | | // possbile to be adjusted. For example, the user has configured the pref to let |
215 | | // [vertical wheel + Shift key] to perform horizontal scrolling instead of |
216 | | // vertical scrolling. |
217 | | // The values in this enumeration list all kinds of scrollings whose delta |
218 | | // values are possible to be adjusted. |
219 | | enum class WheelDeltaAdjustmentStrategy : uint8_t |
220 | | { |
221 | | // There is no strategy, don't adjust delta values in any cases. |
222 | | eNone, |
223 | | // This strategy means we're receiving a horizontalized scroll, so we should |
224 | | // apply horizontalization strategy for its delta values. |
225 | | // Horizontalized scrolling means treating vertical wheel scrolling as |
226 | | // horizontal scrolling by adjusting delta values. |
227 | | // It's important to keep in mind with the percise concept of horizontalized |
228 | | // scrolling: Delta values are *ONLY* going to be adjusted during the process |
229 | | // of its default action handling; in views of any programmes other than the |
230 | | // default action handler, such as a DOM event listener or a plugin, delta |
231 | | // values are never going to be adjusted, they will still retrive original |
232 | | // delta values when horizontalization occured for default actions. |
233 | | eHorizontalize, |
234 | | // The following two strategies mean we're receving an auto-dir scroll, so we |
235 | | // should apply auto-dir adjustment to the delta of the wheel event if needed. |
236 | | // Auto-dir is a feature which treats any single-wheel scroll as a scroll in |
237 | | // the only one scrollable direction if the target has only one scrollable |
238 | | // direction. For example, if the user scrolls a vertical wheel inside a |
239 | | // target which is horizontally scrollable but vertical unscrollable, then the |
240 | | // vertical scroll is converted to a horizontal scroll for that target. |
241 | | // So why do we need two different strategies for auto-dir scrolling? That's |
242 | | // because when a wheel scroll is converted due to auto-dir, there is one |
243 | | // thing called "honoured target" which decides which side the converted |
244 | | // scroll goes towards. If the content of the honoured target horizontally |
245 | | // is RTL content, then an upward scroll maps to a rightward scroll and a |
246 | | // downward scroll maps to a leftward scroll; otherwise, an upward scroll maps |
247 | | // to a leftward scroll and a downward scroll maps to a rightward scroll. |
248 | | // |eAutoDir| considers the scrolling target as the honoured target. |
249 | | // |eAutoDirWithRootHonour| takes the root element of the document with the |
250 | | // scrolling element, and considers that as the honoured target. But note that |
251 | | // there's one exception: for targets in an HTML document, the real root |
252 | | // element(I.e. the <html> element) is typically not considered as a root |
253 | | // element, but the <body> element is typically considered as a root element. |
254 | | // If there is no <body> element, then consider the <html> element instead. |
255 | | // And also note that like |eHorizontalize|, delta values are *ONLY* going to |
256 | | // be adjusted during the process of its default action handling; in views of |
257 | | // any programmes other than the default action handler, such as a DOM event |
258 | | // listener or a plugin, delta values are never going to be adjusted. |
259 | | eAutoDir, |
260 | | eAutoDirWithRootHonour, |
261 | | // Not an actual strategy. This is just used as an upper bound for |
262 | | // ContiguousEnumSerializer. |
263 | | eSentinel, |
264 | | }; |
265 | | |
266 | | /** |
267 | | * When a *pure* vertical wheel event should be treated as if it was a |
268 | | * horizontal scroll because the user wants to horizontalize the wheel scroll, |
269 | | * an instance of this class will adjust the delta values upon calling |
270 | | * Horizontalize(). And the horizontalized delta values will be restored |
271 | | * automatically when the instance of this class is being destructed. Or you can |
272 | | * restore them in advance by calling CancelHorizontalization(). |
273 | | */ |
274 | | class MOZ_STACK_CLASS WheelDeltaHorizontalizer final |
275 | | { |
276 | | public: |
277 | | /** |
278 | | * @param aWheelEvent A wheel event whose delta values will be adjusted |
279 | | * upon calling Horizontalize(). |
280 | | */ |
281 | | explicit WheelDeltaHorizontalizer(WidgetWheelEvent& aWheelEvent) |
282 | | : mWheelEvent(aWheelEvent) |
283 | | , mOldDeltaX(0.0) |
284 | | , mOldDeltaZ(0.0) |
285 | | , mOldOverflowDeltaX(0.0) |
286 | | , mOldLineOrPageDeltaX(0) |
287 | | , mHorizontalized(false) |
288 | | { |
289 | | } |
290 | | /** |
291 | | * Converts vertical scrolling into horizontal scrolling by adjusting the |
292 | | * its delta values. |
293 | | */ |
294 | | void Horizontalize(); |
295 | | ~WheelDeltaHorizontalizer(); |
296 | | void CancelHorizontalization(); |
297 | | |
298 | | private: |
299 | | WidgetWheelEvent& mWheelEvent; |
300 | | double mOldDeltaX; |
301 | | double mOldDeltaZ; |
302 | | double mOldOverflowDeltaX; |
303 | | int32_t mOldLineOrPageDeltaX; |
304 | | bool mHorizontalized; |
305 | | }; |
306 | | |
307 | | /** |
308 | | * This class is used to adjust the delta values for wheel scrolling with the |
309 | | * auto-dir functionality. |
310 | | * A traditional wheel scroll only allows the user use the wheel in the same |
311 | | * scrollable direction as that of the scrolling target to scroll the target, |
312 | | * whereas an auto-dir scroll lets the user use any wheel(either a vertical |
313 | | * wheel or a horizontal tilt wheel) to scroll a frame which is scrollable in |
314 | | * only one direction. For detailed information on auto-dir scrolling, |
315 | | * @see mozilla::WheelDeltaAdjustmentStrategy. |
316 | | */ |
317 | | class MOZ_STACK_CLASS AutoDirWheelDeltaAdjuster |
318 | | { |
319 | | protected: |
320 | | /** |
321 | | * @param aDeltaX DeltaX for a wheel event whose delta values will |
322 | | * be adjusted upon calling Adjust() when |
323 | | * ShouldBeAdjusted() returns true. |
324 | | * @param aDeltaY DeltaY for a wheel event, like DeltaX. |
325 | | */ |
326 | | AutoDirWheelDeltaAdjuster(double& aDeltaX, |
327 | | double& aDeltaY) |
328 | | : mDeltaX(aDeltaX) |
329 | | , mDeltaY(aDeltaY) |
330 | | , mCheckedIfShouldBeAdjusted(false) |
331 | | , mShouldBeAdjusted(false) |
332 | | { |
333 | | } |
334 | | |
335 | | public: |
336 | | /** |
337 | | * Gets whether the values of the delta should be adjusted for auto-dir |
338 | | * scrolling. Note that if Adjust() has been called, this function simply |
339 | | * returns false. |
340 | | * |
341 | | * @return true if the delta should be adjusted; otherwise false. |
342 | | */ |
343 | | bool ShouldBeAdjusted(); |
344 | | /** |
345 | | * Adjusts the values of the delta values for auto-dir scrolling when |
346 | | * ShouldBeAdjusted() returns true. If you call it when ShouldBeAdjusted() |
347 | | * returns false, this function will simply do nothing. |
348 | | */ |
349 | | void Adjust(); |
350 | | |
351 | | private: |
352 | | /** |
353 | | * Called by Adjust() if Adjust() successfully adjusted the delta values. |
354 | | */ |
355 | | virtual void OnAdjusted() |
356 | | { |
357 | | } |
358 | | |
359 | | virtual bool CanScrollAlongXAxis() const = 0; |
360 | | virtual bool CanScrollAlongYAxis() const = 0; |
361 | | virtual bool CanScrollUpwards() const = 0; |
362 | | virtual bool CanScrollDownwards() const = 0; |
363 | | virtual bool CanScrollLeftwards() const = 0; |
364 | | virtual bool CanScrollRightwards() const = 0; |
365 | | |
366 | | /** |
367 | | * Gets whether the horizontal content starts at rightside. |
368 | | * |
369 | | * @return If the content is in vertical-RTL writing mode(E.g. "writing-mode: |
370 | | * vertical-rl" in CSS), or if it's in horizontal-RTL writing-mode |
371 | | * (E.g. "writing-mode: horizontal-tb; direction: rtl;" in CSS), then |
372 | | * this function returns true. From the representation perspective, |
373 | | * frames whose horizontal contents start at rightside also cause |
374 | | * their horizontal scrollbars, if any, initially start at rightside. |
375 | | * So we can also learn about the initial side of the horizontal |
376 | | * scrollbar for the frame by calling this function. |
377 | | */ |
378 | | virtual bool IsHorizontalContentRightToLeft() const = 0; |
379 | | |
380 | | protected: |
381 | | double& mDeltaX; |
382 | | double& mDeltaY; |
383 | | |
384 | | private: |
385 | | bool mCheckedIfShouldBeAdjusted; |
386 | | bool mShouldBeAdjusted; |
387 | | }; |
388 | | |
389 | | /** |
390 | | * This is the implementation of AutoDirWheelDeltaAdjuster for EventStateManager |
391 | | * |
392 | | * Detailed comments about some member functions are given in the base class |
393 | | * AutoDirWheelDeltaAdjuster. |
394 | | */ |
395 | | class MOZ_STACK_CLASS ESMAutoDirWheelDeltaAdjuster final |
396 | | : public AutoDirWheelDeltaAdjuster |
397 | | { |
398 | | public: |
399 | | /** |
400 | | * @param aEvent The auto-dir wheel scroll event. |
401 | | * @param aScrollFrame The scroll target for the event. |
402 | | * @param aHonoursRoot If set to true, the honoured frame is the root |
403 | | * frame in the same document where the target is; |
404 | | * If false, the honoured frame is the scroll |
405 | | * target. For the concept of an honoured target, |
406 | | * @see mozilla::WheelDeltaAdjustmentStrategy |
407 | | */ |
408 | | ESMAutoDirWheelDeltaAdjuster(WidgetWheelEvent& aEvent, |
409 | | nsIFrame& aScrollFrame, |
410 | | bool aHonoursRoot); |
411 | | |
412 | | private: |
413 | | virtual void OnAdjusted() override; |
414 | | virtual bool CanScrollAlongXAxis() const override; |
415 | | virtual bool CanScrollAlongYAxis() const override; |
416 | | virtual bool CanScrollUpwards() const override; |
417 | | virtual bool CanScrollDownwards() const override; |
418 | | virtual bool CanScrollLeftwards() const override; |
419 | | virtual bool CanScrollRightwards() const override; |
420 | | virtual bool IsHorizontalContentRightToLeft() const override; |
421 | | |
422 | | nsIScrollableFrame* mScrollTargetFrame; |
423 | | bool mIsHorizontalContentRightToLeft; |
424 | | |
425 | | int32_t& mLineOrPageDeltaX; |
426 | | int32_t& mLineOrPageDeltaY; |
427 | | double& mOverflowDeltaX; |
428 | | double& mOverflowDeltaY; |
429 | | }; |
430 | | |
431 | | /** |
432 | | * This class is used for restoring the delta in an auto-dir wheel. |
433 | | * |
434 | | * An instance of this calss monitors auto-dir adjustment which may happen |
435 | | * during its lifetime. If the delta values is adjusted during its lifetime, the |
436 | | * instance will restore the adjusted delta when it's being destrcuted. |
437 | | */ |
438 | | class MOZ_STACK_CLASS ESMAutoDirWheelDeltaRestorer final |
439 | | { |
440 | | public: |
441 | | /** |
442 | | * @param aEvent The wheel scroll event to be monitored. |
443 | | */ |
444 | | explicit ESMAutoDirWheelDeltaRestorer(WidgetWheelEvent& aEvent); |
445 | | ~ESMAutoDirWheelDeltaRestorer(); |
446 | | |
447 | | private: |
448 | | WidgetWheelEvent& mEvent; |
449 | | double mOldDeltaX; |
450 | | double mOldDeltaY; |
451 | | int32_t mOldLineOrPageDeltaX; |
452 | | int32_t mOldLineOrPageDeltaY; |
453 | | double mOldOverflowDeltaX; |
454 | | double mOldOverflowDeltaY; |
455 | | }; |
456 | | |
457 | | } // namespace mozilla |
458 | | |
459 | | #endif // mozilla_WheelHandlingHelper_h_ |