Bug 1766192 - Choose the second best edge on the opposite side of the best edge. r=botond

The second best edge is used for `snap-overflow` feature [1]. The
`snap-overflow` is applied when the following conditions are met.
 1) A scroll snap target element is larger than the snapport (e.g. scrollport)
    in an axis
 2) The distance between the best edge and the second best edge (on the opposite
    side of the best edge) is later than the snapport size in the axis

There was a problem in our implementation. For example, in below diagram, D is
the original destination of the given scroll operation, 1 is the best edge, 2 is
the second best in our original implementation and 3 is of of the other snap
points. In this case if there's an element larger than the distance between 1
and 3 and if the snapport size is larger than the distance between 1 and 2 but
smaller than the distance between 1 and 3, we should apply `snap-overflow`.

2 1   D     3
|-|---|-----|

There are a couple of test cases in wpt for this condition, for example there's
one of them in oveflowing-snap-area.html [2]. That test case have been passed
incorrectly due to our incorrect `snap-scope` implementation which will be fixed
a subsequent change in this commit series.

[1] https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow
[2] https://searchfox.org/mozilla-central/rev/13d69189a8abfc5064fe44944550b9b6eb4544f5/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas.html#131-137

Differential Revision: https://phabricator.services.mozilla.com/D144531
This commit is contained in:
Hiroyuki Ikezoe 2022-05-10 08:51:37 +00:00
Родитель c6b806d1b3
Коммит 84149416a9
1 изменённых файлов: 27 добавлений и 9 удалений

Просмотреть файл

@ -32,10 +32,12 @@ class CalcSnapPoints final {
nscoord* aSecondBestEdge, bool* aEdgeFound);
nsPoint GetBestEdge() const;
nscoord XDistanceBetweenBestAndSecondEdge() const {
return std::abs(mBestEdge.x - mSecondBestEdge.x);
return std::abs(
NSCoordSaturatingSubtract(mSecondBestEdge.x, mBestEdge.x, nscoord_MAX));
}
nscoord YDistanceBetweenBestAndSecondEdge() const {
return std::abs(mBestEdge.y - mSecondBestEdge.y);
return std::abs(
NSCoordSaturatingSubtract(mSecondBestEdge.y, mBestEdge.y, nscoord_MAX));
}
protected:
@ -45,8 +47,9 @@ class CalcSnapPoints final {
nsPoint mStartPos; // gives the position before scrolling
nsIntPoint mScrollingDirection; // always -1, 0, or 1
nsPoint mBestEdge; // keeps track of the position of the current best edge
nsPoint mSecondBestEdge; // keeps track of the position of the current
// second best edge
nsPoint mSecondBestEdge; // keeps track of the position of the current
// second best edge on the opposite side of the best
// edge
bool mHorizontalEdgeFound; // true if mBestEdge.x is storing a valid
// horizontal edge
bool mVerticalEdgeFound; // true if mBestEdge.y is storing a valid vertical
@ -74,6 +77,10 @@ CalcSnapPoints::CalcSnapPoints(ScrollUnit aUnit, const nsPoint& aDestination,
mScrollingDirection.y = 1;
}
mBestEdge = aDestination;
// We use NSCoordSaturatingSubtract to calculate the distance between a given
// position and this second best edge position so that it can be an
// uninitialized value as the maximum possible value, because the first
// distance calculation would always be nscoord_MAX.
mSecondBestEdge = nsPoint(nscoord_MAX, nscoord_MAX);
mHorizontalEdgeFound = false;
mVerticalEdgeFound = false;
@ -131,6 +138,8 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
return;
}
const bool isOnOppositeSide =
((aEdge - aDestination) > 0) != ((*aBestEdge - aDestination) > 0);
// A utility function to update the best and the second best edges in the
// given conditions.
// |aIsCloserThanBest| True if the current candidate is closer than the best
@ -139,17 +148,25 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
// the second best edge.
auto updateBestEdges = [&](bool aIsCloserThanBest, bool aIsCloserThanSecond) {
if (aIsCloserThanBest) {
*aSecondBestEdge = *aBestEdge;
// Replace the second best edge with the current best edge only if the new
// best edge (aEdge) is on the opposite side of the current best edge.
if (isOnOppositeSide) {
*aSecondBestEdge = *aBestEdge;
}
*aBestEdge = aEdge;
} else if (aIsCloserThanSecond) {
*aSecondBestEdge = aEdge;
if (isOnOppositeSide) {
*aSecondBestEdge = aEdge;
}
}
};
if (mUnit == ScrollUnit::DEVICE_PIXELS || mUnit == ScrollUnit::LINES) {
nscoord distance = std::abs(aEdge - aDestination);
updateBestEdges(distance < std::abs(*aBestEdge - aDestination),
distance < std::abs(*aSecondBestEdge - aDestination));
updateBestEdges(
distance < std::abs(*aBestEdge - aDestination),
distance < std::abs(NSCoordSaturatingSubtract(
*aSecondBestEdge, aDestination, nscoord_MAX)));
} else if (mUnit == ScrollUnit::PAGES) {
// distance to the edge from the scrolling destination in the direction of
// scrolling
@ -159,7 +176,8 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
nscoord curOvershoot = (*aBestEdge - aDestination) * aScrollingDirection;
nscoord secondOvershoot =
(*aSecondBestEdge - aDestination) * aScrollingDirection;
NSCoordSaturatingSubtract(*aSecondBestEdge, aDestination, nscoord_MAX) *
aScrollingDirection;
// edges between the current position and the scrolling destination are
// favoured to preserve context