| 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 -83 / +65 lines
Lines 42-99 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
        return { exact, exact, snapStop };
84
    return { previous, next, snapStop };
97
}
85
}
98
86
99
template <typename InfoType, typename SizeType, typename LayoutType>
87
template <typename InfoType, typename SizeType, typename LayoutType>
Lines 104-114 static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoA a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec2
104
    if (snapOffsets.isEmpty())
92
    if (snapOffsets.isEmpty())
105
        return pairForNoSnapping;
93
        return pairForNoSnapping;
106
94
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();
95
    auto viewportLength = axis == ScrollEventAxis::Horizontal ? viewportSize.width() : viewportSize.height();
113
    auto isNearEnoughToOffsetForProximity = [&](LayoutType candidateSnapOffset) {
96
    auto isNearEnoughToOffsetForProximity = [&](LayoutType candidateSnapOffset) {
114
        if (info.strictness != ScrollSnapStrictness::Proximity)
97
        if (info.strictness != ScrollSnapStrictness::Proximity)
Lines 120-125 static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoA a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec3
120
        return std::abs(float {candidateSnapOffset - scrollDestinationOffset}) <= (viewportLength * ratioOfScrollPortAxisLengthToBeConsideredForProximity);
103
        return std::abs(float {candidateSnapOffset - scrollDestinationOffset}) <= (viewportLength * ratioOfScrollPortAxisLengthToBeConsideredForProximity);
121
    };
104
    };
122
105
106
    auto [previous, next, snapStop] = searchForPotentialSnapPoints(info, axis, scrollDestinationOffset, originalOffsetForDirectionalSnapping);
107
    if (snapStop)
108
        return *snapStop;
109
123
    if (scrollDestinationOffset <= snapOffsets.first().offset)
110
    if (scrollDestinationOffset <= snapOffsets.first().offset)
124
        return isNearEnoughToOffsetForProximity(snapOffsets.first().offset) ? std::make_pair(snapOffsets.first().offset, std::make_optional(0u)) : pairForNoSnapping;
111
        return isNearEnoughToOffsetForProximity(snapOffsets.first().offset) ? std::make_pair(snapOffsets.first().offset, std::make_optional(0u)) : pairForNoSnapping;
125
112
Lines 128-168 static std::pair<LayoutType, std::optional<unsigned>> closestSnapOffsetWithInfoA a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec4
128
        return isNearEnoughToOffsetForProximity(snapOffsets.last().offset) ? std::make_pair(snapOffsets.last().offset, std::make_optional(lastIndex)) : pairForNoSnapping;
115
        return isNearEnoughToOffsetForProximity(snapOffsets.last().offset) ? std::make_pair(snapOffsets.last().offset, std::make_optional(lastIndex)) : pairForNoSnapping;
129
    }
116
    }
130
117
131
    auto [lowerIndex, upperIndex] = indicesOfNearestSnapOffsets<LayoutType>(scrollDestinationOffset, snapOffsets);
118
    if (originalOffsetForDirectionalSnapping) {
132
    ASSERT(lowerIndex);
119
        // From https://www.w3.org/TR/css-scroll-snap-1/#choosing
133
    ASSERT(upperIndex);
120
        // "User agents must ensure that a user can “escape” a snap position, regardless of the scroll
134
    LayoutType lowerSnapPosition = snapOffsets[*lowerIndex].offset;
121
        // method. For example, if the snap type is mandatory and the next snap position is more than
135
    LayoutType upperSnapPosition = snapOffsets[*upperIndex].offset;
122
        // two screen-widths away, a naïve “always snap to nearest” selection algorithm might “trap” the
136
123
        //
137
    if (!isNearEnoughToOffsetForProximity(lowerSnapPosition)) {
124
        // For a directional scroll, we never snap back to the original scroll position or before it,
138
        lowerSnapPosition = scrollDestinationOffset;
125
        // always preferring the snap offset in the scroll direction.
139
        lowerIndex.reset();
126
        if (*originalOffsetForDirectionalSnapping < scrollDestinationOffset && previous && (*previous).first <= *originalOffsetForDirectionalSnapping)
140
    }
127
            previous.reset();
141
128
        if (*originalOffsetForDirectionalSnapping > scrollDestinationOffset && next && (*next).first >= *originalOffsetForDirectionalSnapping)
142
    if (!isNearEnoughToOffsetForProximity(upperSnapPosition)) {
129
            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
    }
130
    }
152
131
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
132
    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
133
        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.
134
    if (next && !isNearEnoughToOffsetForProximity((*next).first))
156
    // This allows directional scrolling to escape snap points.
135
        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
136
163
    if (lowerIndex && (!originalOffsetForDirectionalSnapping || *originalOffsetForDirectionalSnapping < lowerSnapPosition))
137
    if (!previous && !next)
164
        return std::make_pair(lowerSnapPosition, lowerIndex);
138
        return pairForNoSnapping;
165
    return std::make_pair(upperSnapPosition, upperIndex);
139
    if (!previous)
140
        return *next;
141
    if (!next)
142
        return *previous;
143
144
    // If this scroll isn't directional, then choose whatever snap point is closer, otherwise pick the offset in the scroll direction.
145
    if (!std::abs(velocity))
146
        return (scrollDestinationOffset - (*previous).first) <= ((*next).first - scrollDestinationOffset) ? *previous : *next;
147
    return velocity < 0 ? *previous : *next;
166
}
148
}
167
149
168
enum class InsetOrOutset {
150
enum class InsetOrOutset {

Return to Bug 227062