mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 18:08:58 +00:00
cb2a32d8af
Now that we've removed nsXULScrollFrame, ScrollFrameHelper can be subsumed under nsHTMLScrollFrame. I want to do this before making scrollbars non-XUL. Renaming to mozilla::ScrollFrame is left for a follow-up bug. Differential Revision: https://phabricator.services.mozilla.com/D173063
184 lines
6.6 KiB
C++
184 lines
6.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_layout_ScrollAnchorContainer_h_
|
|
#define mozilla_layout_ScrollAnchorContainer_h_
|
|
|
|
#include "nsPoint.h"
|
|
#include "mozilla/Saturate.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
|
|
class nsFrameList;
|
|
class nsHTMLScrollFrame;
|
|
class nsIFrame;
|
|
class nsIScrollableFrame;
|
|
|
|
namespace mozilla::layout {
|
|
|
|
/**
|
|
* A scroll anchor container finds a descendent element of a scrollable frame
|
|
* to be an anchor node. After every reflow, the scroll anchor will apply
|
|
* scroll adjustments to keep the anchor node in the same relative position.
|
|
*
|
|
* See: https://drafts.csswg.org/css-scroll-anchoring/
|
|
*/
|
|
class ScrollAnchorContainer final {
|
|
public:
|
|
explicit ScrollAnchorContainer(nsHTMLScrollFrame* aScrollFrame);
|
|
~ScrollAnchorContainer();
|
|
|
|
/**
|
|
* Returns the nearest scroll anchor container that could select aFrame as an
|
|
* anchor node.
|
|
*/
|
|
static ScrollAnchorContainer* FindFor(nsIFrame* aFrame);
|
|
|
|
/**
|
|
* Returns the frame that is the selected anchor node or null if no anchor
|
|
* is selected.
|
|
*/
|
|
nsIFrame* AnchorNode() const { return mAnchorNode; }
|
|
|
|
// The owner of this scroll anchor container.
|
|
nsHTMLScrollFrame* Frame() const;
|
|
|
|
/**
|
|
* Returns the frame that owns this scroll anchor container as a scrollable
|
|
* frame. This is always non-null.
|
|
*/
|
|
nsIScrollableFrame* ScrollableFrame() const;
|
|
|
|
/**
|
|
* Find a suitable anchor node among the descendants of the scrollable frame.
|
|
* This should only be called after the scroll anchor has been invalidated.
|
|
*/
|
|
void SelectAnchor();
|
|
|
|
/**
|
|
* Whether this scroll frame can maintain an anchor node at the moment.
|
|
*/
|
|
bool CanMaintainAnchor() const;
|
|
|
|
/**
|
|
* Notify the scroll anchor container that its scroll frame has been
|
|
* scrolled by a user and should invalidate itself.
|
|
*/
|
|
void UserScrolled();
|
|
|
|
/**
|
|
* Notify the scroll anchor container that a reflow has happened and it
|
|
* should query its anchor to see if a scroll adjustment needs to occur.
|
|
*/
|
|
void ApplyAdjustments();
|
|
|
|
/**
|
|
* Notify the scroll anchor container that it should suppress any scroll
|
|
* adjustment that may happen after the next layout flush.
|
|
*/
|
|
void SuppressAdjustments();
|
|
|
|
/**
|
|
* Notify this scroll anchor container that its anchor node should be
|
|
* invalidated, and recomputed at the next available opportunity if
|
|
* ScheduleSelection is Yes.
|
|
*/
|
|
enum class ScheduleSelection { No, Yes };
|
|
void InvalidateAnchor(ScheduleSelection = ScheduleSelection::Yes);
|
|
|
|
/**
|
|
* Notify this scroll anchor container that it will be destroyed along with
|
|
* its parent frame.
|
|
*/
|
|
void Destroy();
|
|
|
|
private:
|
|
// Represents an assessment of a frame's suitability as a scroll anchor,
|
|
// from the scroll-anchoring spec's "candidate examination algorithm":
|
|
// https://drafts.csswg.org/css-scroll-anchoring-1/#candidate-examination
|
|
enum class ExamineResult {
|
|
// The frame is an excluded subtree or fully clipped and should be ignored.
|
|
// This corresponds with step 1 in the algorithm.
|
|
Exclude,
|
|
// This frame is an anonymous or inline box and its descendants should be
|
|
// searched to find an anchor node. If none are found, then continue
|
|
// searching. This is implied by the prologue of the algorithm, and
|
|
// should be made explicit in the spec [1].
|
|
//
|
|
// [1] https://github.com/w3c/csswg-drafts/issues/3489
|
|
PassThrough,
|
|
// The frame is partially visible and its descendants should be searched to
|
|
// find an anchor node. If none are found then this frame should be
|
|
// selected. This corresponds with step 3 in the algorithm.
|
|
Traverse,
|
|
// The frame is fully visible and should be selected as an anchor node. This
|
|
// corresponds with step 2 in the algorithm.
|
|
Accept,
|
|
};
|
|
|
|
ExamineResult ExamineAnchorCandidate(nsIFrame* aPrimaryFrame) const;
|
|
|
|
// Search a frame's children to find an anchor node. Returns the frame for a
|
|
// valid anchor node, if one was found in the frames descendants, or null
|
|
// otherwise.
|
|
nsIFrame* FindAnchorIn(nsIFrame* aFrame) const;
|
|
|
|
// Search a child list to find an anchor node. Returns the frame for a valid
|
|
// anchor node, if one was found in this child list, or null otherwise.
|
|
nsIFrame* FindAnchorInList(const nsFrameList& aFrameList) const;
|
|
|
|
// Notes that a given adjustment has happened, and maybe disables scroll
|
|
// anchoring on this scroller altogether based on various prefs.
|
|
void AdjustmentMade(nscoord aAdjustment);
|
|
|
|
// The anchor node that we will scroll to keep in the same relative position
|
|
// after reflows. This may be null if we were not able to select a valid
|
|
// scroll anchor
|
|
nsIFrame* mAnchorNode = nullptr;
|
|
|
|
// The last offset of the scroll anchor node's scrollable overflow rect start
|
|
// edge relative to the scroll-port start edge, in the block axis of the
|
|
// scroll frame. This is used for calculating the distance to scroll to keep
|
|
// the anchor node in the same relative position
|
|
nscoord mLastAnchorOffset = 0;
|
|
|
|
struct DisablingHeuristic {
|
|
// The number of consecutive scroll anchoring adjustments that have happened
|
|
// without a user scroll.
|
|
SaturateUint32 mConsecutiveScrollAnchoringAdjustments{0};
|
|
|
|
// The total length that has been adjusted by all the consecutive
|
|
// adjustments referenced above. Note that this is a sum, so that
|
|
// oscillating adjustments average towards zero.
|
|
nscoord mConsecutiveScrollAnchoringAdjustmentLength{0};
|
|
|
|
// The time we started checking for adjustments.
|
|
TimeStamp mTimeStamp;
|
|
|
|
// Returns whether anchoring should get disabled.
|
|
bool AdjustmentMade(const ScrollAnchorContainer&, nscoord aAdjustment);
|
|
void Reset();
|
|
} mHeuristic;
|
|
|
|
// True if we've been disabled by the heuristic controlled by
|
|
// layout.css.scroll-anchoring.max-consecutive-adjustments and
|
|
// layout.css.scroll-anchoring.min-adjustment-threshold.
|
|
bool mDisabled : 1;
|
|
|
|
// True if when we selected the current scroll anchor, there were unlaid out
|
|
// children that could be better anchor nodes after layout.
|
|
bool mAnchorMightBeSubOptimal : 1;
|
|
// True if we should recalculate our anchor node at the next chance
|
|
bool mAnchorNodeIsDirty : 1;
|
|
// True if we are applying a scroll anchor adjustment
|
|
bool mApplyingAnchorAdjustment : 1;
|
|
// True if we should suppress anchor adjustments
|
|
bool mSuppressAnchorAdjustment : 1;
|
|
};
|
|
|
|
} // namespace mozilla::layout
|
|
|
|
#endif // mozilla_layout_ScrollAnchorContainer_h_
|