void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle) { if (scrollingElementStyle.scrollSnapType() == ScrollSnapType::None) { scrollableArea.clearHorizontalSnapOffsets(); scrollableArea.clearVerticalSnapOffsets(); return; } LayoutUnit viewWidth = scrollingElementBox.width(); LayoutUnit viewHeight = scrollingElementBox.height(); LayoutUnit scrollWidth = scrollingElementBox.scrollWidth(); LayoutUnit scrollHeight = scrollingElementBox.scrollHeight(); bool canComputeHorizontalOffsets = scrollWidth > 0 && viewWidth > 0 && viewWidth < scrollWidth; bool canComputeVerticalOffsets = scrollHeight > 0 && viewHeight > 0 && viewHeight < scrollHeight; if (!canComputeHorizontalOffsets) scrollableArea.clearHorizontalSnapOffsets(); if (!canComputeVerticalOffsets) scrollableArea.clearVerticalSnapOffsets(); if (!canComputeHorizontalOffsets && !canComputeVerticalOffsets) return; Vector<LayoutUnit> horizontalSnapOffsetSubsequence; Vector<LayoutUnit> verticalSnapOffsetSubsequence; bool scrollSnapPointsXUsesElements = styleUsesElements(ScrollEventAxis::Horizontal, scrollingElementStyle); bool scrollSnapPointsYUsesElements = styleUsesElements(ScrollEventAxis::Vertical , scrollingElementStyle); if (scrollSnapPointsXUsesElements || scrollSnapPointsYUsesElements) { bool shouldAddHorizontalChildOffsets = scrollSnapPointsXUsesElements && canComputeHorizontalOffsets; bool shouldAddVerticalChildOffsets = scrollSnapPointsYUsesElements && canComputeVerticalOffsets; appendChildSnapOffsets(scrollingElement, shouldAddHorizontalChildOffsets, horizontalSnapOffsetSubsequence, shouldAddVerticalChildOffsets, verticalSnapOffsetSubsequence); } if (scrollingElementStyle.scrollSnapPointsX() && !scrollSnapPointsXUsesElements && canComputeHorizontalOffsets) { for (auto& snapLength : scrollingElementStyle.scrollSnapPointsX()->offsets) horizontalSnapOffsetSubsequence.append(valueForLength(snapLength, viewWidth)); } if (scrollingElementStyle.scrollSnapPointsY() && !scrollSnapPointsYUsesElements && canComputeVerticalOffsets) { for (auto& snapLength : scrollingElementStyle.scrollSnapPointsY()->offsets) verticalSnapOffsetSubsequence.append(valueForLength(snapLength, viewHeight)); } if (canComputeHorizontalOffsets) { auto horizontalSnapOffsets = std::make_unique<Vector<LayoutUnit>>(); updateFromStyle(*horizontalSnapOffsets, scrollingElementStyle, ScrollEventAxis::Horizontal, viewWidth, scrollWidth, horizontalSnapOffsetSubsequence); scrollableArea.setHorizontalSnapOffsets(WTF::move(horizontalSnapOffsets)); } if (canComputeVerticalOffsets) { auto verticalSnapOffsets = std::make_unique<Vector<LayoutUnit>>(); updateFromStyle(*verticalSnapOffsets, scrollingElementStyle, ScrollEventAxis::Vertical, viewHeight, scrollHeight, verticalSnapOffsetSubsequence); scrollableArea.setVerticalSnapOffsets(WTF::move(verticalSnapOffsets)); } }
void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle) { auto* scrollContainer = scrollingElement.renderer(); auto scrollSnapType = scrollingElementStyle.scrollSnapType(); if (!scrollContainer || scrollSnapType.strictness == ScrollSnapStrictness::None || scrollContainer->view().boxesWithScrollSnapPositions().isEmpty()) { scrollableArea.clearHorizontalSnapOffsets(); scrollableArea.clearVerticalSnapOffsets(); return; } Vector<LayoutUnit> verticalSnapOffsets; Vector<LayoutUnit> horizontalSnapOffsets; Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges; Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges; HashSet<float> seenVerticalSnapOffsets; HashSet<float> seenHorizontalSnapOffsets; bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline; bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block; auto maxScrollLeft = scrollingElementBox.scrollWidth() - scrollingElementBox.contentWidth(); auto maxScrollTop = scrollingElementBox.scrollHeight() - scrollingElementBox.contentHeight(); LayoutPoint containerScrollOffset(scrollingElementBox.scrollLeft(), scrollingElementBox.scrollTop()); // The bounds of the scrolling container's snap port, where the top left of the scrolling container's border box is the origin. auto scrollSnapPort = computeScrollSnapPortOrAreaRect(scrollingElementBox.paddingBoxRect(), scrollingElementStyle.scrollPadding(), InsetOrOutset::Inset); #if !LOG_DISABLED LOG(Scrolling, "Computing scroll snap offsets in snap port: %s", snapPortOrAreaToString(scrollSnapPort).utf8().data()); #endif for (auto* child : scrollContainer->view().boxesWithScrollSnapPositions()) { if (child->findEnclosingScrollableContainer() != scrollContainer) continue; // The bounds of the child element's snap area, where the top left of the scrolling container's border box is the origin. // The snap area is the bounding box of the child element's border box, after applying transformations. auto scrollSnapArea = LayoutRect(child->localToContainerQuad(FloatQuad(child->borderBoundingBox()), scrollingElement.renderBox()).boundingBox()); scrollSnapArea.moveBy(containerScrollOffset); scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollSnapMargin(), InsetOrOutset::Outset); #if !LOG_DISABLED LOG(Scrolling, " Considering scroll snap area: %s", snapPortOrAreaToString(scrollSnapArea).utf8().data()); #endif auto alignment = child->style().scrollSnapAlign(); if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) { auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.width(), alignment.x) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.width(), alignment.x), 0, maxScrollLeft); if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) { seenHorizontalSnapOffsets.add(absoluteScrollOffset); horizontalSnapOffsets.append(absoluteScrollOffset); } } if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) { auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.height(), alignment.y) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.height(), alignment.y), 0, maxScrollTop); if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) { seenVerticalSnapOffsets.add(absoluteScrollOffset); verticalSnapOffsets.append(absoluteScrollOffset); } } } if (!horizontalSnapOffsets.isEmpty()) { adjustAxisSnapOffsetsForScrollExtent(horizontalSnapOffsets, maxScrollLeft); #if !LOG_DISABLED LOG(Scrolling, " => Computed horizontal scroll snap offsets: %s", snapOffsetsToString(horizontalSnapOffsets).utf8().data()); LOG(Scrolling, " => Computed horizontal scroll snap offset ranges: %s", snapOffsetRangesToString(horizontalSnapOffsetRanges).utf8().data()); #endif if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width()); scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets); scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges); } else scrollableArea.clearHorizontalSnapOffsets(); if (!verticalSnapOffsets.isEmpty()) { adjustAxisSnapOffsetsForScrollExtent(verticalSnapOffsets, maxScrollTop); #if !LOG_DISABLED LOG(Scrolling, " => Computed vertical scroll snap offsets: %s", snapOffsetsToString(verticalSnapOffsets).utf8().data()); LOG(Scrolling, " => Computed vertical scroll snap offset ranges: %s", snapOffsetRangesToString(verticalSnapOffsetRanges).utf8().data()); #endif if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height()); scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets); scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges); } else scrollableArea.clearVerticalSnapOffsets(); }