tor-browser

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

ScrollAnchorContainer.h (6783B)


      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_layout_ScrollAnchorContainer_h_
      8 #define mozilla_layout_ScrollAnchorContainer_h_
      9 
     10 #include "mozilla/Saturate.h"
     11 #include "mozilla/TimeStamp.h"
     12 #include "nsPoint.h"
     13 
     14 class nsFrameList;
     15 class nsIFrame;
     16 
     17 namespace mozilla {
     18 class ScrollContainerFrame;
     19 }
     20 
     21 namespace mozilla::layout {
     22 
     23 /**
     24 * A scroll anchor container finds a descendent element of a scroll container
     25 * frame to be an anchor node. After every reflow, the scroll anchor will apply
     26 * scroll adjustments to keep the anchor node in the same relative position.
     27 *
     28 * See: https://drafts.csswg.org/css-scroll-anchoring/
     29 */
     30 class ScrollAnchorContainer final {
     31 public:
     32  explicit ScrollAnchorContainer(ScrollContainerFrame* aScrollFrame);
     33  ~ScrollAnchorContainer();
     34 
     35  /**
     36   * Returns the nearest scroll anchor container that could select aFrame as an
     37   * anchor node.
     38   */
     39  static ScrollAnchorContainer* FindFor(nsIFrame* aFrame);
     40 
     41  /**
     42   * Returns the frame that is the selected anchor node or null if no anchor
     43   * is selected.
     44   */
     45  nsIFrame* AnchorNode() const { return mAnchorNode; }
     46 
     47  // The owner of this scroll anchor container.
     48  ScrollContainerFrame* Frame() const;
     49 
     50  /**
     51   * Returns the scroll container frame that owns this scroll anchor container.
     52   * This is always non-null.
     53   */
     54  ScrollContainerFrame* ScrollContainer() const;
     55 
     56  /**
     57   * Find a suitable anchor node among the descendants of the scroll container
     58   * frame. This should only be called after the scroll anchor has been
     59   * invalidated.
     60   */
     61  void SelectAnchor();
     62 
     63  /**
     64   * Whether this scroll frame can maintain an anchor node at the moment.
     65   */
     66  bool CanMaintainAnchor() const;
     67 
     68  /**
     69   * Notify the scroll anchor container that its scroll frame has been
     70   * scrolled by a user and should invalidate itself.
     71   */
     72  void UserScrolled();
     73 
     74  /**
     75   * Notify the scroll anchor container that a reflow has happened and it
     76   * should query its anchor to see if a scroll adjustment needs to occur.
     77   */
     78  void ApplyAdjustments();
     79 
     80  /**
     81   * Notify the scroll anchor container that it should suppress any scroll
     82   * adjustment that may happen after the next layout flush.
     83   */
     84  void SuppressAdjustments();
     85 
     86  /**
     87   * Notify this scroll anchor container that its anchor node should be
     88   * invalidated, and recomputed at the next available opportunity if
     89   * ScheduleSelection is Yes.
     90   */
     91  enum class ScheduleSelection { No, Yes };
     92  void InvalidateAnchor(ScheduleSelection = ScheduleSelection::Yes);
     93 
     94  /**
     95   * Notify this scroll anchor container that it will be destroyed along with
     96   * its parent frame.
     97   */
     98  void Destroy();
     99 
    100 private:
    101  // Represents an assessment of a frame's suitability as a scroll anchor,
    102  // from the scroll-anchoring spec's "candidate examination algorithm":
    103  // https://drafts.csswg.org/css-scroll-anchoring-1/#candidate-examination
    104  enum class ExamineResult {
    105    // The frame is an excluded subtree or fully clipped and should be ignored.
    106    // This corresponds with step 1 in the algorithm.
    107    Exclude,
    108    // This frame is an anonymous or inline box and its descendants should be
    109    // searched to find an anchor node. If none are found, then continue
    110    // searching. This is implied by the prologue of the algorithm, and
    111    // should be made explicit in the spec [1].
    112    //
    113    // [1] https://github.com/w3c/csswg-drafts/issues/3489
    114    PassThrough,
    115    // The frame is partially visible and its descendants should be searched to
    116    // find an anchor node. If none are found then this frame should be
    117    // selected. This corresponds with step 3 in the algorithm.
    118    Traverse,
    119    // The frame is fully visible and should be selected as an anchor node. This
    120    // corresponds with step 2 in the algorithm.
    121    Accept,
    122  };
    123 
    124  ExamineResult ExamineAnchorCandidate(nsIFrame* aPrimaryFrame) const;
    125 
    126  // Search a frame's children to find an anchor node. Returns the frame for a
    127  // valid anchor node, if one was found in the frames descendants, or null
    128  // otherwise.
    129  nsIFrame* FindAnchorIn(nsIFrame* aFrame) const;
    130 
    131  // Search a child list to find an anchor node. Returns the frame for a valid
    132  // anchor node, if one was found in this child list, or null otherwise.
    133  nsIFrame* FindAnchorInList(const nsFrameList& aFrameList) const;
    134 
    135  // Notes that a given adjustment has happened, and maybe disables scroll
    136  // anchoring on this scroller altogether based on various prefs.
    137  void AdjustmentMade(nscoord aAdjustment);
    138 
    139  // The anchor node that we will scroll to keep in the same relative position
    140  // after reflows. This may be null if we were not able to select a valid
    141  // scroll anchor
    142  nsIFrame* mAnchorNode = nullptr;
    143 
    144  // The last offset of the scroll anchor node's scrollable overflow rect start
    145  // edge relative to the scroll-port start edge, in the block axis of the
    146  // scroll frame. This is used for calculating the distance to scroll to keep
    147  // the anchor node in the same relative position
    148  nscoord mLastAnchorOffset = 0;
    149 
    150  struct DisablingHeuristic {
    151    // The number of consecutive scroll anchoring adjustments that have happened
    152    // without a user scroll.
    153    SaturateUint32 mConsecutiveScrollAnchoringAdjustments{0};
    154 
    155    // The total length that has been adjusted by all the consecutive
    156    // adjustments referenced above. Note that this is a sum, so that
    157    // oscillating adjustments average towards zero.
    158    nscoord mConsecutiveScrollAnchoringAdjustmentLength{0};
    159 
    160    // The time we started checking for adjustments.
    161    TimeStamp mTimeStamp;
    162 
    163    // Returns whether anchoring should get disabled.
    164    bool AdjustmentMade(const ScrollAnchorContainer&, nscoord aAdjustment);
    165    void Reset();
    166  } mHeuristic;
    167 
    168  // True if we've been disabled by the heuristic controlled by
    169  // layout.css.scroll-anchoring.max-consecutive-adjustments and
    170  // layout.css.scroll-anchoring.min-adjustment-threshold.
    171  bool mDisabled : 1;
    172 
    173  // True if when we selected the current scroll anchor, there were unlaid out
    174  // children that could be better anchor nodes after layout.
    175  bool mAnchorMightBeSubOptimal : 1;
    176  // True if we should recalculate our anchor node at the next chance
    177  bool mAnchorNodeIsDirty : 1;
    178  // True if we are applying a scroll anchor adjustment
    179  bool mApplyingAnchorAdjustment : 1;
    180  // True if we should suppress anchor adjustments
    181  bool mSuppressAnchorAdjustment : 1;
    182 };
    183 
    184 }  // namespace mozilla::layout
    185 
    186 #endif  // mozilla_layout_ScrollAnchorContainer_h_