tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WheelHandlingHelper.h (17834B)


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