| Differences between
and this patch
- a/Source/WebCore/ChangeLog +19 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2021-06-16  Martin Robinson  <mrobinson@webkit.org>
2
3
        [css-scroll-snap] Simplify snap point selection helpers
4
        https://bugs.webkit.org/show_bug.cgi?id=227062
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Combine indicesOfNearestSnapOffsets and findFirstSnapStopOffsetBetweenOriginAndDestination
9
        and have the helper return the geometric previous and next snap points. This makes the
10
        code a bit easier to read, slightly more efficient, and prepares for looking at
11
        the geometric previous and next snap points for certain upcoming CSS Scroll Snap features.
12
13
        No new tests. This should not change behavior in any way.
14
15
        * page/scrolling/ScrollSnapOffsetsInfo.cpp:
16
        (WebCore::searchForPotentialSnapPoints): Added this new helper which is the combination
17
        of indicesOfNearestSnapOffsets and findFirstSnapStopOffsetBetweenOriginAndDestination.
18
        (WebCore::closestSnapOffsetWithInfoAndAxis): Use the new helper.
19
1
2021-06-16  Per Arne  <pvollan@apple.com>
20
2021-06-16  Per Arne  <pvollan@apple.com>
2
21
3
        Update Media Accessibility preferences in the WebContent process from the UI process
22
        Update Media Accessibility preferences in the WebContent process from the UI process
- a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp -84 / +67 lines
Lines 42-114 a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec1
42
42
43
namespace WebCore {
43
namespace WebCore {
44
44
45
template <typename LayoutType>
45
template <typename UnitType>
46
static std::pair<std::optional<unsigned>, std::optional<unsigned>> indicesOfNearestSnapOffsets(LayoutType offset, const Vector<SnapOffset<LayoutType>>& snapOffsets)
46
struct PotentialSnapPointSearchResult {
47
{
47
    std::optional<std::pair<UnitType, unsigned>> previous;
48
    unsigned lowerIndex = 0;
48
    std::optional<std::pair<UnitType, unsigned>> next;
49
    unsigned upperIndex = snapOffsets.size() - 1;
49
    std::optional<std::pair<UnitType, unsigned>> snapStop;
50
    while (lowerIndex < upperIndex - 1) {
50
};
51
        int middleIndex = (lowerIndex + upperIndex) / 2;
52
        auto middleOffset = snapOffsets[middleIndex].offset;
53
        if (offset == middleOffset) {
54
            upperIndex = middleIndex;
55
            lowerIndex = middleIndex;
56
            break;
57
        }
58
59
        if (offset > middleOffset)
60
            lowerIndex = middleIndex;
61
        else
62
            upperIndex = middleIndex;
63
    }
64
65
    return std::make_pair(lowerIndex, upperIndex);
66
}
67
51
68
template <typename LayoutType>
52
template <typename InfoType, typename UnitType>
69
static std::optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(const Vector<SnapOffset<LayoutType>>& snapOffsets, LayoutType scrollOriginOffset, LayoutType scrollDestinationOffset)
53
static PotentialSnapPointSearchResult<UnitType> searchForPotentialSnapPoints(const InfoType& info, ScrollEventAxis axis, UnitType destinationOffset, std::optional<UnitType> originalOffset)
70
{
54
{
71
    LayoutType difference = scrollDestinationOffset - scrollOriginOffset;
55
    const auto& snapOffsets = info.offsetsForAxis(axis);
72
    if (!difference)
56
    std::optional<std::pair<UnitType, unsigned>> previous, next, exact, snapStop;
73
        return std::nullopt;
57
74
58
    // A particular snap stop is better if it's between the original offset and destination offset and closer original
75
    unsigned searchStartOffset = 0;
59
    // offset than the previously selected snap stop. We always want to stop at the closest snap stop to the original offset.
76
    size_t iteration = 1;
60
    auto isBetterSnapStop = [&](UnitType candidate) {
77
    if (difference < 0) {
61
        if (!originalOffset)
78
        searchStartOffset = snapOffsets.size() - 1;
62
            return false;
79
        iteration = -1;
63
        auto original = *originalOffset;
80
    }
64
        if (candidate <= std::min(destinationOffset, original) || candidate >= std::max(destinationOffset, original))
81
65
            return false;
82
    auto isPast = [difference](LayoutType mark, LayoutType candidate) {
66
        return !snapStop || std::abs(float { candidate - original }) < std::abs(float { (*snapStop).first - original });
83
        return (difference > 0 && candidate > mark) || (difference < 0 && candidate < mark);
84
    };
67
    };
85
68
86
    for (int i = searchStartOffset; i >= 0 && static_cast<size_t>(i) < snapOffsets.size(); i += iteration) {
69
    for (unsigned i = 0; i < snapOffsets.size(); i++) {
87
        auto offset = snapOffsets[i].offset;
70
        UnitType potentialSnapOffset = snapOffsets[i].offset;
88
        if (isPast(scrollDestinationOffset, offset))
71
        if (potentialSnapOffset == destinationOffset)
89
            break;
72
            exact = std::make_pair(potentialSnapOffset, i);
90
        if (snapOffsets[i].stop != ScrollSnapStop::Always)
73
        else if (potentialSnapOffset < destinationOffset)
91
            continue;
74
            previous = std::make_pair(potentialSnapOffset, i);
92
        if (isPast(scrollOriginOffset, offset))
75
        else if (!next && potentialSnapOffset > destinationOffset)
93
            return i;
76
            next = std::make_pair(potentialSnapOffset, i);
77
78
        if (snapOffsets[i].stop == ScrollSnapStop::Always && isBetterSnapStop(potentialSnapOffset))
79
            snapStop = std::make_pair(potentialSnapOffset, i);
94
    }
80
    }
95
81
96
    return std::nullopt;
82
    if (exact)
83
        previous = next = exact;
84
85
    return { previous, next, snapStop };
97
}
86
}
98
87
99
template <typename InfoType, typename SizeType, typename LayoutType>
88
template <typename InfoType, typename SizeType, typename LayoutType>
100
static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoAndAxis(const InfoType& info, ScrollEventAxis axis, const SizeType& viewportSize, LayoutType scrollDestinationOffset, float velocity, std::optional<LayoutType> originalOffsetForDirectionalSnapping)
89
static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoAndAxis(const InfoType& info, ScrollEventAxis axis, const SizeType& viewportSize, LayoutType scrollDestinationOffset, float velocity, std::optional<LayoutType> originalOffsetForDirectionalSnapping)
101
{
90
{
102
    const auto& snapOffsets = info.offsetsForAxis(axis);
91
    const auto& snapOffsets = info.offsetsForAxis(axis);
103
    auto pairForNoSnapping = std::make_pair(scrollDestinationOffset, std::nullopt);
92
    auto pairForNoSnapping = std::make_pair(scrollDestinationOffset, std::optional<unsigned>());
104
    if (snapOffsets.isEmpty())
93
    if (snapOffsets.isEmpty())
105
        return pairForNoSnapping;
94
        return pairForNoSnapping;
106
95
107
    if (originalOffsetForDirectionalSnapping) {
108
        if (auto firstSnapStopOffsetIndex = findFirstSnapStopOffsetBetweenOriginAndDestination(snapOffsets, *originalOffsetForDirectionalSnapping, scrollDestinationOffset))
109
            return std::make_pair(snapOffsets[*firstSnapStopOffsetIndex].offset, firstSnapStopOffsetIndex);
110
    }
111
112
    auto viewportLength = axis == ScrollEventAxis::Horizontal ? viewportSize.width() : viewportSize.height();
96
    auto viewportLength = axis == ScrollEventAxis::Horizontal ? viewportSize.width() : viewportSize.height();
113
    auto isNearEnoughToOffsetForProximity = [&](LayoutType candidateSnapOffset) {
97
    auto isNearEnoughToOffsetForProximity = [&](LayoutType candidateSnapOffset) {
114
        if (info.strictness != ScrollSnapStrictness::Proximity)
98
        if (info.strictness != ScrollSnapStrictness::Proximity)
Lines 120-125 static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoA a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec2
120
        return std::abs(float {candidateSnapOffset - scrollDestinationOffset}) <= (viewportLength * ratioOfScrollPortAxisLengthToBeConsideredForProximity);
104
        return std::abs(float {candidateSnapOffset - scrollDestinationOffset}) <= (viewportLength * ratioOfScrollPortAxisLengthToBeConsideredForProximity);
121
    };
105
    };
122
106
107
    auto [previous, next, snapStop] = searchForPotentialSnapPoints(info, axis, scrollDestinationOffset, originalOffsetForDirectionalSnapping);
108
    if (snapStop)
109
        return *snapStop;
110
123
    if (scrollDestinationOffset <= snapOffsets.first().offset)
111
    if (scrollDestinationOffset <= snapOffsets.first().offset)
124
        return isNearEnoughToOffsetForProximity(snapOffsets.first().offset) ? std::make_pair(snapOffsets.first().offset, std::make_optional(0u)) : pairForNoSnapping;
112
        return isNearEnoughToOffsetForProximity(snapOffsets.first().offset) ? std::make_pair(snapOffsets.first().offset, std::make_optional(0u)) : pairForNoSnapping;
125
113
Lines 128-168 static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoA a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec3
128
        return isNearEnoughToOffsetForProximity(snapOffsets.last().offset) ? std::make_pair(snapOffsets.last().offset, std::make_optional(lastIndex)) : pairForNoSnapping;
116
        return isNearEnoughToOffsetForProximity(snapOffsets.last().offset) ? std::make_pair(snapOffsets.last().offset, std::make_optional(lastIndex)) : pairForNoSnapping;
129
    }
117
    }
130
118
131
    auto [lowerIndex, upperIndex] = indicesOfNearestSnapOffsets<LayoutType>(scrollDestinationOffset, snapOffsets);
119
    if (originalOffsetForDirectionalSnapping) {
132
    ASSERT(lowerIndex);
120
        // From https://www.w3.org/TR/css-scroll-snap-1/#choosing
133
    ASSERT(upperIndex);
121
        // "User agents must ensure that a user can “escape” a snap position, regardless of the scroll
134
    LayoutType lowerSnapPosition = snapOffsets[*lowerIndex].offset;
122
        // method. For example, if the snap type is mandatory and the next snap position is more than
135
    LayoutType upperSnapPosition = snapOffsets[*upperIndex].offset;
123
        // two screen-widths away, a naïve “always snap to nearest” selection algorithm might “trap” the
136
124
        //
137
    if (!isNearEnoughToOffsetForProximity(lowerSnapPosition)) {
125
        // For a directional scroll, we never snap back to the original scroll position or before it,
138
        lowerSnapPosition = scrollDestinationOffset;
126
        // always preferring the snap offset in the scroll direction.
139
        lowerIndex.reset();
127
        if (*originalOffsetForDirectionalSnapping < scrollDestinationOffset && previous && (*previous).first <= *originalOffsetForDirectionalSnapping)
140
    }
128
            previous.reset();
141
129
        if (*originalOffsetForDirectionalSnapping > scrollDestinationOffset && next && (*next).first >= *originalOffsetForDirectionalSnapping)
142
    if (!isNearEnoughToOffsetForProximity(upperSnapPosition)) {
130
            next.reset();
143
        upperSnapPosition = scrollDestinationOffset;
144
        upperIndex.reset();
145
146
    }
147
148
    if (!std::abs(velocity)) {
149
        bool isCloserToLowerSnapPosition = !upperIndex || (lowerIndex && scrollDestinationOffset - lowerSnapPosition <= upperSnapPosition - scrollDestinationOffset);
150
        return isCloserToLowerSnapPosition ? std::make_pair(lowerSnapPosition, lowerIndex) : std::make_pair(upperSnapPosition, upperIndex);
151
    }
131
    }
152
132
153
    // Non-zero velocity indicates a flick gesture. Even if another snap point is closer, we should choose the one in the direction of the flick gesture
133
    if (previous && !isNearEnoughToOffsetForProximity((*previous).first))
154
    // as long as a scroll snap offset is close enough for proximity (or we aren't using proximity). If we are doing directional
134
        previous.reset();
155
    // snapping, we should never snap to a point that was on the other side of the original position in the opposite direction of this scroll.
135
    if (next && !isNearEnoughToOffsetForProximity((*next).first))
156
    // This allows directional scrolling to escape snap points.
136
        next.reset();
157
    if (velocity < 0)  {
158
        if (upperIndex && (!originalOffsetForDirectionalSnapping || *originalOffsetForDirectionalSnapping > upperSnapPosition))
159
            return std::make_pair(upperSnapPosition, upperIndex);
160
        return std::make_pair(lowerSnapPosition, lowerIndex);
161
    }
162
137
163
    if (lowerIndex && (!originalOffsetForDirectionalSnapping || *originalOffsetForDirectionalSnapping < lowerSnapPosition))
138
    if (!previous && !next)
164
        return std::make_pair(lowerSnapPosition, lowerIndex);
139
        return pairForNoSnapping;
165
    return std::make_pair(upperSnapPosition, upperIndex);
140
    if (!previous)
141
        return *next;
142
    if (!next)
143
        return *previous;
144
145
    // If this scroll isn't directional, then choose whatever snap point is closer, otherwise pick the offset in the scroll direction.
146
    if (!std::abs(velocity))
147
        return (scrollDestinationOffset - (*previous).first) <= ((*next).first - scrollDestinationOffset) ? *previous : *next;
148
    return velocity < 0 ? *previous : *next;
166
}
149
}
167
150
168
enum class InsetOrOutset {
151
enum class InsetOrOutset {

Return to Bug 227062