Changeset 280527 in webkit


Ignore:
Timestamp:
Aug 2, 2021 1:54:16 AM (12 months ago)
Author:
Martin Robinson
Message:

[css-scroll-snap] Consider all snap areas at a given snap offset when snapping
https://bugs.webkit.org/show_bug.cgi?id=228141

LayoutTests/imported/w3c:

Reviewed by Frédéric Wang.

  • web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt:
  • web-platform-tests/css/css-scroll-snap/overflowing-snap-areas.html:

Source/WebCore:

Reviewed by Frédéric Wang.

When deciding whether to snap to snap areas that overflow the snap port, don't
just consider the first snap area at a given snap offset. Instead, keep information
about all snap areas in the ScrolSnapOffsetInfo. In order to avoid iterating over
arrays of hundreds of rectangles, also record whether any of them are larger than
the viewport. This will avoid extra work on the most common usecase of large sets
of snap areas (gridded table layouts).

This change is tested by extending an existing WPT test.

  • page/scrolling/ScrollSnapOffsetsInfo.cpp:

(WebCore::rangeForAxis): Added this helper.
(WebCore::searchForPotentialSnapPoints): When deciding if a particular snap area
overflows the snap port, look at all available snap areas instead of just the
first one.
(WebCore::updateSnapOffsetsForScrollableArea): Instead of only recording the first
snap area, record all of them in the list of snap area rectangles.
(WebCore::convertOffsetInfo): Convert the list of snap ares.

  • page/scrolling/ScrollSnapOffsetsInfo.h:

(WebCore::operator==): Added this operator which is necessary now that snapAreaIndices
is a vector.

Source/WebKit:

Reviewed by Frédéric Wang.

  • Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp:

(ArgumentCoder<SnapOffset<float>>::encode): Serialize new members.
(ArgumentCoder<SnapOffset<float>>::decode): Deserialize new members.

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r280511 r280527  
     12021-08-02  Martin Robinson  <mrobinson@webkit.org>
     2
     3        [css-scroll-snap] Consider all snap areas at a given snap offset when snapping
     4        https://bugs.webkit.org/show_bug.cgi?id=228141
     5
     6        Reviewed by Frédéric Wang.
     7
     8        * web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt:
     9        * web-platform-tests/css/css-scroll-snap/overflowing-snap-areas.html:
     10
    1112021-07-31  Commit Queue  <commit-queue@webkit.org>
    212
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt

    r279364 r280527  
    1111PASS 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
    1212PASS 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.
     13PASS snap to current scroll position on y as the area is covering y axis, even though that area is not the only scroll area at the same position.
     14PASS snap to current scroll position on x as the area is covering x axis, even though that area is not the only scroll area at the same position.
    1315
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas.html

    r268250 r280527  
    2525.target {
    2626  scroll-snap-align: start;
     27  height: 400px;
     28  width: 400px;
    2729}
    2830.large-x {
    2931  width: 3000px;
    30   height: 400px;
    3132  background-color: yellow;
    3233}
    3334.large-y {
    34   width: 400px;
    3535  height: 2000px;
    36   background-color: green;
     36  background-color: yellow;
    3737}
    3838.small {
     
    7373</div>
    7474
     75<div class="scroller-x" id="overlapping-overflow" style="scroll-snap-type: both mandatory">
     76  <div class="space"></div>
     77  <div style="left: 200px; top: 200px;">
     78    <div class="target small"></div>
     79    <div class="target small"></div>
     80    <div class="target small"></div>
     81    <div class="target large-y large-x"></div>
     82    <div class="target small"></div>
     83    <div class="target small"></div>
     84    <div class="target small"></div>
     85  </div>
     86</div>
     87
    7588<script>
    7689var one_target_scroller = document.getElementById("one-target");
     
    7891var scroller_y = document.getElementById("y");
    7992var two_axes_scroller = document.getElementById("two-axes");
     93var overlapping_scroller = document.getElementById("overlapping-overflow");
    8094
    8195test(() => {
     
    166180   "However, we snap to the specified snap position on y as the area is not " +
    167181   "covering y axis.");
     182
     183test(() => {
     184  overlapping_scroller.scrollTo(200, 800);
     185  assert_equals(overlapping_scroller.scrollLeft, 200);
     186  assert_equals(overlapping_scroller.scrollTop, 800);
     187}, "snap to current scroll position on y as the area is covering y axis, " +
     188   "even though that area is not the only scroll area at the same position.");
     189
     190test(() => {
     191  overlapping_scroller.scrollTo(800, 200);
     192  assert_equals(overlapping_scroller.scrollLeft, 800);
     193  assert_equals(overlapping_scroller.scrollTop, 200);
     194}, "snap to current scroll position on x as the area is covering x axis, " +
     195   "even though that area is not the only scroll area at the same position.");
    168196</script>
     197</script>
  • trunk/Source/WebCore/ChangeLog

    r280525 r280527  
     12021-08-02  Martin Robinson  <mrobinson@webkit.org>
     2
     3        [css-scroll-snap] Consider all snap areas at a given snap offset when snapping
     4        https://bugs.webkit.org/show_bug.cgi?id=228141
     5
     6        Reviewed by Frédéric Wang.
     7
     8        When deciding whether to snap to snap areas that overflow the snap port, don't
     9        just consider the first snap area at a given snap offset. Instead, keep information
     10        about all snap areas in the ScrolSnapOffsetInfo. In order to avoid iterating over
     11        arrays of hundreds of rectangles, also record whether any of them are larger than
     12        the viewport. This will avoid extra work on the most common usecase of large sets
     13        of snap areas (gridded table layouts).
     14
     15        This change is tested by extending an existing WPT test.
     16
     17        * page/scrolling/ScrollSnapOffsetsInfo.cpp:
     18        (WebCore::rangeForAxis): Added this helper.
     19        (WebCore::searchForPotentialSnapPoints): When deciding if a particular snap area
     20        overflows the snap port, look at all available snap areas instead of just the
     21        first one.
     22        (WebCore::updateSnapOffsetsForScrollableArea): Instead of only recording the first
     23        snap area, record all of them in the list of snap area rectangles.
     24        (WebCore::convertOffsetInfo): Convert the list of snap ares.
     25        * page/scrolling/ScrollSnapOffsetsInfo.h:
     26        (WebCore::operator==): Added this operator which is necessary now that snapAreaIndices
     27        is a vector.
     28
    1292021-08-01  Rob Buis  <rbuis@igalia.com>
    230
  • trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp

    r280171 r280527  
    4141namespace WebCore {
    4242
     43template <typename UnitType, typename RectType>
     44static std::pair<UnitType, UnitType> rangeForAxis(RectType rect, ScrollEventAxis axis)
     45{
     46    return axis == ScrollEventAxis::Horizontal ? std::make_pair(rect.x(), rect.maxX()) : std::make_pair(rect.y(), rect.maxY());
     47}
     48
    4349template <typename UnitType>
    4450struct PotentialSnapPointSearchResult {
     
    6874
    6975    for (unsigned i = 0; i < snapOffsets.size(); i++) {
     76        if (!landedInsideSnapAreaThatConsumesViewport && snapOffsets[i].hasSnapAreaLargerThanViewport) {
     77            for (auto snapAreaIndices : snapOffsets[i].snapAreaIndices) {
     78                auto [snapAreaMin, snapAreaMax] = rangeForAxis<UnitType>(info.snapAreas[snapAreaIndices], axis);
     79                if (snapAreaMin <= destinationOffset && snapAreaMax >= (destinationOffset + viewportLength)) {
     80                    landedInsideSnapAreaThatConsumesViewport = true;
     81                    break;
     82                }
     83            }
     84        }
     85
    7086        UnitType potentialSnapOffset = snapOffsets[i].offset;
    71 
    72         const auto& snapArea = info.snapAreas[snapOffsets[i].snapAreaIndex];
    73         auto snapAreaMin = axis == ScrollEventAxis::Horizontal ? snapArea.x() : snapArea.y();
    74         auto snapAreaMax = axis == ScrollEventAxis::Horizontal ? snapArea.maxX() : snapArea.maxY();
    75         landedInsideSnapAreaThatConsumesViewport |= snapAreaMin <= destinationOffset && snapAreaMax >= (destinationOffset + viewportLength);
    76 
    7787        if (potentialSnapOffset == destinationOffset)
    7888            exact = std::make_pair(potentialSnapOffset, i);
     
    220230    }
    221231
    222     auto addOrUpdateStopForSnapOffset = [](HashMap<float, SnapOffset<LayoutUnit>>& offsets, SnapOffset<LayoutUnit> newOffset)
     232    auto addOrUpdateStopForSnapOffset = [](HashMap<float, SnapOffset<LayoutUnit>>& offsets, LayoutUnit newOffset, ScrollSnapStop stop, bool hasSnapAreaLargerThanViewport, size_t snapAreaIndices)
    223233    {
     234        auto offset = offsets.ensure(newOffset, [&] {
     235            return SnapOffset<LayoutUnit> { newOffset, stop, hasSnapAreaLargerThanViewport, { } };
     236        });
     237
    224238        // If the offset already exists, we ensure that it has ScrollSnapStop::Always, when appropriate.
    225         auto addResult = offsets.add(newOffset.offset, newOffset);
    226         if (newOffset.stop == ScrollSnapStop::Always)
    227             addResult.iterator->value.stop = ScrollSnapStop::Always;
     239        if (stop == ScrollSnapStop::Always)
     240            offset.iterator->value.stop = ScrollSnapStop::Always;
     241
     242        offset.iterator->value.hasSnapAreaLargerThanViewport |= hasSnapAreaLargerThanViewport;
     243        offset.iterator->value.snapAreaIndices.append(snapAreaIndices);
    228244    };
    229245
     
    294310            auto absoluteScrollXPosition = computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.maxX(), xAlign, areaXAxisFlipped) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.maxX(), xAlign, areaXAxisFlipped);
    295311            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ roundToInt(absoluteScrollXPosition), 0 }).x(), 0, maxScrollOffset.x());
    296             addOrUpdateStopForSnapOffset(horizontalSnapOffsetsMap, { absoluteScrollOffset, stop, snapAreas.size() - 1 });
     312            addOrUpdateStopForSnapOffset(horizontalSnapOffsetsMap, absoluteScrollOffset, stop, scrollSnapAreaAsOffsets.width() > scrollSnapPort.width(), snapAreas.size() - 1);
    297313        }
    298314        if (snapsVertically) {
    299315            auto absoluteScrollYPosition = computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.maxY(), yAlign, areaYAxisFlipped) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.maxY(), yAlign, areaYAxisFlipped);
    300316            auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ 0, roundToInt(absoluteScrollYPosition) }).y(), 0, maxScrollOffset.y());
    301             addOrUpdateStopForSnapOffset(verticalSnapOffsetsMap, { absoluteScrollOffset, stop, snapAreas.size() - 1 });
     317            addOrUpdateStopForSnapOffset(verticalSnapOffsetsMap, absoluteScrollOffset, stop, scrollSnapAreaAsOffsets.height() > scrollSnapPort.height(), snapAreas.size() - 1);
    302318        }
    303319
     
    349365        output.reserveInitialCapacity(input.size());
    350366        for (auto& offset : input)
    351             output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop, offset.snapAreaIndex });
     367            output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop, offset.hasSnapAreaLargerThanViewport, offset.snapAreaIndices });
    352368        return output;
    353369    };
  • trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h

    r280171 r280527  
    4444    T offset;
    4545    ScrollSnapStop stop;
    46     size_t snapAreaIndex;
     46    bool hasSnapAreaLargerThanViewport;
     47    Vector<size_t> snapAreaIndices;
    4748};
    4849
     
    7576};
    7677
     78template<typename UnitType> inline bool operator==(const SnapOffset<UnitType>& a, const SnapOffset<UnitType>& b)
     79{
     80    return a.offset == b.offset && a.stop == b.stop && a.snapAreaIndices == b.snapAreaIndices;
     81}
     82
    7783using LayoutScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<LayoutUnit, LayoutRect>;
    7884using FloatScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<float, FloatRect>;
  • trunk/Source/WebKit/ChangeLog

    r280523 r280527  
     12021-08-02  Martin Robinson  <mrobinson@igalia.com>
     2
     3        [css-scroll-snap] Consider all snap areas at a given snap offset when snapping
     4        https://bugs.webkit.org/show_bug.cgi?id=228141
     5
     6        Reviewed by Frédéric Wang.
     7
     8        * Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp:
     9        (ArgumentCoder<SnapOffset<float>>::encode): Serialize new members.
     10        (ArgumentCoder<SnapOffset<float>>::decode): Deserialize new members.
     11
    1122021-08-01  Youenn Fablet  <youenn@apple.com>
    213
  • trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp

    r279218 r280527  
    528528    encoder << offset.offset;
    529529    encoder << offset.stop;
    530     encoder << offset.snapAreaIndex;
     530    encoder << offset.hasSnapAreaLargerThanViewport;
     531    encoder << offset.snapAreaIndices;
    531532}
    532533
     
    537538    if (!decoder.decode(offset.stop))
    538539        return false;
    539     if (!decoder.decode(offset.snapAreaIndex))
     540    if (!decoder.decode(offset.hasSnapAreaLargerThanViewport))
     541        return false;
     542    if (!decoder.decode(offset.snapAreaIndices))
    540543        return false;
    541544    return true;
Note: See TracChangeset for help on using the changeset viewer.