| Differences between
and this patch
- a/Source/WebCore/ChangeLog +67 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2021-03-15  Martin Robinson  <mrobinson@webkit.org>
2
3
        [css-scroll-snap] Properly handle overflowing snap areas
4
        https://bugs.webkit.org/show_bug.cgi?id=223175
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Properly handle overflowing snap areas. This requires rewriting the snapping algorithm.
9
        ScrollSnapOffsetsInfo now stores snap area rectangles, which are used to implement
10
        spec-compliant snap point selection behavior in situations where snap areas overflow the
11
        snapport. Concretely, in this situation, any scroll offset within the snap area where the
12
        area fills the entire viewport (in the axis of scroll) is a valid snap point.
13
14
        In addition, snap offset ranges are no longer kept to represent proximity, since proximity
15
        can be handled during a more thorough snap point selection process.
16
17
        ScrollSnapOffsetsInfo now contains a rectangle type, which is either a LayoutRect or a
18
        FloatRect, so the template needs to carry this type as well. In order to make it simpler to
19
        refer to a ScrollSnapOffsetsInfo this change creates two type aliases,
20
        FloatScrollSnapOffsetsInfo and LayoutScrollSnapOffsetsInfo, which are used throughout
21
        the code.
22
23
        No new tests. This change fixes the WPT test:
24
        imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas.html
25
26
        * page/scrolling/AsyncScrollingCoordinator.cpp: Use the new type aliases.
27
        * page/scrolling/ScrollSnapOffsetsInfo.cpp:
28
        (WebCore::findFirstSnapStopOffsetBetweenOriginAndDestination): Updated to return an offset/index
29
        pair so the return type of this function is compatible with that of calculateClosestSnapOffset.
30
        (WebCore::updateSnapOffsetsForScrollableArea): No longer store offset ranges for proximity snapping
31
        and also store snap areas converted to "offset" coordinates.
32
        (WebCore::convertOffsetInfo): No longer need to convert offset ranges, but this does now
33
        require converting offset areas.
34
        (WebCore::FloatScrollSnapOffsetsInfo::convertUnits const): Use the new type aliases.
35
        (WebCore::LayoutScrollSnapOffsetsInfo::convertUnits const): Ditto.
36
        (WebCore::maxForAxis): Added this helper which gets the maximum in a particular axis given the axis.
37
        (WebCore::minForAxis): Ditto.
38
        (WebCore::isNearEnoughToOffsetForProximity): Added this helper which replaces computeAxisProximitySnapOffsetRanges.
39
        (WebCore::calculateClosestSnapOffset): This replaces closestSnapOffsetWithOffsetsAndRanges, but also
40
        and indicesOfNearestSnapOffsets. Now written using snap areas from the data structure as well as
41
        with proper handling for overflowing snap areas.
42
        (WebCore::LayoutScrollSnapOffsetsInfo::closestSnapOffset const): Update for new method names and type aliases.
43
        (WebCore::FloatScrollSnapOffsetsInfo::closestSnapOffset const): Ditto.
44
        * page/scrolling/ScrollSnapOffsetsInfo.h:
45
        (WebCore::ScrollSnapOffsetsInfo::isEqual const): No longer look at snap offset ranges, but do take into
46
        consideration the snap areas.
47
        (WebCore::ScrollSnapOffsetsInfo::offsetsForAxis const): Renamed the template argument.
48
        (WebCore::operator<<): Removed.
49
        * page/scrolling/ScrollingStateScrollingNode.cpp: Use the new type alias.
50
        * page/scrolling/ScrollingStateScrollingNode.h: Ditto.
51
        * page/scrolling/ScrollingTreeScrollingNode.cpp: Ditto.
52
        * page/scrolling/ScrollingTreeScrollingNode.h: Ditto.
53
        * page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm:
54
        (WebCore::ScrollingTreeScrollingNodeDelegateMac::updateFromStateNode): convertUnits now takes two template arguments.
55
        * platform/ScrollController.cpp:
56
        (WebCore::ScrollController::updateScrollSnapPoints): Pass in the viewport size when selecting snap points.
57
        (WebCore::ScrollController::adjustScrollDestination): Ditto.
58
        * platform/ScrollController.h: Use the new type alias.
59
        * platform/ScrollSnapAnimatorState.cpp:
60
        (WebCore::ScrollSnapAnimatorState::setupAnimationForState): Pass viewport size to targetOffsetForStartOffset.
61
        (WebCore::ScrollSnapAnimatorState::targetOffsetForStartOffset const): Ditto.
62
        (WebCore::operator<<): No longer print snap offset ranges.
63
        * platform/ScrollSnapAnimatorState.h: Removed method dealing with ranges. Now pass viewport size to targetOffsetForStartOffset.
64
        * platform/ScrollableArea.cpp: Use the new type aliases.
65
        * platform/ScrollableArea.h: Ditto.
66
        * platform/mac/ScrollAnimatorMac.mm: Ditto.
67
1
2021-03-10  Sergio Villar Senin  <svillar@igalia.com>
68
2021-03-10  Sergio Villar Senin  <svillar@igalia.com>
2
69
3
        Do not shrink radio buttons bellow its size
70
        Do not shrink radio buttons bellow its size
- a/Source/WebKit/ChangeLog +23 lines
Lines 1-3 a/Source/WebKit/ChangeLog_sec1
1
2021-03-15  Martin Robinson  <mrobinson@webkit.org>
2
3
        [css-scroll-snap] Properly handle overflowing snap areas
4
        https://bugs.webkit.org/show_bug.cgi?id=223175
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        ScrollSnapOffsetsInfo no longer holds snap offset ranges, but is also templated off
9
        of a rect type. In WebKit we switch to using the FloatScrollSnapOffsetsInfo alias.
10
        Additionally, we need to pass in the viewport size when requesting a snap offset
11
        position.
12
13
        * Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp:
14
        (ArgumentCoder<ScrollingStateScrollingNode>::decode): Switch to using the FloatScrollSnapOffsetsInfo alias.
15
        (ArgumentCoder<FloatScrollSnapOffsetsInfo>::encode): Ditto. Also we no longer need to encode the ranges.
16
        (ArgumentCoder<FloatScrollSnapOffsetsInfo>::decode): Ditto.
17
        * Shared/WebCoreArgumentCoders.cpp: Ditto.
18
        * Shared/WebCoreArgumentCoders.h: Ditto.
19
        * UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:
20
        (WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling const): Pass in the viewport size now.
21
        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
22
        (-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Ditto.
23
1
2021-03-15  Kimmo Kinnunen  <kkinnunen@apple.com>
24
2021-03-15  Kimmo Kinnunen  <kkinnunen@apple.com>
2
25
3
        RemoteRenderingBackend commands are not processed in order if GPU process is waiting for replies
26
        RemoteRenderingBackend commands are not processed in order if GPU process is waiting for replies
- a/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp -3 / +3 lines
Lines 92-106 void AsyncScrollingCoordinator::handleWheelEventPhase(ScrollingNodeID nodeID, Pl a/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp_sec1
92
#endif
92
#endif
93
93
94
#if ENABLE(CSS_SCROLL_SNAP)
94
#if ENABLE(CSS_SCROLL_SNAP)
95
static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, const ScrollSnapOffsetsInfo<LayoutUnit>* offsetInfo, float deviceScaleFactor)
95
static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, const LayoutScrollSnapOffsetsInfo* offsetInfo, float deviceScaleFactor)
96
{
96
{
97
    if (!offsetInfo) {
97
    if (!offsetInfo) {
98
        node.setSnapOffsetsInfo(ScrollSnapOffsetsInfo<float>());
98
        node.setSnapOffsetsInfo(FloatScrollSnapOffsetsInfo());
99
        return;
99
        return;
100
    }
100
    }
101
101
102
    // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping?
102
    // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping?
103
    node.setSnapOffsetsInfo(offsetInfo->convertUnits<float>(deviceScaleFactor));
103
    node.setSnapOffsetsInfo(offsetInfo->convertUnits<float, FloatRect>(deviceScaleFactor));
104
}
104
}
105
#endif
105
#endif
106
106
- a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp -175 / +167 lines
Lines 41-108 a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec1
41
41
42
namespace WebCore {
42
namespace WebCore {
43
43
44
template <typename LayoutType>
44
template <typename UnitType>
45
static void indicesOfNearestSnapOffsetRanges(LayoutType offset, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, unsigned& lowerIndex, unsigned& upperIndex)
45
static Optional<std::pair<UnitType, unsigned>> findFirstSnapStopOffsetBetweenOriginAndDestination(const Vector<SnapOffset<UnitType>>& snapOffsets, UnitType scrollOriginOffset, UnitType scrollDestinationOffset)
46
{
46
{
47
    if (snapOffsetRanges.isEmpty()) {
47
    UnitType difference = scrollDestinationOffset - scrollOriginOffset;
48
        lowerIndex = invalidSnapOffsetIndex;
49
        upperIndex = invalidSnapOffsetIndex;
50
        return;
51
    }
52
53
    int lowerIndexAsInt = -1;
54
    int upperIndexAsInt = snapOffsetRanges.size();
55
    do {
56
        int middleIndex = (lowerIndexAsInt + upperIndexAsInt) / 2;
57
        auto& range = snapOffsetRanges[middleIndex];
58
        if (range.start < offset && offset < range.end) {
59
            lowerIndexAsInt = middleIndex;
60
            upperIndexAsInt = middleIndex;
61
            break;
62
        }
63
64
        if (offset > range.end)
65
            lowerIndexAsInt = middleIndex;
66
        else
67
            upperIndexAsInt = middleIndex;
68
    } while (lowerIndexAsInt < upperIndexAsInt - 1);
69
70
    if (offset <= snapOffsetRanges.first().start)
71
        lowerIndex = invalidSnapOffsetIndex;
72
    else
73
        lowerIndex = lowerIndexAsInt;
74
75
    if (offset >= snapOffsetRanges.last().end)
76
        upperIndex = invalidSnapOffsetIndex;
77
    else
78
        upperIndex = upperIndexAsInt;
79
}
80
81
template <typename LayoutType>
82
static void indicesOfNearestSnapOffsets(LayoutType offset, const Vector<SnapOffset<LayoutType>>& snapOffsets, unsigned& lowerIndex, unsigned& upperIndex)
83
{
84
    lowerIndex = 0;
85
    upperIndex = snapOffsets.size() - 1;
86
    while (lowerIndex < upperIndex - 1) {
87
        int middleIndex = (lowerIndex + upperIndex) / 2;
88
        auto middleOffset = snapOffsets[middleIndex].offset;
89
        if (offset == middleOffset) {
90
            upperIndex = middleIndex;
91
            lowerIndex = middleIndex;
92
            break;
93
        }
94
95
        if (offset > middleOffset)
96
            lowerIndex = middleIndex;
97
        else
98
            upperIndex = middleIndex;
99
    }
100
}
101
102
template <typename LayoutType>
103
static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(const Vector<SnapOffset<LayoutType>>& snapOffsets, LayoutType scrollOriginOffset, LayoutType scrollDestinationOffset)
104
{
105
    LayoutType difference = scrollDestinationOffset - scrollOriginOffset;
106
    if (!difference)
48
    if (!difference)
107
        return WTF::nullopt;
49
        return WTF::nullopt;
108
50
Lines 113-119 static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(con a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec2
113
        iteration = -1;
55
        iteration = -1;
114
    }
56
    }
115
57
116
    auto isPast = [difference](LayoutType mark, LayoutType candidate) {
58
    auto isPast = [difference](UnitType mark, UnitType candidate) {
117
        return (difference > 0 && candidate > mark) || (difference < 0 && candidate < mark);
59
        return (difference > 0 && candidate > mark) || (difference < 0 && candidate < mark);
118
    };
60
    };
119
61
Lines 124-188 static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(con a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec3
124
        if (snapOffsets[i].stop != ScrollSnapStop::Always)
66
        if (snapOffsets[i].stop != ScrollSnapStop::Always)
125
            continue;
67
            continue;
126
        if (isPast(scrollOriginOffset, offset))
68
        if (isPast(scrollOriginOffset, offset))
127
            return i;
69
            return std::make_pair(offset, static_cast<unsigned>(i));
128
    }
70
    }
129
71
130
    return WTF::nullopt;
72
    return WTF::nullopt;
131
}
73
}
132
74
133
template <typename LayoutType>
134
static std::pair<LayoutType, unsigned> closestSnapOffsetWithOffsetsAndRanges(const Vector<SnapOffset<LayoutType>>& snapOffsets, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, LayoutType scrollDestinationOffset, float velocity, Optional<LayoutType> originalOffsetForDirectionalSnapping)
135
{
136
    if (snapOffsets.isEmpty())
137
        return std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
138
139
    if (originalOffsetForDirectionalSnapping.hasValue()) {
140
        auto firstSnapStopOffsetIndex = findFirstSnapStopOffsetBetweenOriginAndDestination(snapOffsets, *originalOffsetForDirectionalSnapping, scrollDestinationOffset);
141
        if (firstSnapStopOffsetIndex.hasValue())
142
            return std::make_pair(snapOffsets[*firstSnapStopOffsetIndex].offset, *firstSnapStopOffsetIndex);
143
    }
144
145
    unsigned lowerSnapOffsetRangeIndex;
146
    unsigned upperSnapOffsetRangeIndex;
147
    indicesOfNearestSnapOffsetRanges<LayoutType>(scrollDestinationOffset, snapOffsetRanges, lowerSnapOffsetRangeIndex, upperSnapOffsetRangeIndex);
148
    if (lowerSnapOffsetRangeIndex == upperSnapOffsetRangeIndex && upperSnapOffsetRangeIndex != invalidSnapOffsetIndex)
149
        return std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
150
151
    if (scrollDestinationOffset <= snapOffsets.first().offset)
152
        return std::make_pair(snapOffsets.first().offset, 0u);
153
154
    if (scrollDestinationOffset >= snapOffsets.last().offset)
155
        return std::make_pair(snapOffsets.last().offset, snapOffsets.size() - 1);
156
157
    unsigned lowerIndex;
158
    unsigned upperIndex;
159
    indicesOfNearestSnapOffsets<LayoutType>(scrollDestinationOffset, snapOffsets, lowerIndex, upperIndex);
160
    LayoutType lowerSnapPosition = snapOffsets[lowerIndex].offset;
161
    LayoutType upperSnapPosition = snapOffsets[upperIndex].offset;
162
    if (!std::abs(velocity)) {
163
        bool isCloserToLowerSnapPosition = scrollDestinationOffset - lowerSnapPosition <= upperSnapPosition - scrollDestinationOffset;
164
        return isCloserToLowerSnapPosition ? std::make_pair(lowerSnapPosition, lowerIndex) : std::make_pair(upperSnapPosition, upperIndex);
165
    }
166
167
    // 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
168
    // as long as a scroll snap offset range does not lie between the scroll destination and the targeted snap offset. If we are doing directional
169
    // 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.
170
    // This allows directional scrolling to escape snap points.
171
    if (velocity < 0) {
172
        if (lowerSnapOffsetRangeIndex == invalidSnapOffsetIndex || lowerSnapPosition >= snapOffsetRanges[lowerSnapOffsetRangeIndex].end)
173
            return std::make_pair(lowerSnapPosition, lowerIndex);
174
        if (!originalOffsetForDirectionalSnapping.hasValue() || *originalOffsetForDirectionalSnapping > upperSnapPosition)
175
            return std::make_pair(upperSnapPosition, upperIndex);
176
    } else {
177
        if (upperSnapOffsetRangeIndex == invalidSnapOffsetIndex || snapOffsetRanges[upperSnapOffsetRangeIndex].start >= upperSnapPosition)
178
            return std::make_pair(upperSnapPosition, upperIndex);
179
        if (!originalOffsetForDirectionalSnapping.hasValue() || *originalOffsetForDirectionalSnapping < lowerSnapPosition)
180
            return std::make_pair(lowerSnapPosition, lowerIndex);
181
    }
182
183
    return std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
184
}
185
186
enum class InsetOrOutset {
75
enum class InsetOrOutset {
187
    Inset,
76
    Inset,
188
    Outset
77
    Outset
Lines 219-248 static LayoutUnit computeScrollSnapAlignOffset(LayoutUnit minLocation, LayoutUni a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec4
219
    }
108
    }
220
}
109
}
221
110
222
static void computeAxisProximitySnapOffsetRanges(const Vector<SnapOffset<LayoutUnit>>& snapOffsets, Vector<ScrollOffsetRange<LayoutUnit>>& offsetRanges, LayoutUnit scrollPortAxisLength)
223
{
224
    // This is an arbitrary choice for what it means to be "in proximity" of a snap offset. We should play around with
225
    // this and see what feels best.
226
    static const float ratioOfScrollPortAxisLengthToBeConsideredForProximity = 0.3;
227
    if (snapOffsets.size() < 2)
228
        return;
229
230
    // The extra rule accounting for scroll offset ranges in between the scroll destination and a potential snap offset
231
    // handles the corner case where the user scrolls with momentum very lightly away from a snap offset, such that the
232
    // predicted scroll destination is still within proximity of the snap offset. In this case, the regular (mandatory
233
    // scroll snapping) behavior would be to snap to the next offset in the direction of momentum scrolling, but
234
    // instead, it is more intuitive to either return to the original snap position (which we arbitrarily choose here)
235
    // or scroll just outside of the snap offset range. This is another minor behavior tweak that we should play around
236
    // with to see what feels best.
237
    LayoutUnit proximityDistance { ratioOfScrollPortAxisLengthToBeConsideredForProximity * scrollPortAxisLength };
238
    for (size_t index = 1; index < snapOffsets.size(); ++index) {
239
        auto startOffset = snapOffsets[index - 1].offset + proximityDistance;
240
        auto endOffset = snapOffsets[index].offset - proximityDistance;
241
        if (startOffset < endOffset)
242
            offsetRanges.append({ startOffset, endOffset });
243
    }
244
}
245
246
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates)
111
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates)
247
{
112
{
248
    auto scrollSnapType = scrollingElementStyle.scrollSnapType();
113
    auto scrollSnapType = scrollingElementStyle.scrollSnapType();
Lines 262-267 void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec5
262
127
263
    HashMap<float, SnapOffset<LayoutUnit>> verticalSnapOffsetsMap;
128
    HashMap<float, SnapOffset<LayoutUnit>> verticalSnapOffsetsMap;
264
    HashMap<float, SnapOffset<LayoutUnit>> horizontalSnapOffsetsMap;
129
    HashMap<float, SnapOffset<LayoutUnit>> horizontalSnapOffsetsMap;
130
    Vector<LayoutRect> snapAreas;
265
    bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline;
131
    bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline;
266
    bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block;
132
    bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block;
267
133
Lines 288-304 void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec6
288
154
289
        scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollMargin(), InsetOrOutset::Outset);
155
        scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollMargin(), InsetOrOutset::Outset);
290
        LOG_WITH_STREAM(ScrollSnap, stream << "    Considering scroll snap target area " << scrollSnapArea);
156
        LOG_WITH_STREAM(ScrollSnap, stream << "    Considering scroll snap target area " << scrollSnapArea);
157
291
        auto alignment = child->style().scrollSnapAlign();
158
        auto alignment = child->style().scrollSnapAlign();
159
        bool snapsHorizontally = hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None;
160
        bool snapsVertically = hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None;
161
        if (!snapsHorizontally && !snapsVertically)
162
            continue;
163
164
        // The scroll snap area is defined via its scroll position, so convert the snap area rectangle to be relative to scroll offsets.
165
        auto snapAreaOriginRelativeToBorderEdge = scrollSnapArea.location() - scrollSnapPort.location();
166
        LayoutRect scrollSnapAreaAsOffsets(scrollableArea.scrollOffsetFromPosition(roundedIntPoint(snapAreaOriginRelativeToBorderEdge)), scrollSnapArea.size());
167
        snapAreas.append(scrollSnapAreaAsOffsets);
168
292
        auto stop = child->style().scrollSnapStop();
169
        auto stop = child->style().scrollSnapStop();
293
        if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) {
170
        if (snapsHorizontally) {
294
            auto absoluteScrollXPosition = computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.maxX(), alignment.x, scrollerIsRTL) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.maxX(), alignment.x, scrollerIsRTL);
171
            auto absoluteScrollXPosition = computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.maxX(), alignment.x, scrollerIsRTL) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.maxX(), alignment.x, scrollerIsRTL);
295
            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ roundToInt(absoluteScrollXPosition), 0 }).x(), 0, maxScrollOffset.x());
172
            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ roundToInt(absoluteScrollXPosition), 0 }).x(), 0, maxScrollOffset.x());
296
            addOrUpdateStopForSnapOffset(horizontalSnapOffsetsMap, { absoluteScrollOffset, stop });
173
            addOrUpdateStopForSnapOffset(horizontalSnapOffsetsMap, { absoluteScrollOffset, stop, snapAreas.size() - 1 });
297
        }
174
        }
298
        if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) {
175
        if (snapsVertically) {
299
            auto absoluteScrollYPosition = computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.maxY(), alignment.y, false) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.maxY(), alignment.y, false);
176
            auto absoluteScrollYPosition = computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.maxY(), alignment.y, false) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.maxY(), alignment.y, false);
300
            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ 0, roundToInt(absoluteScrollYPosition) }).y(), 0, maxScrollOffset.y());
177
            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ 0, roundToInt(absoluteScrollYPosition) }).y(), 0, maxScrollOffset.y());
301
            addOrUpdateStopForSnapOffset(verticalSnapOffsetsMap, { absoluteScrollOffset, stop });
178
            addOrUpdateStopForSnapOffset(verticalSnapOffsetsMap, { absoluteScrollOffset, stop, snapAreas.size() - 1 });
302
        }
179
        }
303
    }
180
    }
304
181
Lines 308-339 void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec7
308
    };
185
    };
309
186
310
    Vector<SnapOffset<LayoutUnit>> horizontalSnapOffsets = copyToVector(horizontalSnapOffsetsMap.values());
187
    Vector<SnapOffset<LayoutUnit>> horizontalSnapOffsets = copyToVector(horizontalSnapOffsetsMap.values());
311
    Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges;
312
    if (!horizontalSnapOffsets.isEmpty()) {
188
    if (!horizontalSnapOffsets.isEmpty()) {
313
        std::sort(horizontalSnapOffsets.begin(), horizontalSnapOffsets.end(), compareSnapOffsets);
189
        std::sort(horizontalSnapOffsets.begin(), horizontalSnapOffsets.end(), compareSnapOffsets);
314
        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
315
            computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width());
316
317
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offsets: " << horizontalSnapOffsets);
190
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offsets: " << horizontalSnapOffsets);
318
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offset ranges: " << horizontalSnapOffsetRanges);
319
    }
191
    }
320
192
321
    Vector<SnapOffset<LayoutUnit>> verticalSnapOffsets = copyToVector(verticalSnapOffsetsMap.values());
193
    Vector<SnapOffset<LayoutUnit>> verticalSnapOffsets = copyToVector(verticalSnapOffsetsMap.values());
322
    Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges;
323
    if (!verticalSnapOffsets.isEmpty()) {
194
    if (!verticalSnapOffsets.isEmpty()) {
324
        std::sort(verticalSnapOffsets.begin(), verticalSnapOffsets.end(), compareSnapOffsets);
195
        std::sort(verticalSnapOffsets.begin(), verticalSnapOffsets.end(), compareSnapOffsets);
325
        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
326
            computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height());
327
328
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offsets: " << verticalSnapOffsets);
196
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offsets: " << verticalSnapOffsets);
329
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offset ranges: " << verticalSnapOffsetRanges);
330
    }
197
    }
331
198
199
    if (!snapAreas.isEmpty())
200
        LOG_WITH_STREAM(ScrollSnap, stream << " => Computed snap areas: " << snapAreas);
201
332
    scrollableArea.setScrollSnapOffsetInfo({
202
    scrollableArea.setScrollSnapOffsetInfo({
203
        scrollSnapType.strictness,
333
        horizontalSnapOffsets,
204
        horizontalSnapOffsets,
334
        verticalSnapOffsets,
205
        verticalSnapOffsets,
335
        horizontalSnapOffsetRanges,
206
        snapAreas,
336
        verticalSnapOffsetRanges
337
    });
207
    });
338
}
208
}
339
209
Lines 347-403 static LayoutUnit convertOffsetUnit(float input, float /* scaleFactor */) a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp_sec8
347
    return LayoutUnit(input);
217
    return LayoutUnit(input);
348
}
218
}
349
219
350
template <typename InputType, typename OutputType>
220
template <typename InputType, typename InputRectType, typename OutputType, typename OutputRectType>
351
static ScrollSnapOffsetsInfo<OutputType> convertOffsetInfo(const ScrollSnapOffsetsInfo<InputType>& input, float scaleFactor = 0.0)
221
static ScrollSnapOffsetsInfo<OutputType, OutputRectType> convertOffsetInfo(const ScrollSnapOffsetsInfo<InputType, InputRectType>& input, float scaleFactor = 0.0)
352
{
222
{
353
    auto convertOffsets = [scaleFactor](const Vector<SnapOffset<InputType>>& input)
223
    auto convertOffsets = [scaleFactor](const Vector<SnapOffset<InputType>>& input)
354
    {
224
    {
355
        Vector<SnapOffset<OutputType>> output;
225
        Vector<SnapOffset<OutputType>> output;
356
        output.reserveInitialCapacity(input.size());
226
        output.reserveInitialCapacity(input.size());
357
        for (auto& offset : input)
227
        for (auto& offset : input)
358
            output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop });
228
            output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop, offset.snapAreaIndex });
359
        return output;
229
        return output;
360
    };
230
    };
361
231
362
    auto convertOffsetRanges = [scaleFactor](const Vector<ScrollOffsetRange<InputType>>& input)
232
    auto convertRects = [scaleFactor](const Vector<InputRectType>& input)
363
    {
233
    {
364
        Vector<ScrollOffsetRange<OutputType>> output;
234
        Vector<OutputRectType> output;
365
        output.reserveInitialCapacity(input.size());
235
        output.reserveInitialCapacity(input.size());
366
        for (auto& range : input)
236
        for (auto& rect : input) {
367
            output.uncheckedAppend({ convertOffsetUnit(range.start, scaleFactor), convertOffsetUnit(range.end, scaleFactor) });
237
            OutputRectType outputRect(
238
                convertOffsetUnit(rect.x(), scaleFactor), convertOffsetUnit(rect.y(), scaleFactor),
239
                convertOffsetUnit(rect.width(), scaleFactor), convertOffsetUnit(rect.height(), scaleFactor));
240
            output.uncheckedAppend(outputRect);
241
        }
368
        return output;
242
        return output;
369
    };
243
    };
370
244
371
    return {
245
    return {
246
        input.strictness,
372
        convertOffsets(input.horizontalSnapOffsets),
247
        convertOffsets(input.horizontalSnapOffsets),
373
        convertOffsets(input.verticalSnapOffsets),
248
        convertOffsets(input.verticalSnapOffsets),
374
        convertOffsetRanges(input.horizontalSnapOffsetRanges),
249
        convertRects(input.snapAreas),
375
        convertOffsetRanges(input.verticalSnapOffsetRanges)
376
    };
250
    };
377
}
251
}
378
252
379
template <> template <>
253
template <> template <>
380
ScrollSnapOffsetsInfo<LayoutUnit> ScrollSnapOffsetsInfo<float>::convertUnits(float /* unusedScaleFactor */) const
254
LayoutScrollSnapOffsetsInfo FloatScrollSnapOffsetsInfo::convertUnits(float /* unusedScaleFactor */) const
381
{
255
{
382
    return convertOffsetInfo<float, LayoutUnit>(*this);
256
    return convertOffsetInfo<float, FloatRect, LayoutUnit, LayoutRect>(*this);
383
}
257
}
384
258
385
template <> template <>
259
template <> template <>
386
ScrollSnapOffsetsInfo<float> ScrollSnapOffsetsInfo<LayoutUnit>::convertUnits(float deviceScaleFactor) const
260
FloatScrollSnapOffsetsInfo LayoutScrollSnapOffsetsInfo::convertUnits(float deviceScaleFactor) const
387
{
261
{
388
    return convertOffsetInfo<LayoutUnit, float>(*this, deviceScaleFactor);
262
    return convertOffsetInfo<LayoutUnit, LayoutRect, float, FloatRect>(*this, deviceScaleFactor);
389
}
263
}
390
264
391
template <>
265
template <typename UnitType, typename RectType>
392
std::pair<LayoutUnit, unsigned> ScrollSnapOffsetsInfo<LayoutUnit>::closestSnapOffset(ScrollEventAxis axis, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const
266
static UnitType maxForAxis(RectType rect, ScrollEventAxis axis)
393
{
267
{
394
    return closestSnapOffsetWithOffsetsAndRanges(offsetsForAxis(axis), offsetRangesForAxis(axis), scrollDestinationOffset, velocity, originalPositionForDirectionalSnapping);
268
    return axis == ScrollEventAxis::Horizontal ? rect.maxX() : rect.maxY();
395
}
269
}
396
270
397
template <>
271
template <typename UnitType, typename RectType>
398
std::pair<float, unsigned> ScrollSnapOffsetsInfo<float>::closestSnapOffset(ScrollEventAxis axis, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const
272
static UnitType minForAxis(RectType rect, ScrollEventAxis axis)
273
{
274
    return axis == ScrollEventAxis::Horizontal ? rect.x() : rect.y();
275
}
276
277
template <typename UnitType>
278
static bool isNearEnoughToOffsetForProximity(ScrollSnapStrictness strictness, UnitType scrollDestination, UnitType candidateSnapOffset, UnitType viewportLength)
279
{
280
    if (strictness != ScrollSnapStrictness::Proximity)
281
        return true;
282
283
    // This is an arbitrary choice for what it means to be "in proximity" of a snap offset. We should play around with
284
    // this and see what feels best.
285
    static const float ratioOfScrollPortAxisLengthToBeConsideredForProximity = 0.3;
286
    return std::abs(float {candidateSnapOffset - scrollDestination}) <= (viewportLength * ratioOfScrollPortAxisLengthToBeConsideredForProximity);
287
}
288
289
template <typename UnitType, typename RectType, typename SizeType>
290
static std::pair<UnitType, unsigned> calculateClosestSnapOffset(const ScrollSnapOffsetsInfo<UnitType, RectType>& info, ScrollEventAxis axis, const SizeType& viewportSize, UnitType scrollDestinationOffset, float velocity, Optional<UnitType> originalOffset)
291
{
292
    const auto& snapOffsets = info.offsetsForAxis(axis);
293
    auto pairForNoSnapping = std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
294
    if (snapOffsets.isEmpty())
295
        return pairForNoSnapping;
296
297
    // The first snap point with `scroll-snap-stop: always` between the original position and destination takes precedence.
298
    if (originalOffset) {
299
        if (auto firstSnapStopOffset = findFirstSnapStopOffsetBetweenOriginAndDestination(snapOffsets, *originalOffset, scrollDestinationOffset))
300
            return *firstSnapStopOffset;
301
    }
302
303
    auto viewportLength = axis == ScrollEventAxis::Horizontal ? viewportSize.width() : viewportSize.height();
304
    bool landedInsideSnapAreaThatConsumesViewport = false;
305
    Optional<std::pair<UnitType, unsigned>> previous, next;
306
    Optional<RectType> previousSnapArea, nextSnapArea;
307
    for (unsigned i = 0; i < snapOffsets.size(); i++) {
308
        const auto& snapArea = info.snapAreas[snapOffsets[i].snapAreaIndex];
309
        landedInsideSnapAreaThatConsumesViewport |= (minForAxis<UnitType>(snapArea, axis) <= scrollDestinationOffset && maxForAxis<UnitType>(snapArea, axis) >= (scrollDestinationOffset + viewportLength));
310
        // If the scroll landed exactly on a snap offset, choose that one.
311
        UnitType potentialSnapOffset = snapOffsets[i].offset;
312
        if (potentialSnapOffset == scrollDestinationOffset)
313
            return std::make_pair(potentialSnapOffset, i);
314
315
        if (potentialSnapOffset < scrollDestinationOffset) {
316
            previous = std::make_pair(potentialSnapOffset, i);
317
            previousSnapArea = snapArea;
318
        } else if (!next && potentialSnapOffset > scrollDestinationOffset) {
319
            next = std::make_pair(potentialSnapOffset, i);
320
            nextSnapArea = snapArea;
321
        }
322
    }
323
324
    // From https://www.w3.org/TR/css-scroll-snap-1/#snap-overflow
325
    // "If the snap area is larger than the snapport in a particular axis, then any scroll position
326
    // in which the snap area covers the snapport, and the distance between the geometrically
327
    // previous and subsequent snap positions in that axis is larger than size of the snapport in
328
    // that axis, is a valid snap position in that axis. The UA may use the specified alignment as a
329
    // more precise target for certain scroll operations (e.g. explicit paging)."
330
    if (landedInsideSnapAreaThatConsumesViewport && (!previous || !next || ((*next).first - (*previous).first) >= viewportLength))
331
        return pairForNoSnapping;
332
333
    if (originalOffset) {
334
        // If this is a directional scroll and there are no scroll stops in a particular direction,
335
        // don't scroll past the edge of the scroll area.
336
        if (!next && previous)
337
            return std::make_pair(std::max((*previous).first, maxForAxis<UnitType>(*previousSnapArea, axis) - viewportLength), invalidSnapOffsetIndex);
338
        if (!previous && next)
339
            return std::make_pair(minForAxis<UnitType>(*nextSnapArea, axis), invalidSnapOffsetIndex);
340
341
        // From https://www.w3.org/TR/css-scroll-snap-1/#choosing
342
        // "User agents must ensure that a user can “escape” a snap position, regardless of the scroll
343
        // method. For example, if the snap type is mandatory and the next snap position is more than
344
        // two screen-widths away, a naïve “always snap to nearest” selection algorithm might “trap” the
345
        // user if their end position was only one screen-width away. Instead, a smarter algorithm that
346
        // only returned to the starting snap position if the end-point was a fairly small distance from
347
        // it, and otherwise ignored the starting snap position, would give better behavior."
348
        // We only do this if we are in a situation where we are choosing between two compatible
349
        // snap offsets.
350
        if (*originalOffset < scrollDestinationOffset && (*previous).first <= *originalOffset)
351
            previous.reset();
352
        if (*originalOffset > scrollDestinationOffset && (*next).first >= *originalOffset)
353
            next.reset();
354
    }
355
356
    if (next && !isNearEnoughToOffsetForProximity(info.strictness, scrollDestinationOffset, (*next).first, viewportLength))
357
        next.reset();
358
    if (previous && !isNearEnoughToOffsetForProximity(info.strictness, scrollDestinationOffset, (*previous).first, viewportLength))
359
        previous.reset();
360
361
    // If we don't have compatible offsets to choose between, just do the obvious thing
362
    // and do no snapping or only select the single compatible offset.
363
    if (!previous && !next)
364
        return pairForNoSnapping;
365
    if (!previous)
366
        return *next;
367
    if (!next)
368
        return *previous;
369
370
    // If this scroll isn't directional, then choose whatever snap point is closer.
371
    UnitType previousSnapOffset = (*previous).first;
372
    UnitType nextSnapOffset = (*next).first;
373
    if (!std::abs(velocity)) {
374
        bool isCloserToPreviousSnapIndex = scrollDestinationOffset - previousSnapOffset <= nextSnapOffset - scrollDestinationOffset;
375
        return isCloserToPreviousSnapIndex ? *previous : *next;
376
    }
377
378
    // Finally, if we have a directional scroll prefer the snap point that is in the scroll direction.
379
    return velocity < 0 ? *previous : *next;
380
}
381
382
383
template <> template <>
384
std::pair<LayoutUnit, unsigned> LayoutScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis axis, const LayoutSize& viewportSize, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalOffsetForDirectionalSnapping) const
385
{
386
    return calculateClosestSnapOffset<LayoutUnit, LayoutRect, LayoutSize>(*this, axis, viewportSize, scrollDestinationOffset, velocity, originalOffsetForDirectionalSnapping);
387
}
388
389
template <> template <>
390
std::pair<float, unsigned> FloatScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis axis, const FloatSize& viewportSize, float scrollDestinationOffset, float velocity, Optional<float> originalOffsetForDirectionalSnapping) const
399
{
391
{
400
    return closestSnapOffsetWithOffsetsAndRanges(offsetsForAxis(axis), offsetRangesForAxis(axis), scrollDestinationOffset, velocity, originalPositionForDirectionalSnapping);
392
    return calculateClosestSnapOffset<float, FloatRect, FloatSize>(*this, axis, viewportSize, scrollDestinationOffset, velocity, originalOffsetForDirectionalSnapping);
401
}
393
}
402
394
403
}
395
}
- a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h -42 / +27 lines
Lines 27-32 a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h_sec1
27
27
28
#if ENABLE(CSS_SCROLL_SNAP)
28
#if ENABLE(CSS_SCROLL_SNAP)
29
29
30
#include "FloatRect.h"
31
#include "FloatSize.h"
32
#include "LayoutRect.h"
33
#include "LayoutSize.h"
30
#include "LayoutUnit.h"
34
#include "LayoutUnit.h"
31
#include "ScrollTypes.h"
35
#include "ScrollTypes.h"
32
#include "StyleScrollSnapPoints.h"
36
#include "StyleScrollSnapPoints.h"
Lines 35-73 a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h_sec2
35
39
36
namespace WebCore {
40
namespace WebCore {
37
41
38
class LayoutRect;
39
class ScrollableArea;
42
class ScrollableArea;
40
class RenderBox;
43
class RenderBox;
41
class RenderStyle;
44
class RenderStyle;
42
45
43
template <typename T>
46
template <typename OffsetType>
44
struct SnapOffset {
47
struct SnapOffset {
45
    T offset;
48
    OffsetType offset;
46
    ScrollSnapStop stop;
49
    ScrollSnapStop stop;
50
    size_t snapAreaIndex;
47
};
51
};
48
52
49
template <typename T>
53
template <typename OffsetType, typename RectType>
50
struct ScrollOffsetRange {
51
    T start;
52
    T end;
53
};
54
55
template <typename T>
56
struct ScrollSnapOffsetsInfo {
54
struct ScrollSnapOffsetsInfo {
57
    WTF_MAKE_STRUCT_FAST_ALLOCATED;
55
    WTF_MAKE_STRUCT_FAST_ALLOCATED;
58
    Vector<SnapOffset<T>> horizontalSnapOffsets;
56
    ScrollSnapStrictness strictness;
59
    Vector<SnapOffset<T>> verticalSnapOffsets;
60
57
61
    // Snap offset ranges represent non-empty ranges of scroll offsets in which scrolling may rest after scroll snapping.
58
    Vector<SnapOffset<OffsetType>> horizontalSnapOffsets;
62
    // These are used in two cases: (1) for proximity scroll snapping, where portions of areas between adjacent snap offsets
59
    Vector<SnapOffset<OffsetType>> verticalSnapOffsets;
63
    // may emit snap offset ranges, and (2) in the case where the snap area is larger than the snap port, in which case areas
60
    Vector<RectType> snapAreas;
64
    // where the snap port fits within the snap area are considered to be valid snap positions.
65
    Vector<ScrollOffsetRange<T>> horizontalSnapOffsetRanges;
66
    Vector<ScrollOffsetRange<T>> verticalSnapOffsetRanges;
67
61
68
    bool isEqual(const ScrollSnapOffsetsInfo<T>& other) const
62
    bool isEqual(const ScrollSnapOffsetsInfo<OffsetType, RectType>& other) const
69
    {
63
    {
70
        return horizontalSnapOffsets == other.horizontalSnapOffsets && verticalSnapOffsets == other.verticalSnapOffsets && horizontalSnapOffsetRanges == other.horizontalSnapOffsetRanges && verticalSnapOffsetRanges == other.verticalSnapOffsetRanges;
64
        return strictness == other.strictness && horizontalSnapOffsets == other.horizontalSnapOffsets && verticalSnapOffsets == other.verticalSnapOffsets && snapAreas == other.snapAreas;
71
    }
65
    }
72
66
73
    bool isEmpty() const
67
    bool isEmpty() const
Lines 75-103 struct ScrollSnapOffsetsInfo { a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h_sec3
75
        return horizontalSnapOffsets.isEmpty() && verticalSnapOffsets.isEmpty();
69
        return horizontalSnapOffsets.isEmpty() && verticalSnapOffsets.isEmpty();
76
    }
70
    }
77
71
78
    Vector<SnapOffset<T>> offsetsForAxis(ScrollEventAxis axis) const
72
    Vector<SnapOffset<OffsetType>> offsetsForAxis(ScrollEventAxis axis) const
79
    {
73
    {
80
        return axis == ScrollEventAxis::Vertical ? verticalSnapOffsets : horizontalSnapOffsets;
74
        return axis == ScrollEventAxis::Vertical ? verticalSnapOffsets : horizontalSnapOffsets;
81
    }
75
    }
82
76
83
    Vector<ScrollOffsetRange<T>> offsetRangesForAxis(ScrollEventAxis axis) const
77
    template<typename OutputType, typename OutputRectType> ScrollSnapOffsetsInfo<OutputType, OutputRectType> convertUnits(float deviceScaleFactor = 0.0) const;
84
    {
78
    template<typename SizeType> WEBCORE_EXPORT std::pair<OffsetType, unsigned> closestSnapOffset(ScrollEventAxis, const SizeType& viewportSize, OffsetType scrollDestinationOffset, float velocity, Optional<OffsetType> originalPositionForDirectionalSnapping = WTF::nullopt) const;
85
        return axis == ScrollEventAxis::Vertical ? verticalSnapOffsetRanges : horizontalSnapOffsetRanges;
86
    }
87
88
    template<typename OutputType> ScrollSnapOffsetsInfo<OutputType> convertUnits(float deviceScaleFactor = 0.0) const;
89
    WEBCORE_EXPORT std::pair<T, unsigned> closestSnapOffset(ScrollEventAxis, T scrollDestinationOffset, float velocity, Optional<T> originalPositionForDirectionalSnapping = WTF::nullopt) const;
90
};
79
};
91
80
81
using LayoutScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<LayoutUnit, LayoutRect>;
82
using FloatScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<float, FloatRect>;
83
92
template <> template <>
84
template <> template <>
93
ScrollSnapOffsetsInfo<LayoutUnit> ScrollSnapOffsetsInfo<float>::convertUnits(float /* unusedScaleFactor */) const;
85
LayoutScrollSnapOffsetsInfo FloatScrollSnapOffsetsInfo::convertUnits(float /* unusedScaleFactor */) const;
94
template <>
86
template <> template <>
95
WEBCORE_EXPORT std::pair<float, unsigned> ScrollSnapOffsetsInfo<float>::closestSnapOffset(ScrollEventAxis, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const;
87
WEBCORE_EXPORT std::pair<float, unsigned> FloatScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis, const FloatSize& viewportSize, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const;
96
88
97
template <> template <>
89
template <> template <>
98
ScrollSnapOffsetsInfo<float> ScrollSnapOffsetsInfo<LayoutUnit>::convertUnits(float deviceScaleFactor) const;
90
FloatScrollSnapOffsetsInfo LayoutScrollSnapOffsetsInfo::convertUnits(float deviceScaleFactor) const;
99
template <>
91
template <> template <>
100
WEBCORE_EXPORT std::pair<LayoutUnit, unsigned> ScrollSnapOffsetsInfo<LayoutUnit>::closestSnapOffset(ScrollEventAxis, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const;
92
WEBCORE_EXPORT std::pair<LayoutUnit, unsigned> LayoutScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis, const LayoutSize& viewportSize, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const;
101
93
102
const unsigned invalidSnapOffsetIndex = UINT_MAX;
94
const unsigned invalidSnapOffsetIndex = UINT_MAX;
103
95
Lines 106-112 const unsigned invalidSnapOffsetIndex = UINT_MAX; a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h_sec4
106
// the scrolling container's border box.
98
// the scrolling container's border box.
107
void updateSnapOffsetsForScrollableArea(ScrollableArea&, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates);
99
void updateSnapOffsetsForScrollableArea(ScrollableArea&, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates);
108
100
109
template <typename T> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffset<T> offset)
101
template <typename OffsetType> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffset<OffsetType> offset)
110
{
102
{
111
    ts << offset.offset;
103
    ts << offset.offset;
112
    if (offset.stop == ScrollSnapStop::Always)
104
    if (offset.stop == ScrollSnapStop::Always)
Lines 114-126 template <typename T> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffse a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h_sec5
114
    return ts;
106
    return ts;
115
}
107
}
116
108
117
template<typename T>
118
TextStream& operator<<(TextStream& ts, const ScrollOffsetRange<T>& range)
119
{
120
    ts << "start: " << range.start << " end: " << range.end;
121
    return ts;
122
}
123
124
}; // namespace WebCore
109
}; // namespace WebCore
125
110
126
#endif // ENABLE(CSS_SCROLL_SNAP)
111
#endif // ENABLE(CSS_SCROLL_SNAP)
- a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp -1 / +1 lines
Lines 157-163 void ScrollingStateScrollingNode::setScrollOrigin(const IntPoint& scrollOrigin) a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp_sec1
157
}
157
}
158
158
159
#if ENABLE(CSS_SCROLL_SNAP)
159
#if ENABLE(CSS_SCROLL_SNAP)
160
void ScrollingStateScrollingNode::setSnapOffsetsInfo(const ScrollSnapOffsetsInfo<float>& info)
160
void ScrollingStateScrollingNode::setSnapOffsetsInfo(const FloatScrollSnapOffsetsInfo& info)
161
{
161
{
162
    if (m_snapOffsetsInfo.isEqual(info))
162
    if (m_snapOffsetsInfo.isEqual(info))
163
        return;
163
        return;
- a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h -3 / +3 lines
Lines 71-78 public: a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h_sec1
71
    WEBCORE_EXPORT void setScrollOrigin(const IntPoint&);
71
    WEBCORE_EXPORT void setScrollOrigin(const IntPoint&);
72
72
73
#if ENABLE(CSS_SCROLL_SNAP)
73
#if ENABLE(CSS_SCROLL_SNAP)
74
    const ScrollSnapOffsetsInfo<float>& snapOffsetsInfo() const { return m_snapOffsetsInfo; }
74
    const FloatScrollSnapOffsetsInfo& snapOffsetsInfo() const { return m_snapOffsetsInfo; }
75
    WEBCORE_EXPORT void setSnapOffsetsInfo(const ScrollSnapOffsetsInfo<float>& newOffsetsInfo);
75
    WEBCORE_EXPORT void setSnapOffsetsInfo(const FloatScrollSnapOffsetsInfo& newOffsetsInfo);
76
76
77
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
77
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
78
    WEBCORE_EXPORT void setCurrentHorizontalSnapPointIndex(unsigned);
78
    WEBCORE_EXPORT void setCurrentHorizontalSnapPointIndex(unsigned);
Lines 130-136 private: a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h_sec2
130
    IntPoint m_scrollOrigin;
130
    IntPoint m_scrollOrigin;
131
131
132
#if ENABLE(CSS_SCROLL_SNAP)
132
#if ENABLE(CSS_SCROLL_SNAP)
133
    ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo;
133
    FloatScrollSnapOffsetsInfo m_snapOffsetsInfo;
134
    unsigned m_currentHorizontalSnapPointIndex { 0 };
134
    unsigned m_currentHorizontalSnapPointIndex { 0 };
135
    unsigned m_currentVerticalSnapPointIndex { 0 };
135
    unsigned m_currentVerticalSnapPointIndex { 0 };
136
#endif
136
#endif
- a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp -1 / +1 lines
Lines 327-333 void ScrollingTreeScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTr a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp_sec1
327
}
327
}
328
328
329
#if ENABLE(CSS_SCROLL_SNAP)
329
#if ENABLE(CSS_SCROLL_SNAP)
330
const ScrollSnapOffsetsInfo<float>& ScrollingTreeScrollingNode::snapOffsetsInfo() const
330
const FloatScrollSnapOffsetsInfo& ScrollingTreeScrollingNode::snapOffsetsInfo() const
331
{
331
{
332
    return m_snapOffsetsInfo;
332
    return m_snapOffsetsInfo;
333
}
333
}
- a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h -2 / +2 lines
Lines 95-101 public: a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h_sec1
95
    bool canHaveScrollbars() const { return m_scrollableAreaParameters.horizontalScrollbarMode != ScrollbarAlwaysOff || m_scrollableAreaParameters.verticalScrollbarMode != ScrollbarAlwaysOff; }
95
    bool canHaveScrollbars() const { return m_scrollableAreaParameters.horizontalScrollbarMode != ScrollbarAlwaysOff || m_scrollableAreaParameters.verticalScrollbarMode != ScrollbarAlwaysOff; }
96
96
97
#if ENABLE(CSS_SCROLL_SNAP)
97
#if ENABLE(CSS_SCROLL_SNAP)
98
    const ScrollSnapOffsetsInfo<float>& snapOffsetsInfo() const;
98
    const FloatScrollSnapOffsetsInfo& snapOffsetsInfo() const;
99
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
99
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
100
    unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; }
100
    unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; }
101
    void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
101
    void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
Lines 159-165 private: a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h_sec2
159
    FloatPoint m_currentScrollPosition;
159
    FloatPoint m_currentScrollPosition;
160
    IntPoint m_scrollOrigin;
160
    IntPoint m_scrollOrigin;
161
#if ENABLE(CSS_SCROLL_SNAP)
161
#if ENABLE(CSS_SCROLL_SNAP)
162
    ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo;
162
    FloatScrollSnapOffsetsInfo m_snapOffsetsInfo;
163
    unsigned m_currentHorizontalSnapPointIndex { 0 };
163
    unsigned m_currentHorizontalSnapPointIndex { 0 };
164
    unsigned m_currentVerticalSnapPointIndex { 0 };
164
    unsigned m_currentVerticalSnapPointIndex { 0 };
165
#endif
165
#endif
- a/Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm -1 / +1 lines
Lines 65-71 void ScrollingTreeScrollingNodeDelegateMac::updateFromStateNode(const ScrollingS a/Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm_sec1
65
65
66
#if ENABLE(CSS_SCROLL_SNAP)
66
#if ENABLE(CSS_SCROLL_SNAP)
67
    if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::SnapOffsetsInfo))
67
    if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::SnapOffsetsInfo))
68
        m_scrollController.updateScrollSnapPoints(scrollingStateNode.snapOffsetsInfo().convertUnits<LayoutUnit>());
68
        m_scrollController.updateScrollSnapPoints(scrollingStateNode.snapOffsetsInfo().convertUnits<LayoutUnit, LayoutRect>());
69
69
70
    if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex))
70
    if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex))
71
        m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());
71
        m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());
- a/Source/WebCore/platform/ScrollController.cpp -4 / +6 lines
Lines 67-73 void ScrollController::updateScrollSnapState(const ScrollableArea& scrollableAre a/Source/WebCore/platform/ScrollController.cpp_sec1
67
    updateScrollSnapPoints(*snapOffsetInfo);
67
    updateScrollSnapPoints(*snapOffsetInfo);
68
}
68
}
69
69
70
void ScrollController::updateScrollSnapPoints(const ScrollSnapOffsetsInfo<LayoutUnit>& snapOffsetInfo)
70
void ScrollController::updateScrollSnapPoints(const LayoutScrollSnapOffsetsInfo& snapOffsetInfo)
71
{
71
{
72
    if (snapOffsetInfo.isEmpty()) {
72
    if (snapOffsetInfo.isEmpty()) {
73
        m_scrollSnapState = nullptr;
73
        m_scrollSnapState = nullptr;
Lines 115-122 void ScrollController::setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis a/Source/WebCore/platform/ScrollController.cpp_sec2
115
        return;
115
        return;
116
116
117
    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first().offset), snapOffsets.last().offset);
117
    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first().offset), snapOffsets.last().offset);
118
118
    LayoutSize viewportSize(m_client.viewportSize().width(), m_client.viewportSize().height());
119
    unsigned activeIndex = snapState.snapOffsetInfo().closestSnapOffset(axis, clampedOffset, 0).second;
119
    unsigned activeIndex = snapState.snapOffsetInfo().closestSnapOffset(axis, viewportSize, clampedOffset, 0).second;
120
    if (activeIndex == activeScrollSnapIndexForAxis(axis))
120
    if (activeIndex == activeScrollSnapIndexForAxis(axis))
121
        return;
121
        return;
122
122
Lines 137-143 float ScrollController::adjustScrollDestination(ScrollEventAxis axis, float dest a/Source/WebCore/platform/ScrollController.cpp_sec3
137
    Optional<LayoutUnit> originalOffsetInLayoutUnits;
137
    Optional<LayoutUnit> originalOffsetInLayoutUnits;
138
    if (originalOffset.hasValue())
138
    if (originalOffset.hasValue())
139
        originalOffsetInLayoutUnits = LayoutUnit(*originalOffset / m_client.pageScaleFactor());
139
        originalOffsetInLayoutUnits = LayoutUnit(*originalOffset / m_client.pageScaleFactor());
140
    LayoutUnit offset = snapState.snapOffsetInfo().closestSnapOffset(axis, LayoutUnit(destinationOffset / m_client.pageScaleFactor()), velocity, originalOffsetInLayoutUnits).first;
140
141
    LayoutSize viewportSize(m_client.viewportSize().width(), m_client.viewportSize().height());
142
    LayoutUnit offset = snapState.snapOffsetInfo().closestSnapOffset(axis, viewportSize, LayoutUnit(destinationOffset / m_client.pageScaleFactor()), velocity, originalOffsetInLayoutUnits).first;
141
    return offset * m_client.pageScaleFactor();
143
    return offset * m_client.pageScaleFactor();
142
}
144
}
143
145
- a/Source/WebCore/platform/ScrollController.h -1 / +1 lines
Lines 132-138 public: a/Source/WebCore/platform/ScrollController.h_sec1
132
    void scrollPositionChanged();
132
    void scrollPositionChanged();
133
133
134
#if ENABLE(CSS_SCROLL_SNAP)
134
#if ENABLE(CSS_SCROLL_SNAP)
135
    void updateScrollSnapPoints(const ScrollSnapOffsetsInfo<LayoutUnit>&);
135
    void updateScrollSnapPoints(const LayoutScrollSnapOffsetsInfo&);
136
    void setActiveScrollSnapIndexForAxis(ScrollEventAxis, unsigned);
136
    void setActiveScrollSnapIndexForAxis(ScrollEventAxis, unsigned);
137
    void setActiveScrollSnapIndicesForOffset(ScrollOffset);
137
    void setActiveScrollSnapIndicesForOffset(ScrollOffset);
138
    bool activeScrollSnapIndexDidChange() const { return m_activeScrollSnapIndexDidChange; }
138
    bool activeScrollSnapIndexDidChange() const { return m_activeScrollSnapIndexDidChange; }
- a/Source/WebCore/platform/ScrollSnapAnimatorState.cpp -9 / +5 lines
Lines 51-58 void ScrollSnapAnimatorState::setupAnimationForState(ScrollSnapState state, cons a/Source/WebCore/platform/ScrollSnapAnimatorState.cpp_sec1
51
51
52
    m_momentumCalculator = ScrollingMomentumCalculator::create(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
52
    m_momentumCalculator = ScrollingMomentumCalculator::create(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
53
    auto predictedScrollTarget = m_momentumCalculator->predictedDestinationOffset();
53
    auto predictedScrollTarget = m_momentumCalculator->predictedDestinationOffset();
54
    float targetOffsetX = targetOffsetForStartOffset(ScrollEventAxis::Horizontal, contentSize.width() - viewportSize.width(), initialOffset.x(), predictedScrollTarget.width(), pageScale, initialDelta.width(), m_activeSnapIndexX);
54
    float targetOffsetX = targetOffsetForStartOffset(ScrollEventAxis::Horizontal, viewportSize, contentSize.width() - viewportSize.width(), initialOffset.x(), predictedScrollTarget.width(), pageScale, initialDelta.width(), m_activeSnapIndexX);
55
    float targetOffsetY = targetOffsetForStartOffset(ScrollEventAxis::Vertical, contentSize.height() - viewportSize.height(), initialOffset.y(), predictedScrollTarget.height(), pageScale, initialDelta.height(), m_activeSnapIndexY);
55
    float targetOffsetY = targetOffsetForStartOffset(ScrollEventAxis::Vertical, viewportSize, contentSize.height() - viewportSize.height(), initialOffset.y(), predictedScrollTarget.height(), pageScale, initialDelta.height(), m_activeSnapIndexY);
56
    m_momentumCalculator->setRetargetedScrollOffset({ targetOffsetX, targetOffsetY });
56
    m_momentumCalculator->setRetargetedScrollOffset({ targetOffsetX, targetOffsetY });
57
    m_startTime = MonotonicTime::now();
57
    m_startTime = MonotonicTime::now();
58
    m_currentState = state;
58
    m_currentState = state;
Lines 91-100 FloatPoint ScrollSnapAnimatorState::currentAnimatedScrollOffset(bool& isAnimatio a/Source/WebCore/platform/ScrollSnapAnimatorState.cpp_sec2
91
    return m_momentumCalculator->scrollOffsetAfterElapsedTime(elapsedTime);
91
    return m_momentumCalculator->scrollOffsetAfterElapsedTime(elapsedTime);
92
}
92
}
93
93
94
float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const
94
float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, const FloatSize& viewportSize, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const
95
{
95
{
96
    LayoutUnit proposedOffset;
96
    LayoutUnit proposedOffset;
97
    std::tie(proposedOffset, outActiveSnapIndex) = m_snapOffsetsInfo.closestSnapOffset(axis, LayoutUnit(predictedOffset / pageScale), initialDelta);
97
    LayoutSize viewportLayoutSize(viewportSize.width(), viewportSize.height());
98
    std::tie(proposedOffset, outActiveSnapIndex) = m_snapOffsetsInfo.closestSnapOffset(axis, viewportLayoutSize, LayoutUnit(predictedOffset / pageScale), initialDelta);
98
    if (outActiveSnapIndex != invalidSnapOffsetIndex)
99
    if (outActiveSnapIndex != invalidSnapOffsetIndex)
99
        return pageScale * proposedOffset;
100
        return pageScale * proposedOffset;
100
    return clampTo<float>(startOffset, 0, maxScrollOffset);
101
    return clampTo<float>(startOffset, 0, maxScrollOffset);
Lines 105-115 TextStream& operator<<(TextStream& ts, const ScrollSnapAnimatorState& state) a/Source/WebCore/platform/ScrollSnapAnimatorState.cpp_sec3
105
    ts << "ScrollSnapAnimatorState";
106
    ts << "ScrollSnapAnimatorState";
106
    ts.dumpProperty("snap offsets x", state.snapOffsetsForAxis(ScrollEventAxis::Horizontal));
107
    ts.dumpProperty("snap offsets x", state.snapOffsetsForAxis(ScrollEventAxis::Horizontal));
107
    ts.dumpProperty("snap offsets y", state.snapOffsetsForAxis(ScrollEventAxis::Vertical));
108
    ts.dumpProperty("snap offsets y", state.snapOffsetsForAxis(ScrollEventAxis::Vertical));
108
    if (!state.snapOffsetRangesForAxis(ScrollEventAxis::Horizontal).isEmpty())
109
        ts.dumpProperty("snap offsets ranges x", state.snapOffsetRangesForAxis(ScrollEventAxis::Horizontal));
110
    if (!state.snapOffsetRangesForAxis(ScrollEventAxis::Vertical).isEmpty())
111
        ts.dumpProperty("snap offsets ranges y", state.snapOffsetRangesForAxis(ScrollEventAxis::Vertical));
112
113
    ts.dumpProperty("active snap index x", state.activeSnapIndexForAxis(ScrollEventAxis::Horizontal));
109
    ts.dumpProperty("active snap index x", state.activeSnapIndexForAxis(ScrollEventAxis::Horizontal));
114
    ts.dumpProperty("active snap index y", state.activeSnapIndexForAxis(ScrollEventAxis::Vertical));
110
    ts.dumpProperty("active snap index y", state.activeSnapIndexForAxis(ScrollEventAxis::Vertical));
115
111
- a/Source/WebCore/platform/ScrollSnapAnimatorState.h -20 / +4 lines
Lines 57-80 public: a/Source/WebCore/platform/ScrollSnapAnimatorState.h_sec1
57
        return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsInfo.horizontalSnapOffsets : m_snapOffsetsInfo.verticalSnapOffsets;
57
        return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsInfo.horizontalSnapOffsets : m_snapOffsetsInfo.verticalSnapOffsets;
58
    }
58
    }
59
59
60
    const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRangesForAxis(ScrollEventAxis axis) const
60
    const LayoutScrollSnapOffsetsInfo& snapOffsetInfo() const { return m_snapOffsetsInfo; }
61
    {
61
    void setSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo& newInfo) { m_snapOffsetsInfo = newInfo; }
62
        return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsInfo.horizontalSnapOffsetRanges : m_snapOffsetsInfo.verticalSnapOffsetRanges;
63
    }
64
65
    const ScrollSnapOffsetsInfo<LayoutUnit>& snapOffsetInfo() const { return m_snapOffsetsInfo; }
66
    void setSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>& newInfo) { m_snapOffsetsInfo = newInfo; }
67
68
    void setSnapOffsetsAndPositionRangesForAxis(ScrollEventAxis axis, const Vector<SnapOffset<LayoutUnit>>& snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRanges)
69
    {
70
        if (axis == ScrollEventAxis::Horizontal) {
71
            m_snapOffsetsInfo.horizontalSnapOffsets = snapOffsets;
72
            m_snapOffsetsInfo.horizontalSnapOffsetRanges = snapOffsetRanges;
73
        } else {
74
            m_snapOffsetsInfo.verticalSnapOffsets = snapOffsets;
75
            m_snapOffsetsInfo.verticalSnapOffsetRanges = snapOffsetRanges;
76
        }
77
    }
78
62
79
    ScrollSnapState currentState() const { return m_currentState; }
63
    ScrollSnapState currentState() const { return m_currentState; }
80
64
Lines 100-112 public: a/Source/WebCore/platform/ScrollSnapAnimatorState.h_sec2
100
    void transitionToDestinationReachedState();
84
    void transitionToDestinationReachedState();
101
85
102
private:
86
private:
103
    float targetOffsetForStartOffset(ScrollEventAxis, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const;
87
    float targetOffsetForStartOffset(ScrollEventAxis, const FloatSize& viewportSize, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const;
104
    void teardownAnimationForState(ScrollSnapState);
88
    void teardownAnimationForState(ScrollSnapState);
105
    void setupAnimationForState(ScrollSnapState, const FloatSize& contentSize, const FloatSize& viewportSize, float pageScale, const FloatPoint& initialOffset, const FloatSize& initialVelocity, const FloatSize& initialDelta);
89
    void setupAnimationForState(ScrollSnapState, const FloatSize& contentSize, const FloatSize& viewportSize, float pageScale, const FloatPoint& initialOffset, const FloatSize& initialVelocity, const FloatSize& initialDelta);
106
90
107
    ScrollSnapState m_currentState { ScrollSnapState::UserInteraction };
91
    ScrollSnapState m_currentState { ScrollSnapState::UserInteraction };
108
92
109
    ScrollSnapOffsetsInfo<LayoutUnit> m_snapOffsetsInfo;
93
    LayoutScrollSnapOffsetsInfo m_snapOffsetsInfo;
110
94
111
    unsigned m_activeSnapIndexX { 0 };
95
    unsigned m_activeSnapIndexX { 0 };
112
    unsigned m_activeSnapIndexY { 0 };
96
    unsigned m_activeSnapIndexY { 0 };
- a/Source/WebCore/platform/ScrollableArea.cpp -4 / +4 lines
Lines 479-497 String ScrollableArea::verticalScrollbarStateForTesting() const a/Source/WebCore/platform/ScrollableArea.cpp_sec1
479
}
479
}
480
480
481
#if ENABLE(CSS_SCROLL_SNAP)
481
#if ENABLE(CSS_SCROLL_SNAP)
482
ScrollSnapOffsetsInfo<LayoutUnit>& ScrollableArea::ensureSnapOffsetsInfo()
482
LayoutScrollSnapOffsetsInfo& ScrollableArea::ensureSnapOffsetsInfo()
483
{
483
{
484
    if (!m_snapOffsetsInfo)
484
    if (!m_snapOffsetsInfo)
485
        m_snapOffsetsInfo = makeUnique<ScrollSnapOffsetsInfo<LayoutUnit>>();
485
        m_snapOffsetsInfo = makeUnique<LayoutScrollSnapOffsetsInfo>();
486
    return *m_snapOffsetsInfo;
486
    return *m_snapOffsetsInfo;
487
}
487
}
488
488
489
const ScrollSnapOffsetsInfo<LayoutUnit>* ScrollableArea::snapOffsetInfo() const
489
const LayoutScrollSnapOffsetsInfo* ScrollableArea::snapOffsetInfo() const
490
{
490
{
491
    return m_snapOffsetsInfo.get();
491
    return m_snapOffsetsInfo.get();
492
}
492
}
493
493
494
void ScrollableArea::setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>& info)
494
void ScrollableArea::setScrollSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo& info)
495
{
495
{
496
    if (info.isEmpty()) {
496
    if (info.isEmpty()) {
497
        clearSnapOffsets();
497
        clearSnapOffsets();
- a/Source/WebCore/platform/ScrollableArea.h -4 / +4 lines
Lines 91-99 public: a/Source/WebCore/platform/ScrollableArea.h_sec1
91
    bool usesScrollSnap() const;
91
    bool usesScrollSnap() const;
92
92
93
#if ENABLE(CSS_SCROLL_SNAP)
93
#if ENABLE(CSS_SCROLL_SNAP)
94
    WEBCORE_EXPORT const ScrollSnapOffsetsInfo<LayoutUnit>* snapOffsetInfo() const;
94
    WEBCORE_EXPORT const LayoutScrollSnapOffsetsInfo* snapOffsetInfo() const;
95
    virtual void updateSnapOffsets() { };
95
    virtual void updateSnapOffsets() { };
96
    void setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>&);
96
    void setScrollSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo&);
97
    void clearSnapOffsets();
97
    void clearSnapOffsets();
98
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
98
    unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
99
    void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
99
    void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
Lines 385-392 private: a/Source/WebCore/platform/ScrollableArea.h_sec2
385
    mutable std::unique_ptr<ScrollAnimator> m_scrollAnimator;
385
    mutable std::unique_ptr<ScrollAnimator> m_scrollAnimator;
386
386
387
#if ENABLE(CSS_SCROLL_SNAP)
387
#if ENABLE(CSS_SCROLL_SNAP)
388
    ScrollSnapOffsetsInfo<LayoutUnit>& ensureSnapOffsetsInfo();
388
    LayoutScrollSnapOffsetsInfo& ensureSnapOffsetsInfo();
389
    std::unique_ptr<ScrollSnapOffsetsInfo<LayoutUnit>> m_snapOffsetsInfo;
389
    std::unique_ptr<LayoutScrollSnapOffsetsInfo> m_snapOffsetsInfo;
390
    unsigned m_currentHorizontalSnapPointIndex { 0 };
390
    unsigned m_currentHorizontalSnapPointIndex { 0 };
391
    unsigned m_currentVerticalSnapPointIndex { 0 };
391
    unsigned m_currentVerticalSnapPointIndex { 0 };
392
#endif
392
#endif
- a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm -1 / +1 lines
Lines 1278-1284 bool ScrollAnimatorMac::isPinnedForScrollDelta(const FloatSize& delta) const a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm_sec1
1278
}
1278
}
1279
1279
1280
#if ENABLE(CSS_SCROLL_SNAP)
1280
#if ENABLE(CSS_SCROLL_SNAP)
1281
static bool gestureShouldBeginSnap(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis, const ScrollSnapOffsetsInfo<LayoutUnit>* offsetInfo)
1281
static bool gestureShouldBeginSnap(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis, const LayoutScrollSnapOffsetsInfo* offsetInfo)
1282
{
1282
{
1283
    if (!offsetInfo)
1283
    if (!offsetInfo)
1284
        return false;
1284
        return false;
- a/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp -11 / +11 lines
Lines 98-106 template<> struct ArgumentCoder<RequestedScrollData> { a/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp_sec1
98
    static WARN_UNUSED_RETURN bool decode(Decoder&, RequestedScrollData&);
98
    static WARN_UNUSED_RETURN bool decode(Decoder&, RequestedScrollData&);
99
};
99
};
100
100
101
template<> struct ArgumentCoder<ScrollSnapOffsetsInfo<float>> {
101
template<> struct ArgumentCoder<FloatScrollSnapOffsetsInfo> {
102
    static void encode(Encoder&, const ScrollSnapOffsetsInfo<float>&);
102
    static void encode(Encoder&, const FloatScrollSnapOffsetsInfo&);
103
    static WARN_UNUSED_RETURN bool decode(Decoder&, ScrollSnapOffsetsInfo<float>&);
103
    static WARN_UNUSED_RETURN bool decode(Decoder&, FloatScrollSnapOffsetsInfo&);
104
};
104
};
105
105
106
template<> struct ArgumentCoder<SnapOffset<float>> {
106
template<> struct ArgumentCoder<SnapOffset<float>> {
Lines 311-317 bool ArgumentCoder<ScrollingStateScrollingNode>::decode(Decoder& decoder, Scroll a/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp_sec2
311
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollPosition, FloatPoint, setScrollPosition);
311
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollPosition, FloatPoint, setScrollPosition);
312
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollOrigin, IntPoint, setScrollOrigin);
312
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollOrigin, IntPoint, setScrollOrigin);
313
#if ENABLE(CSS_SCROLL_SNAP)
313
#if ENABLE(CSS_SCROLL_SNAP)
314
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::SnapOffsetsInfo, ScrollSnapOffsetsInfo<float>, setSnapOffsetsInfo);
314
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::SnapOffsetsInfo, FloatScrollSnapOffsetsInfo, setSnapOffsetsInfo);
315
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex, unsigned, setCurrentHorizontalSnapPointIndex);
315
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex, unsigned, setCurrentHorizontalSnapPointIndex);
316
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentVerticalSnapOffsetIndex, unsigned, setCurrentVerticalSnapPointIndex);
316
    SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentVerticalSnapOffsetIndex, unsigned, setCurrentVerticalSnapPointIndex);
317
#endif
317
#endif
Lines 532-537 void ArgumentCoder<SnapOffset<float>>::encode(Encoder& encoder, const SnapOffset a/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp_sec3
532
{
532
{
533
    encoder << offset.offset;
533
    encoder << offset.offset;
534
    encoder << offset.stop;
534
    encoder << offset.stop;
535
    encoder << offset.snapAreaIndex;
535
}
536
}
536
537
537
bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float>& offset)
538
bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float>& offset)
Lines 540-566 bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float a/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp_sec4
540
        return false;
541
        return false;
541
    if (!decoder.decode(offset.stop))
542
    if (!decoder.decode(offset.stop))
542
        return false;
543
        return false;
544
    if (!decoder.decode(offset.snapAreaIndex))
545
        return false;
543
    return true;
546
    return true;
544
}
547
}
545
548
546
549
547
void ArgumentCoder<ScrollSnapOffsetsInfo<float>>::encode(Encoder& encoder, const ScrollSnapOffsetsInfo<float>& info)
550
void ArgumentCoder<FloatScrollSnapOffsetsInfo>::encode(Encoder& encoder, const FloatScrollSnapOffsetsInfo& info)
548
{
551
{
549
    encoder << info.horizontalSnapOffsets;
552
    encoder << info.horizontalSnapOffsets;
550
    encoder << info.verticalSnapOffsets;
553
    encoder << info.verticalSnapOffsets;
551
    encoder << info.horizontalSnapOffsetRanges;
554
    encoder << info.snapAreas;
552
    encoder << info.verticalSnapOffsetRanges;
553
}
555
}
554
556
555
bool ArgumentCoder<ScrollSnapOffsetsInfo<float>>::decode(Decoder& decoder, ScrollSnapOffsetsInfo<float>& info)
557
bool ArgumentCoder<FloatScrollSnapOffsetsInfo>::decode(Decoder& decoder, FloatScrollSnapOffsetsInfo& info)
556
{
558
{
557
    if (!decoder.decode(info.horizontalSnapOffsets))
559
    if (!decoder.decode(info.horizontalSnapOffsets))
558
        return false;
560
        return false;
559
    if (!decoder.decode(info.verticalSnapOffsets))
561
    if (!decoder.decode(info.verticalSnapOffsets))
560
        return false;
562
        return false;
561
    if (!decoder.decode(info.horizontalSnapOffsetRanges))
563
    if (!decoder.decode(info.snapAreas))
562
        return false;
563
    if (!decoder.decode(info.verticalSnapOffsetRanges))
564
        return false;
564
        return false;
565
    return true;
565
    return true;
566
}
566
}
- a/Source/WebKit/Shared/WebCoreArgumentCoders.cpp -26 lines
Lines 2805-2836 bool ArgumentCoder<ServiceWorkerOrClientIdentifier>::decode(Decoder& decoder, Se a/Source/WebKit/Shared/WebCoreArgumentCoders.cpp_sec1
2805
}
2805
}
2806
#endif
2806
#endif
2807
2807
2808
#if ENABLE(CSS_SCROLL_SNAP)
2809
2810
void ArgumentCoder<ScrollOffsetRange<float>>::encode(Encoder& encoder, const ScrollOffsetRange<float>& range)
2811
{
2812
    encoder << range.start;
2813
    encoder << range.end;
2814
}
2815
2816
auto ArgumentCoder<ScrollOffsetRange<float>>::decode(Decoder& decoder) -> Optional<WebCore::ScrollOffsetRange<float>>
2817
{
2818
    WebCore::ScrollOffsetRange<float> range;
2819
    float start;
2820
    if (!decoder.decode(start))
2821
        return WTF::nullopt;
2822
2823
    float end;
2824
    if (!decoder.decode(end))
2825
        return WTF::nullopt;
2826
2827
    range.start = start;
2828
    range.end = end;
2829
    return range;
2830
}
2831
2832
#endif
2833
2834
void ArgumentCoder<MediaSelectionOption>::encode(Encoder& encoder, const MediaSelectionOption& option)
2808
void ArgumentCoder<MediaSelectionOption>::encode(Encoder& encoder, const MediaSelectionOption& option)
2835
{
2809
{
2836
    encoder << option.displayName;
2810
    encoder << option.displayName;
- a/Source/WebKit/Shared/WebCoreArgumentCoders.h -9 lines
Lines 743-757 template<> struct ArgumentCoder<WebCore::ServiceWorkerOrClientIdentifier> { a/Source/WebKit/Shared/WebCoreArgumentCoders.h_sec1
743
743
744
#endif
744
#endif
745
745
746
#if ENABLE(CSS_SCROLL_SNAP)
747
748
template<> struct ArgumentCoder<WebCore::ScrollOffsetRange<float>> {
749
    static void encode(Encoder&, const WebCore::ScrollOffsetRange<float>&);
750
    static Optional<WebCore::ScrollOffsetRange<float>> decode(Decoder&);
751
};
752
753
#endif
754
755
template<> struct ArgumentCoder<WebCore::MediaSelectionOption> {
746
template<> struct ArgumentCoder<WebCore::MediaSelectionOption> {
756
    static void encode(Encoder&, const WebCore::MediaSelectionOption&);
747
    static void encode(Encoder&, const WebCore::MediaSelectionOption&);
757
    static Optional<WebCore::MediaSelectionOption> decode(Decoder&);
748
    static Optional<WebCore::MediaSelectionOption> decode(Decoder&);
- a/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm -4 / +2 lines
Lines 223-236 bool RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling(ScrollEven a/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm_sec1
223
223
224
float RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity, unsigned& currentIndex) const
224
float RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity, unsigned& currentIndex) const
225
{
225
{
226
    ScrollingTreeNode* root = m_scrollingTree->rootNode();
226
    ScrollingTreeFrameScrollingNode* rootScrollingNode = m_scrollingTree->rootNode();
227
    ASSERT(root && root->isFrameScrollingNode());
228
    ScrollingTreeFrameScrollingNode* rootScrollingNode = static_cast<ScrollingTreeFrameScrollingNode*>(root);
229
    const auto& snapOffsetsInfo = rootScrollingNode->snapOffsetsInfo();
227
    const auto& snapOffsetsInfo = rootScrollingNode->snapOffsetsInfo();
230
228
231
    float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
229
    float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
232
    float rawClosestSnapOffset;
230
    float rawClosestSnapOffset;
233
    std::tie(rawClosestSnapOffset, currentIndex) = snapOffsetsInfo.closestSnapOffset(axis, scaledScrollDestination, velocity);
231
    std::tie(rawClosestSnapOffset, currentIndex) = snapOffsetsInfo.closestSnapOffset(axis, rootScrollingNode->layoutViewport().size(), scaledScrollDestination, velocity);
234
    return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
232
    return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
235
}
233
}
236
234
- a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm -2 / +3 lines
Lines 106-121 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoi a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm_sec1
106
    unsigned originalHorizontalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentHorizontalSnapPointIndex();
106
    unsigned originalHorizontalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentHorizontalSnapPointIndex();
107
    unsigned originalVerticalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex();
107
    unsigned originalVerticalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex();
108
108
109
    WebCore::FloatSize viewportSize(static_cast<float>(CGRectGetWidth([scrollView bounds])), static_cast<float>(CGRectGetHeight([scrollView bounds])));
109
    const auto& snapOffsetsInfo = _scrollingTreeNodeDelegate->scrollingNode().snapOffsetsInfo();
110
    const auto& snapOffsetsInfo = _scrollingTreeNodeDelegate->scrollingNode().snapOffsetsInfo();
110
    if (!snapOffsetsInfo.horizontalSnapOffsets.isEmpty()) {
111
    if (!snapOffsetsInfo.horizontalSnapOffsets.isEmpty()) {
111
        auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Horizontal, horizontalTarget, velocity.x);
112
        auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Horizontal, viewportSize, horizontalTarget, velocity.x);
112
        _scrollingTreeNodeDelegate->scrollingNode().setCurrentHorizontalSnapPointIndex(index);
113
        _scrollingTreeNodeDelegate->scrollingNode().setCurrentHorizontalSnapPointIndex(index);
113
        if (horizontalTarget >= 0 && horizontalTarget <= scrollView.contentSize.width)
114
        if (horizontalTarget >= 0 && horizontalTarget <= scrollView.contentSize.width)
114
            targetContentOffset->x = potentialSnapPosition;
115
            targetContentOffset->x = potentialSnapPosition;
115
    }
116
    }
116
117
117
    if (!snapOffsetsInfo.verticalSnapOffsets.isEmpty()) {
118
    if (!snapOffsetsInfo.verticalSnapOffsets.isEmpty()) {
118
        auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Vertical, verticalTarget, velocity.x);
119
        auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Vertical, viewportSize, verticalTarget, velocity.x);
119
        _scrollingTreeNodeDelegate->scrollingNode().setCurrentVerticalSnapPointIndex(index);
120
        _scrollingTreeNodeDelegate->scrollingNode().setCurrentVerticalSnapPointIndex(index);
120
        if (verticalTarget >= 0 && verticalTarget <= scrollView.contentSize.height)
121
        if (verticalTarget >= 0 && verticalTarget <= scrollView.contentSize.height)
121
            targetContentOffset->y = potentialSnapPosition;
122
            targetContentOffset->y = potentialSnapPosition;
- a/LayoutTests/ChangeLog +13 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2021-03-15  Martin Robinson  <mrobinson@webkit.org>
2
3
        [css-scroll-snap] Properly handle overflowing snap areas
4
        https://bugs.webkit.org/show_bug.cgi?id=223175
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        * css3/scroll-snap/scroll-snap-click-scrollbar-gutter.html: This test used snap areas that
9
        overflow the snapport, thus meant that it relied on non-spec compliant behavior. Rework it so
10
        that the snap areas do not overflow, maintaining the existing behavior.
11
        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html: Ditto.
12
        * tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html: Ditto.
13
1
2021-03-14  Yusuke Suzuki  <ysuzuki@apple.com>
14
2021-03-14  Yusuke Suzuki  <ysuzuki@apple.com>
2
15
3
        Prevent dynamic import in service worker
16
        Prevent dynamic import in service worker
- a/LayoutTests/imported/w3c/ChangeLog +10 lines
Lines 1-3 a/LayoutTests/imported/w3c/ChangeLog_sec1
1
2021-03-15  Martin Robinson  <mrobinson@webkit.org>
2
3
        [css-scroll-snap] Properly handle overflowing snap areas
4
        https://bugs.webkit.org/show_bug.cgi?id=223175
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        * web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt: Update expectations to
9
        show that this test is now passing.
10
1
2021-03-10  Sergio Villar Senin  <svillar@igalia.com>
11
2021-03-10  Sergio Villar Senin  <svillar@igalia.com>
2
12
3
        Do not shrink radio buttons bellow its size
13
        Do not shrink radio buttons bellow its size
- a/LayoutTests/css3/scroll-snap/scroll-snap-click-scrollbar-gutter.html -13 / +28 lines
Lines 34-54 a/LayoutTests/css3/scroll-snap/scroll-snap-click-scrollbar-gutter.html_sec1
34
                scroll-snap-type: both mandatory;
34
                scroll-snap-type: both mandatory;
35
            }
35
            }
36
36
37
            .block {
38
                height: 1000px;
39
                width: 250px;
40
                scroll-snap-align: start;
41
            }
42
43
            .horizontal-drawer {
37
            .horizontal-drawer {
44
                height: 100%;
38
                height: 100%;
45
                width: 2000px;
39
                width: 2000px;
46
            }
40
            }
47
41
48
            .horizontal-drawer .block {
42
            .horizontal-drawer > div {
49
                width: 1000px;
43
                float: left;
50
                height: 250px;
44
                width: 990px;
45
                height: 100%;
46
            }
47
48
            .horizontal-drawer > div.snap-point {
49
                scroll-snap-align: start;
50
                width: 10px;
51
             }
52
53
            #vertical-container > div {
54
                width: 100%;
55
                height: 990px;
56
            }
57
58
            #vertical-container > div.snap-point {
59
                scroll-snap-align: start;
60
                height: 10px;
51
            }
61
            }
62
52
        </style>
63
        </style>
53
        <script src="../../resources/js-test.js"></script>
64
        <script src="../../resources/js-test.js"></script>
54
        <script src="../../resources/ui-helper.js"></script>
65
        <script src="../../resources/ui-helper.js"></script>
Lines 87-99 a/LayoutTests/css3/scroll-snap/scroll-snap-click-scrollbar-gutter.html_sec2
87
    <body onload="onLoad();">
98
    <body onload="onLoad();">
88
        <div id="horizontal-container" class="container">
99
        <div id="horizontal-container" class="container">
89
            <div class="horizontal-drawer">
100
            <div class="horizontal-drawer">
90
                <div class="block" style="float: left; background: #80475E"></div>
101
                <div class="snap-point" style="background: #80475E"></div>
91
                <div class="block" style="width: 450px; float: left; background: #CC5A71"></div>
102
                <div></div>
103
                <div class="snap-point" style="background: #CC5A71"></div>
104
                <div></div>
92
            </div>
105
            </div>
93
        </div>
106
        </div>
94
        <div id="vertical-container" class="container">
107
        <div id="vertical-container" class="container">
95
            <div class="block" style="background: #80475E"></div>
108
            <div class="snap-point" style="background: #80475E"></div>
96
            <div class="block" style="height: 450px; background: #CC5A71"></div>
109
            <div></div>
110
            <div class="snap-point" style="background: #CC5A71"></div>
111
            <div></div>
97
        </div>
112
        </div>
98
        <p id="console"></p>
113
        <p id="console"></p>
99
        <script>
114
        <script>
- a/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt -8 / +8 lines
Lines 1-13 a/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt_sec1
1
1
2
PASS Snaps to the snap position if the snap area doesn't cover the snapport on x.
2
PASS Snaps to the snap position if the snap area doesn't cover the snapport on x.
3
FAIL Snaps to the snap position if the snap area covers the snapport on x on the right border. assert_equals: expected 2715 but got 200
3
PASS Snaps to the snap position if the snap area covers the snapport on x on the right border.
4
FAIL Snaps to the snap position if the snap area covers the snapport on x on the left border. assert_equals: expected 200 but got 2715
4
PASS Snaps to the snap position if the snap area covers the snapport on x on the left border.
5
PASS Snaps if the distance between the previous(400) and next(800) snap positions is smaller than snapport(500) on x.
5
PASS Snaps if the distance between the previous(400) and next(800) snap positions is smaller than snapport(500) on x.
6
PASS Snaps if the distance between the previous(400) and next(800) snap positions is smaller than snapport(500) on y.
6
PASS Snaps if the distance between the previous(400) and next(800) snap positions is smaller than snapport(500) on y.
7
FAIL Snap to current scroll position which is a valid snap position, as the snap area covers snapport on x and the distance between the previous(800) and next(1400) is larger than snapport(500). assert_equals: expected 900 but got 800
7
PASS Snap to current scroll position which is a valid snap position, as the snap area covers snapport on x and the distance between the previous(800) and next(1400) is larger than snapport(500).
8
FAIL Snap to current scroll position which is a valid snap position, as the snap area covers snapport on y and the distance between the previous(800) and next(1400) is larger than snapport(500). assert_equals: expected 900 but got 800
8
PASS Snap to current scroll position which is a valid snap position, as the snap area covers snapport on y and the distance between the previous(800) and next(1400) is larger than snapport(500).
9
FAIL Snap to current scroll position which is a valid snap position, as the snap area covers snapport on x and there is no subsequent snap positions. assert_equals: expected 1500 but got 1400
9
PASS Snap to current scroll position which is a valid snap position, as the snap area covers snapport on x and there is no subsequent snap positions.
10
FAIL Snap to current scroll position which is a valid snap position, as the snap area covers snapport on y and there is no subsequent snap positions. assert_equals: expected 1500 but got 1400
10
PASS Snap to current scroll position which is a valid snap position, as the snap area covers snapport on y and there is no subsequent snap positions.
11
FAIL Don't snap back even if scrollTo tries to scroll to positions which are outside of the scroll range and if a snap target element covers the snaport assert_equals: expected 3715 but got 2200
11
PASS Don't snap back even if scrollTo tries to scroll to positions which are outside of the scroll range and if a snap target element covers the snaport
12
FAIL Snap to current scroll position on x as the area is covering x axis.However, we snap to the specified snap position on y as the area is not covering y axis. assert_equals: expected 10 but got 0
12
PASS Snap to current scroll position on x as the area is covering x axis.However, we snap to the specified snap position on y as the area is not covering y axis.
13
13
- a/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html -1 / +2 lines
Lines 4-9 a/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html_sec1
4
        <style>
4
        <style>
5
            html {
5
            html {
6
                scroll-snap-type: y mandatory;
6
                scroll-snap-type: y mandatory;
7
                margin: 0;
7
            }
8
            }
8
            .verticalGallery {
9
            .verticalGallery {
9
                width: 100vw;
10
                width: 100vw;
Lines 12-18 a/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html_sec2
12
                padding: 0;
13
                padding: 0;
13
            }
14
            }
14
            .colorBox {
15
            .colorBox {
15
                height: 100vh;
16
                height: 90vh;
16
                width: 100vw;
17
                width: 100vw;
17
                float: left;
18
                float: left;
18
                scroll-snap-align: start;
19
                scroll-snap-align: start;
- a/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html -7 / +2 lines
Lines 13-33 a/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html_sec1
13
                position: absolute;
13
                position: absolute;
14
                top: 0;
14
                top: 0;
15
                left: 0;
15
                left: 0;
16
                overflow-x: none;
17
                overflow-y: scroll;
16
                overflow-y: scroll;
18
                scroll-snap-type: y proximity;
17
                scroll-snap-type: y proximity;
19
                -webkit-scroll-snap-type: proximity;
20
                scroll-snap-type: proximity;
21
                opacity: 0.5;
18
                opacity: 0.5;
22
            }
19
            }
23
20
24
            .area {
21
            .area {
25
                height: 600px;
22
                height: 100%;
26
                width: 600px;
23
                width: 100%;
27
                float: left;
24
                float: left;
28
                scroll-snap-align: start;
25
                scroll-snap-align: start;
29
                -webkit-scroll-snap-coordinate: 0 0;
30
                scroll-snap-coordinate: 0 0;
31
            }
26
            }
32
        </style>
27
        </style>
33
        <script>
28
        <script>

Return to Bug 223175