Source/WebCore/ChangeLog

 12021-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
1682021-03-10 Sergio Villar Senin <svillar@igalia.com>
269
370 Do not shrink radio buttons bellow its size

Source/WebKit/ChangeLog

 12021-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
1242021-03-15 Kimmo Kinnunen <kkinnunen@apple.com>
225
326 RemoteRenderingBackend commands are not processed in order if GPU process is waiting for replies

Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp

@@void AsyncScrollingCoordinator::handleWheelEventPhase(ScrollingNodeID nodeID, Pl
9292#endif
9393
9494#if ENABLE(CSS_SCROLL_SNAP)
95 static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, const ScrollSnapOffsetsInfo<LayoutUnit>* offsetInfo, float deviceScaleFactor)
 95static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, const LayoutScrollSnapOffsetsInfo* offsetInfo, float deviceScaleFactor)
9696{
9797 if (!offsetInfo) {
98  node.setSnapOffsetsInfo(ScrollSnapOffsetsInfo<float>());
 98 node.setSnapOffsetsInfo(FloatScrollSnapOffsetsInfo());
9999 return;
100100 }
101101
102102 // 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));
104104}
105105#endif
106106

Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp

4141
4242namespace WebCore {
4343
44 template <typename LayoutType>
45 static void indicesOfNearestSnapOffsetRanges(LayoutType offset, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, unsigned& lowerIndex, unsigned& upperIndex)
 44template <typename UnitType>
 45static Optional<std::pair<UnitType, unsigned>> findFirstSnapStopOffsetBetweenOriginAndDestination(const Vector<SnapOffset<UnitType>>& snapOffsets, UnitType scrollOriginOffset, UnitType scrollDestinationOffset)
4646{
47  if (snapOffsetRanges.isEmpty()) {
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;
 47 UnitType difference = scrollDestinationOffset - scrollOriginOffset;
10648 if (!difference)
10749 return WTF::nullopt;
10850

@@static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(con
11355 iteration = -1;
11456 }
11557
116  auto isPast = [difference](LayoutType mark, LayoutType candidate) {
 58 auto isPast = [difference](UnitType mark, UnitType candidate) {
11759 return (difference > 0 && candidate > mark) || (difference < 0 && candidate < mark);
11860 };
11961

@@static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(con
12466 if (snapOffsets[i].stop != ScrollSnapStop::Always)
12567 continue;
12668 if (isPast(scrollOriginOffset, offset))
127  return i;
 69 return std::make_pair(offset, static_cast<unsigned>(i));
12870 }
12971
13072 return WTF::nullopt;
13173}
13274
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 
18675enum class InsetOrOutset {
18776 Inset,
18877 Outset

@@static LayoutUnit computeScrollSnapAlignOffset(LayoutUnit minLocation, LayoutUni
219108 }
220109}
221110
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 
246111void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates)
247112{
248113 auto scrollSnapType = scrollingElementStyle.scrollSnapType();

@@void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re
262127
263128 HashMap<float, SnapOffset<LayoutUnit>> verticalSnapOffsetsMap;
264129 HashMap<float, SnapOffset<LayoutUnit>> horizontalSnapOffsetsMap;
 130 Vector<LayoutRect> snapAreas;
265131 bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline;
266132 bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block;
267133

@@void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re
288154
289155 scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollMargin(), InsetOrOutset::Outset);
290156 LOG_WITH_STREAM(ScrollSnap, stream << " Considering scroll snap target area " << scrollSnapArea);
 157
291158 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
292169 auto stop = child->style().scrollSnapStop();
293  if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) {
 170 if (snapsHorizontally) {
294171 auto absoluteScrollXPosition = computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.maxX(), alignment.x, scrollerIsRTL) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.maxX(), alignment.x, scrollerIsRTL);
295172 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 });
297174 }
298  if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) {
 175 if (snapsVertically) {
299176 auto absoluteScrollYPosition = computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.maxY(), alignment.y, false) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.maxY(), alignment.y, false);
300177 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 });
302179 }
303180 }
304181

@@void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, const Re
308185 };
309186
310187 Vector<SnapOffset<LayoutUnit>> horizontalSnapOffsets = copyToVector(horizontalSnapOffsetsMap.values());
311  Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges;
312188 if (!horizontalSnapOffsets.isEmpty()) {
313189 std::sort(horizontalSnapOffsets.begin(), horizontalSnapOffsets.end(), compareSnapOffsets);
314  if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
315  computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width());
316 
317190 LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offsets: " << horizontalSnapOffsets);
318  LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offset ranges: " << horizontalSnapOffsetRanges);
319191 }
320192
321193 Vector<SnapOffset<LayoutUnit>> verticalSnapOffsets = copyToVector(verticalSnapOffsetsMap.values());
322  Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges;
323194 if (!verticalSnapOffsets.isEmpty()) {
324195 std::sort(verticalSnapOffsets.begin(), verticalSnapOffsets.end(), compareSnapOffsets);
325  if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
326  computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height());
327 
328196 LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offsets: " << verticalSnapOffsets);
329  LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offset ranges: " << verticalSnapOffsetRanges);
330197 }
331198
 199 if (!snapAreas.isEmpty())
 200 LOG_WITH_STREAM(ScrollSnap, stream << " => Computed snap areas: " << snapAreas);
 201
332202 scrollableArea.setScrollSnapOffsetInfo({
 203 scrollSnapType.strictness,
333204 horizontalSnapOffsets,
334205 verticalSnapOffsets,
335  horizontalSnapOffsetRanges,
336  verticalSnapOffsetRanges
 206 snapAreas,
337207 });
338208}
339209

@@static LayoutUnit convertOffsetUnit(float input, float /* scaleFactor */)
347217 return LayoutUnit(input);
348218}
349219
350 template <typename InputType, typename OutputType>
351 static ScrollSnapOffsetsInfo<OutputType> convertOffsetInfo(const ScrollSnapOffsetsInfo<InputType>& input, float scaleFactor = 0.0)
 220template <typename InputType, typename InputRectType, typename OutputType, typename OutputRectType>
 221static ScrollSnapOffsetsInfo<OutputType, OutputRectType> convertOffsetInfo(const ScrollSnapOffsetsInfo<InputType, InputRectType>& input, float scaleFactor = 0.0)
352222{
353223 auto convertOffsets = [scaleFactor](const Vector<SnapOffset<InputType>>& input)
354224 {
355225 Vector<SnapOffset<OutputType>> output;
356226 output.reserveInitialCapacity(input.size());
357227 for (auto& offset : input)
358  output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop });
 228 output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop, offset.snapAreaIndex });
359229 return output;
360230 };
361231
362  auto convertOffsetRanges = [scaleFactor](const Vector<ScrollOffsetRange<InputType>>& input)
 232 auto convertRects = [scaleFactor](const Vector<InputRectType>& input)
363233 {
364  Vector<ScrollOffsetRange<OutputType>> output;
 234 Vector<OutputRectType> output;
365235 output.reserveInitialCapacity(input.size());
366  for (auto& range : input)
367  output.uncheckedAppend({ convertOffsetUnit(range.start, scaleFactor), convertOffsetUnit(range.end, scaleFactor) });
 236 for (auto& rect : input) {
 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 }
368242 return output;
369243 };
370244
371245 return {
 246 input.strictness,
372247 convertOffsets(input.horizontalSnapOffsets),
373248 convertOffsets(input.verticalSnapOffsets),
374  convertOffsetRanges(input.horizontalSnapOffsetRanges),
375  convertOffsetRanges(input.verticalSnapOffsetRanges)
 249 convertRects(input.snapAreas),
376250 };
377251}
378252
379253template <> template <>
380 ScrollSnapOffsetsInfo<LayoutUnit> ScrollSnapOffsetsInfo<float>::convertUnits(float /* unusedScaleFactor */) const
 254LayoutScrollSnapOffsetsInfo FloatScrollSnapOffsetsInfo::convertUnits(float /* unusedScaleFactor */) const
381255{
382  return convertOffsetInfo<float, LayoutUnit>(*this);
 256 return convertOffsetInfo<float, FloatRect, LayoutUnit, LayoutRect>(*this);
383257}
384258
385259template <> template <>
386 ScrollSnapOffsetsInfo<float> ScrollSnapOffsetsInfo<LayoutUnit>::convertUnits(float deviceScaleFactor) const
 260FloatScrollSnapOffsetsInfo LayoutScrollSnapOffsetsInfo::convertUnits(float deviceScaleFactor) const
387261{
388  return convertOffsetInfo<LayoutUnit, float>(*this, deviceScaleFactor);
 262 return convertOffsetInfo<LayoutUnit, LayoutRect, float, FloatRect>(*this, deviceScaleFactor);
389263}
390264
391 template <>
392 std::pair<LayoutUnit, unsigned> ScrollSnapOffsetsInfo<LayoutUnit>::closestSnapOffset(ScrollEventAxis axis, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const
 265template <typename UnitType, typename RectType>
 266static UnitType maxForAxis(RectType rect, ScrollEventAxis axis)
393267{
394  return closestSnapOffsetWithOffsetsAndRanges(offsetsForAxis(axis), offsetRangesForAxis(axis), scrollDestinationOffset, velocity, originalPositionForDirectionalSnapping);
 268 return axis == ScrollEventAxis::Horizontal ? rect.maxX() : rect.maxY();
395269}
396270
397 template <>
398 std::pair<float, unsigned> ScrollSnapOffsetsInfo<float>::closestSnapOffset(ScrollEventAxis axis, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const
 271template <typename UnitType, typename RectType>
 272static UnitType minForAxis(RectType rect, ScrollEventAxis axis)
 273{
 274 return axis == ScrollEventAxis::Horizontal ? rect.x() : rect.y();
 275}
 276
 277template <typename UnitType>
 278static 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
 289template <typename UnitType, typename RectType, typename SizeType>
 290static 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
 383template <> template <>
 384std::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
 389template <> template <>
 390std::pair<float, unsigned> FloatScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis axis, const FloatSize& viewportSize, float scrollDestinationOffset, float velocity, Optional<float> originalOffsetForDirectionalSnapping) const
399391{
400  return closestSnapOffsetWithOffsetsAndRanges(offsetsForAxis(axis), offsetRangesForAxis(axis), scrollDestinationOffset, velocity, originalPositionForDirectionalSnapping);
 392 return calculateClosestSnapOffset<float, FloatRect, FloatSize>(*this, axis, viewportSize, scrollDestinationOffset, velocity, originalOffsetForDirectionalSnapping);
401393}
402394
403395}

Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h

2727
2828#if ENABLE(CSS_SCROLL_SNAP)
2929
 30#include "FloatRect.h"
 31#include "FloatSize.h"
 32#include "LayoutRect.h"
 33#include "LayoutSize.h"
3034#include "LayoutUnit.h"
3135#include "ScrollTypes.h"
3236#include "StyleScrollSnapPoints.h"

3539
3640namespace WebCore {
3741
38 class LayoutRect;
3942class ScrollableArea;
4043class RenderBox;
4144class RenderStyle;
4245
43 template <typename T>
 46template <typename OffsetType>
4447struct SnapOffset {
45  T offset;
 48 OffsetType offset;
4649 ScrollSnapStop stop;
 50 size_t snapAreaIndex;
4751};
4852
49 template <typename T>
50 struct ScrollOffsetRange {
51  T start;
52  T end;
53 };
54 
55 template <typename T>
 53template <typename OffsetType, typename RectType>
5654struct ScrollSnapOffsetsInfo {
5755 WTF_MAKE_STRUCT_FAST_ALLOCATED;
58  Vector<SnapOffset<T>> horizontalSnapOffsets;
59  Vector<SnapOffset<T>> verticalSnapOffsets;
 56 ScrollSnapStrictness strictness;
6057
61  // Snap offset ranges represent non-empty ranges of scroll offsets in which scrolling may rest after scroll snapping.
62  // These are used in two cases: (1) for proximity scroll snapping, where portions of areas between adjacent snap offsets
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
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;
 58 Vector<SnapOffset<OffsetType>> horizontalSnapOffsets;
 59 Vector<SnapOffset<OffsetType>> verticalSnapOffsets;
 60 Vector<RectType> snapAreas;
6761
68  bool isEqual(const ScrollSnapOffsetsInfo<T>& other) const
 62 bool isEqual(const ScrollSnapOffsetsInfo<OffsetType, RectType>& other) const
6963 {
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;
7165 }
7266
7367 bool isEmpty() const

@@struct ScrollSnapOffsetsInfo {
7569 return horizontalSnapOffsets.isEmpty() && verticalSnapOffsets.isEmpty();
7670 }
7771
78  Vector<SnapOffset<T>> offsetsForAxis(ScrollEventAxis axis) const
 72 Vector<SnapOffset<OffsetType>> offsetsForAxis(ScrollEventAxis axis) const
7973 {
8074 return axis == ScrollEventAxis::Vertical ? verticalSnapOffsets : horizontalSnapOffsets;
8175 }
8276
83  Vector<ScrollOffsetRange<T>> offsetRangesForAxis(ScrollEventAxis axis) const
84  {
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;
 77 template<typename OutputType, typename OutputRectType> ScrollSnapOffsetsInfo<OutputType, OutputRectType> convertUnits(float deviceScaleFactor = 0.0) const;
 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;
9079};
9180
 81using LayoutScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<LayoutUnit, LayoutRect>;
 82using FloatScrollSnapOffsetsInfo = ScrollSnapOffsetsInfo<float, FloatRect>;
 83
9284template <> template <>
93 ScrollSnapOffsetsInfo<LayoutUnit> ScrollSnapOffsetsInfo<float>::convertUnits(float /* unusedScaleFactor */) const;
94 template <>
95 WEBCORE_EXPORT std::pair<float, unsigned> ScrollSnapOffsetsInfo<float>::closestSnapOffset(ScrollEventAxis, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const;
 85LayoutScrollSnapOffsetsInfo FloatScrollSnapOffsetsInfo::convertUnits(float /* unusedScaleFactor */) const;
 86template <> template <>
 87WEBCORE_EXPORT std::pair<float, unsigned> FloatScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis, const FloatSize& viewportSize, float scrollDestinationOffset, float velocity, Optional<float> originalPositionForDirectionalSnapping) const;
9688
9789template <> template <>
98 ScrollSnapOffsetsInfo<float> ScrollSnapOffsetsInfo<LayoutUnit>::convertUnits(float deviceScaleFactor) const;
99 template <>
100 WEBCORE_EXPORT std::pair<LayoutUnit, unsigned> ScrollSnapOffsetsInfo<LayoutUnit>::closestSnapOffset(ScrollEventAxis, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const;
 90FloatScrollSnapOffsetsInfo LayoutScrollSnapOffsetsInfo::convertUnits(float deviceScaleFactor) const;
 91template <> template <>
 92WEBCORE_EXPORT std::pair<LayoutUnit, unsigned> LayoutScrollSnapOffsetsInfo::closestSnapOffset(ScrollEventAxis, const LayoutSize& viewportSize, LayoutUnit scrollDestinationOffset, float velocity, Optional<LayoutUnit> originalPositionForDirectionalSnapping) const;
10193
10294const unsigned invalidSnapOffsetIndex = UINT_MAX;
10395

@@const unsigned invalidSnapOffsetIndex = UINT_MAX;
10698// the scrolling container's border box.
10799void updateSnapOffsetsForScrollableArea(ScrollableArea&, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates);
108100
109 template <typename T> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffset<T> offset)
 101template <typename OffsetType> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffset<OffsetType> offset)
110102{
111103 ts << offset.offset;
112104 if (offset.stop == ScrollSnapStop::Always)

@@template <typename T> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffse
114106 return ts;
115107}
116108
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 
124109}; // namespace WebCore
125110
126111#endif // ENABLE(CSS_SCROLL_SNAP)

Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp

@@void ScrollingStateScrollingNode::setScrollOrigin(const IntPoint& scrollOrigin)
157157}
158158
159159#if ENABLE(CSS_SCROLL_SNAP)
160 void ScrollingStateScrollingNode::setSnapOffsetsInfo(const ScrollSnapOffsetsInfo<float>& info)
 160void ScrollingStateScrollingNode::setSnapOffsetsInfo(const FloatScrollSnapOffsetsInfo& info)
161161{
162162 if (m_snapOffsetsInfo.isEqual(info))
163163 return;

Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h

@@public:
7171 WEBCORE_EXPORT void setScrollOrigin(const IntPoint&);
7272
7373#if ENABLE(CSS_SCROLL_SNAP)
74  const ScrollSnapOffsetsInfo<float>& snapOffsetsInfo() const { return m_snapOffsetsInfo; }
75  WEBCORE_EXPORT void setSnapOffsetsInfo(const ScrollSnapOffsetsInfo<float>& newOffsetsInfo);
 74 const FloatScrollSnapOffsetsInfo& snapOffsetsInfo() const { return m_snapOffsetsInfo; }
 75 WEBCORE_EXPORT void setSnapOffsetsInfo(const FloatScrollSnapOffsetsInfo& newOffsetsInfo);
7676
7777 unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
7878 WEBCORE_EXPORT void setCurrentHorizontalSnapPointIndex(unsigned);

@@private:
130130 IntPoint m_scrollOrigin;
131131
132132#if ENABLE(CSS_SCROLL_SNAP)
133  ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo;
 133 FloatScrollSnapOffsetsInfo m_snapOffsetsInfo;
134134 unsigned m_currentHorizontalSnapPointIndex { 0 };
135135 unsigned m_currentVerticalSnapPointIndex { 0 };
136136#endif

Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp

@@void ScrollingTreeScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTr
327327}
328328
329329#if ENABLE(CSS_SCROLL_SNAP)
330 const ScrollSnapOffsetsInfo<float>& ScrollingTreeScrollingNode::snapOffsetsInfo() const
 330const FloatScrollSnapOffsetsInfo& ScrollingTreeScrollingNode::snapOffsetsInfo() const
331331{
332332 return m_snapOffsetsInfo;
333333}

Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h

@@public:
9595 bool canHaveScrollbars() const { return m_scrollableAreaParameters.horizontalScrollbarMode != ScrollbarAlwaysOff || m_scrollableAreaParameters.verticalScrollbarMode != ScrollbarAlwaysOff; }
9696
9797#if ENABLE(CSS_SCROLL_SNAP)
98  const ScrollSnapOffsetsInfo<float>& snapOffsetsInfo() const;
 98 const FloatScrollSnapOffsetsInfo& snapOffsetsInfo() const;
9999 unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
100100 unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; }
101101 void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }

@@private:
159159 FloatPoint m_currentScrollPosition;
160160 IntPoint m_scrollOrigin;
161161#if ENABLE(CSS_SCROLL_SNAP)
162  ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo;
 162 FloatScrollSnapOffsetsInfo m_snapOffsetsInfo;
163163 unsigned m_currentHorizontalSnapPointIndex { 0 };
164164 unsigned m_currentVerticalSnapPointIndex { 0 };
165165#endif

Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm

@@void ScrollingTreeScrollingNodeDelegateMac::updateFromStateNode(const ScrollingS
6565
6666#if ENABLE(CSS_SCROLL_SNAP)
6767 if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::SnapOffsetsInfo))
68  m_scrollController.updateScrollSnapPoints(scrollingStateNode.snapOffsetsInfo().convertUnits<LayoutUnit>());
 68 m_scrollController.updateScrollSnapPoints(scrollingStateNode.snapOffsetsInfo().convertUnits<LayoutUnit, LayoutRect>());
6969
7070 if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex))
7171 m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());

Source/WebCore/platform/ScrollController.cpp

@@void ScrollController::updateScrollSnapState(const ScrollableArea& scrollableAre
6767 updateScrollSnapPoints(*snapOffsetInfo);
6868}
6969
70 void ScrollController::updateScrollSnapPoints(const ScrollSnapOffsetsInfo<LayoutUnit>& snapOffsetInfo)
 70void ScrollController::updateScrollSnapPoints(const LayoutScrollSnapOffsetsInfo& snapOffsetInfo)
7171{
7272 if (snapOffsetInfo.isEmpty()) {
7373 m_scrollSnapState = nullptr;

@@void ScrollController::setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis
115115 return;
116116
117117 LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first().offset), snapOffsets.last().offset);
118 
119  unsigned activeIndex = snapState.snapOffsetInfo().closestSnapOffset(axis, clampedOffset, 0).second;
 118 LayoutSize viewportSize(m_client.viewportSize().width(), m_client.viewportSize().height());
 119 unsigned activeIndex = snapState.snapOffsetInfo().closestSnapOffset(axis, viewportSize, clampedOffset, 0).second;
120120 if (activeIndex == activeScrollSnapIndexForAxis(axis))
121121 return;
122122

@@float ScrollController::adjustScrollDestination(ScrollEventAxis axis, float dest
137137 Optional<LayoutUnit> originalOffsetInLayoutUnits;
138138 if (originalOffset.hasValue())
139139 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;
141143 return offset * m_client.pageScaleFactor();
142144}
143145

Source/WebCore/platform/ScrollController.h

@@public:
132132 void scrollPositionChanged();
133133
134134#if ENABLE(CSS_SCROLL_SNAP)
135  void updateScrollSnapPoints(const ScrollSnapOffsetsInfo<LayoutUnit>&);
 135 void updateScrollSnapPoints(const LayoutScrollSnapOffsetsInfo&);
136136 void setActiveScrollSnapIndexForAxis(ScrollEventAxis, unsigned);
137137 void setActiveScrollSnapIndicesForOffset(ScrollOffset);
138138 bool activeScrollSnapIndexDidChange() const { return m_activeScrollSnapIndexDidChange; }

Source/WebCore/platform/ScrollSnapAnimatorState.cpp

@@void ScrollSnapAnimatorState::setupAnimationForState(ScrollSnapState state, cons
5151
5252 m_momentumCalculator = ScrollingMomentumCalculator::create(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
5353 auto predictedScrollTarget = m_momentumCalculator->predictedDestinationOffset();
54  float targetOffsetX = targetOffsetForStartOffset(ScrollEventAxis::Horizontal, 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);
 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, viewportSize, contentSize.height() - viewportSize.height(), initialOffset.y(), predictedScrollTarget.height(), pageScale, initialDelta.height(), m_activeSnapIndexY);
5656 m_momentumCalculator->setRetargetedScrollOffset({ targetOffsetX, targetOffsetY });
5757 m_startTime = MonotonicTime::now();
5858 m_currentState = state;

@@FloatPoint ScrollSnapAnimatorState::currentAnimatedScrollOffset(bool& isAnimatio
9191 return m_momentumCalculator->scrollOffsetAfterElapsedTime(elapsedTime);
9292}
9393
94 float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const
 94float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, const FloatSize& viewportSize, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned& outActiveSnapIndex) const
9595{
9696 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);
9899 if (outActiveSnapIndex != invalidSnapOffsetIndex)
99100 return pageScale * proposedOffset;
100101 return clampTo<float>(startOffset, 0, maxScrollOffset);

@@TextStream& operator<<(TextStream& ts, const ScrollSnapAnimatorState& state)
105106 ts << "ScrollSnapAnimatorState";
106107 ts.dumpProperty("snap offsets x", state.snapOffsetsForAxis(ScrollEventAxis::Horizontal));
107108 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 
113109 ts.dumpProperty("active snap index x", state.activeSnapIndexForAxis(ScrollEventAxis::Horizontal));
114110 ts.dumpProperty("active snap index y", state.activeSnapIndexForAxis(ScrollEventAxis::Vertical));
115111

Source/WebCore/platform/ScrollSnapAnimatorState.h

@@public:
5757 return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsInfo.horizontalSnapOffsets : m_snapOffsetsInfo.verticalSnapOffsets;
5858 }
5959
60  const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRangesForAxis(ScrollEventAxis axis) const
61  {
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  }
 60 const LayoutScrollSnapOffsetsInfo& snapOffsetInfo() const { return m_snapOffsetsInfo; }
 61 void setSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo& newInfo) { m_snapOffsetsInfo = newInfo; }
7862
7963 ScrollSnapState currentState() const { return m_currentState; }
8064

@@public:
10084 void transitionToDestinationReachedState();
10185
10286private:
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;
10488 void teardownAnimationForState(ScrollSnapState);
10589 void setupAnimationForState(ScrollSnapState, const FloatSize& contentSize, const FloatSize& viewportSize, float pageScale, const FloatPoint& initialOffset, const FloatSize& initialVelocity, const FloatSize& initialDelta);
10690
10791 ScrollSnapState m_currentState { ScrollSnapState::UserInteraction };
10892
109  ScrollSnapOffsetsInfo<LayoutUnit> m_snapOffsetsInfo;
 93 LayoutScrollSnapOffsetsInfo m_snapOffsetsInfo;
11094
11195 unsigned m_activeSnapIndexX { 0 };
11296 unsigned m_activeSnapIndexY { 0 };

Source/WebCore/platform/ScrollableArea.cpp

@@String ScrollableArea::verticalScrollbarStateForTesting() const
479479}
480480
481481#if ENABLE(CSS_SCROLL_SNAP)
482 ScrollSnapOffsetsInfo<LayoutUnit>& ScrollableArea::ensureSnapOffsetsInfo()
 482LayoutScrollSnapOffsetsInfo& ScrollableArea::ensureSnapOffsetsInfo()
483483{
484484 if (!m_snapOffsetsInfo)
485  m_snapOffsetsInfo = makeUnique<ScrollSnapOffsetsInfo<LayoutUnit>>();
 485 m_snapOffsetsInfo = makeUnique<LayoutScrollSnapOffsetsInfo>();
486486 return *m_snapOffsetsInfo;
487487}
488488
489 const ScrollSnapOffsetsInfo<LayoutUnit>* ScrollableArea::snapOffsetInfo() const
 489const LayoutScrollSnapOffsetsInfo* ScrollableArea::snapOffsetInfo() const
490490{
491491 return m_snapOffsetsInfo.get();
492492}
493493
494 void ScrollableArea::setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>& info)
 494void ScrollableArea::setScrollSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo& info)
495495{
496496 if (info.isEmpty()) {
497497 clearSnapOffsets();

Source/WebCore/platform/ScrollableArea.h

@@public:
9191 bool usesScrollSnap() const;
9292
9393#if ENABLE(CSS_SCROLL_SNAP)
94  WEBCORE_EXPORT const ScrollSnapOffsetsInfo<LayoutUnit>* snapOffsetInfo() const;
 94 WEBCORE_EXPORT const LayoutScrollSnapOffsetsInfo* snapOffsetInfo() const;
9595 virtual void updateSnapOffsets() { };
96  void setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>&);
 96 void setScrollSnapOffsetInfo(const LayoutScrollSnapOffsetsInfo&);
9797 void clearSnapOffsets();
9898 unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
9999 void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }

@@private:
385385 mutable std::unique_ptr<ScrollAnimator> m_scrollAnimator;
386386
387387#if ENABLE(CSS_SCROLL_SNAP)
388  ScrollSnapOffsetsInfo<LayoutUnit>& ensureSnapOffsetsInfo();
389  std::unique_ptr<ScrollSnapOffsetsInfo<LayoutUnit>> m_snapOffsetsInfo;
 388 LayoutScrollSnapOffsetsInfo& ensureSnapOffsetsInfo();
 389 std::unique_ptr<LayoutScrollSnapOffsetsInfo> m_snapOffsetsInfo;
390390 unsigned m_currentHorizontalSnapPointIndex { 0 };
391391 unsigned m_currentVerticalSnapPointIndex { 0 };
392392#endif

Source/WebCore/platform/mac/ScrollAnimatorMac.mm

@@bool ScrollAnimatorMac::isPinnedForScrollDelta(const FloatSize& delta) const
12781278}
12791279
12801280#if ENABLE(CSS_SCROLL_SNAP)
1281 static bool gestureShouldBeginSnap(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis, const ScrollSnapOffsetsInfo<LayoutUnit>* offsetInfo)
 1281static bool gestureShouldBeginSnap(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis, const LayoutScrollSnapOffsetsInfo* offsetInfo)
12821282{
12831283 if (!offsetInfo)
12841284 return false;

Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp

@@template<> struct ArgumentCoder<RequestedScrollData> {
9898 static WARN_UNUSED_RETURN bool decode(Decoder&, RequestedScrollData&);
9999};
100100
101 template<> struct ArgumentCoder<ScrollSnapOffsetsInfo<float>> {
102  static void encode(Encoder&, const ScrollSnapOffsetsInfo<float>&);
103  static WARN_UNUSED_RETURN bool decode(Decoder&, ScrollSnapOffsetsInfo<float>&);
 101template<> struct ArgumentCoder<FloatScrollSnapOffsetsInfo> {
 102 static void encode(Encoder&, const FloatScrollSnapOffsetsInfo&);
 103 static WARN_UNUSED_RETURN bool decode(Decoder&, FloatScrollSnapOffsetsInfo&);
104104};
105105
106106template<> struct ArgumentCoder<SnapOffset<float>> {

@@bool ArgumentCoder<ScrollingStateScrollingNode>::decode(Decoder& decoder, Scroll
311311 SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollPosition, FloatPoint, setScrollPosition);
312312 SCROLLING_NODE_DECODE(ScrollingStateNode::Property::ScrollOrigin, IntPoint, setScrollOrigin);
313313#if ENABLE(CSS_SCROLL_SNAP)
314  SCROLLING_NODE_DECODE(ScrollingStateNode::Property::SnapOffsetsInfo, ScrollSnapOffsetsInfo<float>, setSnapOffsetsInfo);
 314 SCROLLING_NODE_DECODE(ScrollingStateNode::Property::SnapOffsetsInfo, FloatScrollSnapOffsetsInfo, setSnapOffsetsInfo);
315315 SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentHorizontalSnapOffsetIndex, unsigned, setCurrentHorizontalSnapPointIndex);
316316 SCROLLING_NODE_DECODE(ScrollingStateNode::Property::CurrentVerticalSnapOffsetIndex, unsigned, setCurrentVerticalSnapPointIndex);
317317#endif

@@void ArgumentCoder<SnapOffset<float>>::encode(Encoder& encoder, const SnapOffset
532532{
533533 encoder << offset.offset;
534534 encoder << offset.stop;
 535 encoder << offset.snapAreaIndex;
535536}
536537
537538bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float>& offset)

@@bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float
540541 return false;
541542 if (!decoder.decode(offset.stop))
542543 return false;
 544 if (!decoder.decode(offset.snapAreaIndex))
 545 return false;
543546 return true;
544547}
545548
546549
547 void ArgumentCoder<ScrollSnapOffsetsInfo<float>>::encode(Encoder& encoder, const ScrollSnapOffsetsInfo<float>& info)
 550void ArgumentCoder<FloatScrollSnapOffsetsInfo>::encode(Encoder& encoder, const FloatScrollSnapOffsetsInfo& info)
548551{
549552 encoder << info.horizontalSnapOffsets;
550553 encoder << info.verticalSnapOffsets;
551  encoder << info.horizontalSnapOffsetRanges;
552  encoder << info.verticalSnapOffsetRanges;
 554 encoder << info.snapAreas;
553555}
554556
555 bool ArgumentCoder<ScrollSnapOffsetsInfo<float>>::decode(Decoder& decoder, ScrollSnapOffsetsInfo<float>& info)
 557bool ArgumentCoder<FloatScrollSnapOffsetsInfo>::decode(Decoder& decoder, FloatScrollSnapOffsetsInfo& info)
556558{
557559 if (!decoder.decode(info.horizontalSnapOffsets))
558560 return false;
559561 if (!decoder.decode(info.verticalSnapOffsets))
560562 return false;
561  if (!decoder.decode(info.horizontalSnapOffsetRanges))
562  return false;
563  if (!decoder.decode(info.verticalSnapOffsetRanges))
 563 if (!decoder.decode(info.snapAreas))
564564 return false;
565565 return true;
566566}

Source/WebKit/Shared/WebCoreArgumentCoders.cpp

@@bool ArgumentCoder<ServiceWorkerOrClientIdentifier>::decode(Decoder& decoder, Se
28052805}
28062806#endif
28072807
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 
28342808void ArgumentCoder<MediaSelectionOption>::encode(Encoder& encoder, const MediaSelectionOption& option)
28352809{
28362810 encoder << option.displayName;

Source/WebKit/Shared/WebCoreArgumentCoders.h

@@template<> struct ArgumentCoder<WebCore::ServiceWorkerOrClientIdentifier> {
743743
744744#endif
745745
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 
755746template<> struct ArgumentCoder<WebCore::MediaSelectionOption> {
756747 static void encode(Encoder&, const WebCore::MediaSelectionOption&);
757748 static Optional<WebCore::MediaSelectionOption> decode(Decoder&);

Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm

@@bool RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling(ScrollEven
223223
224224float RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity, unsigned& currentIndex) const
225225{
226  ScrollingTreeNode* root = m_scrollingTree->rootNode();
227  ASSERT(root && root->isFrameScrollingNode());
228  ScrollingTreeFrameScrollingNode* rootScrollingNode = static_cast<ScrollingTreeFrameScrollingNode*>(root);
 226 ScrollingTreeFrameScrollingNode* rootScrollingNode = m_scrollingTree->rootNode();
229227 const auto& snapOffsetsInfo = rootScrollingNode->snapOffsetsInfo();
230228
231229 float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
232230 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);
234232 return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
235233}
236234

Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm

@@- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoi
106106 unsigned originalHorizontalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentHorizontalSnapPointIndex();
107107 unsigned originalVerticalSnapPosition = _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex();
108108
 109 WebCore::FloatSize viewportSize(static_cast<float>(CGRectGetWidth([scrollView bounds])), static_cast<float>(CGRectGetHeight([scrollView bounds])));
109110 const auto& snapOffsetsInfo = _scrollingTreeNodeDelegate->scrollingNode().snapOffsetsInfo();
110111 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);
112113 _scrollingTreeNodeDelegate->scrollingNode().setCurrentHorizontalSnapPointIndex(index);
113114 if (horizontalTarget >= 0 && horizontalTarget <= scrollView.contentSize.width)
114115 targetContentOffset->x = potentialSnapPosition;
115116 }
116117
117118 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);
119120 _scrollingTreeNodeDelegate->scrollingNode().setCurrentVerticalSnapPointIndex(index);
120121 if (verticalTarget >= 0 && verticalTarget <= scrollView.contentSize.height)
121122 targetContentOffset->y = potentialSnapPosition;

LayoutTests/ChangeLog

 12021-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
1142021-03-14 Yusuke Suzuki <ysuzuki@apple.com>
215
316 Prevent dynamic import in service worker

LayoutTests/imported/w3c/ChangeLog

 12021-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
1112021-03-10 Sergio Villar Senin <svillar@igalia.com>
212
313 Do not shrink radio buttons bellow its size

LayoutTests/css3/scroll-snap/scroll-snap-click-scrollbar-gutter.html

3434 scroll-snap-type: both mandatory;
3535 }
3636
37  .block {
38  height: 1000px;
39  width: 250px;
40  scroll-snap-align: start;
41  }
42 
4337 .horizontal-drawer {
4438 height: 100%;
4539 width: 2000px;
4640 }
4741
48  .horizontal-drawer .block {
49  width: 1000px;
50  height: 250px;
 42 .horizontal-drawer > div {
 43 float: left;
 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;
5161 }
 62
5263 </style>
5364 <script src="../../resources/js-test.js"></script>
5465 <script src="../../resources/ui-helper.js"></script>

8798 <body onload="onLoad();">
8899 <div id="horizontal-container" class="container">
89100 <div class="horizontal-drawer">
90  <div class="block" style="float: left; background: #80475E"></div>
91  <div class="block" style="width: 450px; float: left; background: #CC5A71"></div>
 101 <div class="snap-point" style="background: #80475E"></div>
 102 <div></div>
 103 <div class="snap-point" style="background: #CC5A71"></div>
 104 <div></div>
92105 </div>
93106 </div>
94107 <div id="vertical-container" class="container">
95  <div class="block" style="background: #80475E"></div>
96  <div class="block" style="height: 450px; background: #CC5A71"></div>
 108 <div class="snap-point" style="background: #80475E"></div>
 109 <div></div>
 110 <div class="snap-point" style="background: #CC5A71"></div>
 111 <div></div>
97112 </div>
98113 <p id="console"></p>
99114 <script>

LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/overflowing-snap-areas-expected.txt

11
22PASS 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
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
 3PASS Snaps to the snap position if the snap area covers the snapport on x on the right border.
 4PASS Snaps to the snap position if the snap area covers the snapport on x on the left border.
55PASS Snaps if the distance between the previous(400) and next(800) snap positions is smaller than snapport(500) on x.
66PASS 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
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
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
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
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
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
 7PASS 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).
 8PASS 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).
 9PASS 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.
 10PASS 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.
 11PASS 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
 12PASS 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.
1313

LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html

44 <style>
55 html {
66 scroll-snap-type: y mandatory;
 7 margin: 0;
78 }
89 .verticalGallery {
910 width: 100vw;

1213 padding: 0;
1314 }
1415 .colorBox {
15  height: 100vh;
 16 height: 90vh;
1617 width: 100vw;
1718 float: left;
1819 scroll-snap-align: start;

LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html

1313 position: absolute;
1414 top: 0;
1515 left: 0;
16  overflow-x: none;
1716 overflow-y: scroll;
1817 scroll-snap-type: y proximity;
19  -webkit-scroll-snap-type: proximity;
20  scroll-snap-type: proximity;
2118 opacity: 0.5;
2219 }
2320
2421 .area {
25  height: 600px;
26  width: 600px;
 22 height: 100%;
 23 width: 100%;
2724 float: left;
2825 scroll-snap-align: start;
29  -webkit-scroll-snap-coordinate: 0 0;
30  scroll-snap-coordinate: 0 0;
3126 }
3227 </style>
3328 <script>