Title: [272610] trunk
Revision
272610
Author
[email protected]
Date
2021-02-09 13:52:39 -0800 (Tue, 09 Feb 2021)

Log Message

Implement scroll-snap-stop for scroll snapping
https://bugs.webkit.org/show_bug.cgi?id=197744
<rdar://problem/50708356>

LayoutTests/imported/w3c:

Reviewed by Simon Fraser.

* web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt:
* web-platform-tests/css/css-scroll-snap/inheritance-expected.txt:
* web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt:
* web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt:
* web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt:
* web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt:
* web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt:
* web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:

Source/WebCore:

Reviewed by Simon Fraser.

No new tests. This is tested by existing, imported WPT tests:
    imported/w3c/web-platform-tests/css/css-scroll-snap/inheritance-expected.txt:
    imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt:
    imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt:
    imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt:
    imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt:
    imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt:

* css/CSSComputedStyleDeclaration.cpp:
(WebCore::ComputedStyleExtractor::valueForPropertyInStyle): Add support the new CSS property.
* css/CSSPrimitiveValueMappings.h:
(WebCore::CSSPrimitiveValue::operator ScrollSnapAxisAlignType const): Ditto.
(WebCore::CSSPrimitiveValue::CSSPrimitiveValue): Ditto.
(WebCore::CSSPrimitiveValue::operator ScrollSnapStop const): ditto.
* css/CSSProperties.json: Ditto.
* css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::parseSingleValue): Ditto.
* page/scrolling/ScrollSnapOffsetsInfo.cpp:
(WebCore::indicesOfNearestSnapOffsets): Update to accept SnapOffset instead of a raw
value.
(WebCore::findFirstSnapStopOffsetBetweenOriginAndDestination): Added this helper which looks
between the scroll origin and destination to find a candidate scroll offset with ScrollSnapStop::Always.
This offset will always be chosen for directional scrolls.
(WebCore::closestSnapOffsetWithOffsetsAndRanges): Use new helper to find offsets with ScrollSnapStop::Always.
(WebCore::computeAxisProximitySnapOffsetRanges): Deal with SnapOffset instead of raw values.
(WebCore::updateSnapOffsetsForScrollableArea): Ditto.
(WebCore::convertOffsetInfo): Ditto.
* page/scrolling/ScrollSnapOffsetsInfo.h:
(WebCore::ScrollSnapOffsetsInfo::offsetsForAxis const): Ditto.
(WebCore::operator<<): Consolidated all TextStream implementations here instead of repeating
them throughout the code.
* platform/ScrollableArea.cpp:
(WebCore::ScrollableArea::setScrollSnapOffsetInfo): Set the scroll snap offset info all at once.
(WebCore::ScrollableArea::clearSnapOffsets): We no longer clear horizontal and vertical offsets separately.
(WebCore::ScrollableArea::nearestActiveSnapPoint): Access the offset value.
* platform/ScrollableArea.h:
* platform/cocoa/ScrollController.mm:
(WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset): Ditto.
* platform/cocoa/ScrollSnapAnimatorState.h:
(WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis const): Deal with SnapOffsets instead of raw values.
(WebCore::ScrollSnapAnimatorState::setSnapOffsetsAndPositionRangesForAxis): Ditto.
* platform/cocoa/ScrollSnapAnimatorState.mm:
* rendering/RenderLayerModelObject.cpp:
(WebCore::RenderLayerModelObject::styleDidChange): Update scroll snap information when the `scroll-snap-stop`
property changes. Also ensure that we update everything for FrameView's instead of only some properties.
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::initialScrollSnapStop): Added for new CSS property.
(WebCore::RenderStyle::scrollSnapStop const): Ditto.
(WebCore::RenderStyle::setScrollSnapStop): Ditto.
* rendering/style/RenderStyle.h:
* rendering/style/RenderStyleConstants.cpp:
(WebCore::operator<<): Ditto.
* rendering/style/RenderStyleConstants.h:
* rendering/style/StyleRareNonInheritedData.h:
* style/StyleBuilderConverter.h:
(WebCore::Style::BuilderConverter::convertScrollSnapStop): Ditto.
* testing/Internals.cpp:
(WebCore::appendOffsets): Handle the case where we are printing an offset with ScrollSnapStop::Always.

Source/WebKit:

Reviewed by Simon Fraser.

* Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp: Add encoding and decoding support
for the SnapOffset struct.
(ArgumentCoder<SnapOffset<float>>::encode):
(ArgumentCoder<SnapOffset<float>>::decode):
* UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm: Update to use SnapOffset struct.
(WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling const):
(WebKit::RemoteScrollingCoordinatorProxy::hasActiveSnapPoint const):
(WebKit::RemoteScrollingCoordinatorProxy::nearestActiveContentInsetAdjustedSnapOffset const):

LayoutTests:

Reviewed by Simon Fraser.

* platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (272609 => 272610)


--- trunk/LayoutTests/ChangeLog	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/ChangeLog	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,3 +1,13 @@
+2021-02-09  Martin Robinson  <[email protected]>
+
+        Implement scroll-snap-stop for scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=197744
+        <rdar://problem/50708356>
+
+        Reviewed by Simon Fraser.
+
+        * platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
+
 2021-02-09  Jonathan Bedard  <[email protected]>
 
         [LayoutTests] Convert xmlhttprequest php to Python (Follow-up fix)

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,3 +1,20 @@
+2021-02-09  Martin Robinson  <[email protected]>
+
+        Implement scroll-snap-stop for scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=197744
+        <rdar://problem/50708356>
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/inheritance-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt:
+        * web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt:
+        * web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
+
 2021-02-09  Manuel Rego Casasnovas  <[email protected]>
 
         Allow sending modifier keys in test_driver.send_keys()

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -241,6 +241,7 @@
 PASS scroll-padding-right
 PASS scroll-padding-top
 PASS scroll-snap-align
+PASS scroll-snap-stop
 PASS scroll-snap-type
 PASS shape-image-threshold
 PASS shape-margin

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/inheritance-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/inheritance-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/inheritance-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -33,8 +33,8 @@
 PASS Property scroll-padding-top does not inherit
 PASS Property scroll-snap-align has initial value none
 PASS Property scroll-snap-align does not inherit
-FAIL Property scroll-snap-stop has initial value normal assert_true: scroll-snap-stop doesn't seem to be supported in the computed style expected true got false
-FAIL Property scroll-snap-stop does not inherit assert_true: expected true got false
+PASS Property scroll-snap-stop has initial value normal
+PASS Property scroll-snap-stop does not inherit
 PASS Property scroll-snap-type has initial value none
 PASS Property scroll-snap-type does not inherit
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,4 +1,4 @@
 
-FAIL Property scroll-snap-stop value 'normal' assert_true: scroll-snap-stop doesn't seem to be supported in the computed style expected true got false
-FAIL Property scroll-snap-stop value 'always' assert_true: scroll-snap-stop doesn't seem to be supported in the computed style expected true got false
+PASS Property scroll-snap-stop value 'normal'
+PASS Property scroll-snap-stop value 'always'
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,4 +1,4 @@
 
-FAIL e.style['scroll-snap-stop'] = "normal" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['scroll-snap-stop'] = "always" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['scroll-snap-stop'] = "normal" should set the property value
+PASS e.style['scroll-snap-stop'] = "always" should set the property value
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,4 +1,4 @@
 
-FAIL scroll-snap-stop for areas on HTML should control snapping behavior and changing it takes effect assert_equals: scrollBy should not skip area with stop always expected 100 but got 500
-FAIL scroll-snap-stop for areas on DIV should control snapping behavior and changing it takes effect assert_equals: scrollBy should not skip area with stop always expected 100 but got 500
+PASS scroll-snap-stop for areas on HTML should control snapping behavior and changing it takes effect
+PASS scroll-snap-stop for areas on DIV should control snapping behavior and changing it takes effect
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,6 +1,6 @@
 
-FAIL A scroll with intended direction and end position should not pass a snap area with scroll-snap-stop: always. assert_equals: expected 100 but got 200
+PASS A scroll with intended direction and end position should not pass a snap area with scroll-snap-stop: always.
 PASS A scroll with intended end position should always choose the closest snap position regardless of the scroll-snap-stop value.
-FAIL A scroll outside bounds in the snapping axis with intended direction and end position should not pass a snap area with scroll-snap-stop: always. assert_equals: expected 100 but got 200
-FAIL A scroll outside bounds in the non-snapping axis with intended direction and end position should not pass a snap area with scroll-snap-stop: always. assert_equals: expected 100 but got 200
+PASS A scroll outside bounds in the snapping axis with intended direction and end position should not pass a snap area with scroll-snap-stop: always.
+PASS A scroll outside bounds in the non-snapping axis with intended direction and end position should not pass a snap area with scroll-snap-stop: always.
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,5 +1,5 @@
 
-FAIL The scroll-snap-type on the root element is applied assert_equals: expected 515 but got 800
+PASS The scroll-snap-type on the root element is applied
 FAIL The writing-mode (vertical-lr) on the body is used assert_equals: inline should snap expected 515 but got 800
 PASS The writing-mode (horizontal-tb) on the body is used
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt (272609 => 272610)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -7,5 +7,5 @@
 PASS another cssText order (non-alphabetical order)
 PASS whitespaces in value
 PASS invalid property does not appear
-FAIL cssText on computed style declaration returns the empty string assert_equals: cssText is empty expected "" but got "align-content: normal; align-items: normal; align-self: auto; alignment-baseline: auto; all: ; alt: \"\"; animation-delay: 0s; animation-direction: normal; animation-duration: 0s; animation-fill-mode: none; animation-iteration-count: 1; animation-name: none; animation-play-state: running; animation-timing-function: ease; aspect-ratio: auto; background-attachment: scroll; background-blend-mode: normal; background-clip: border-box; background-color: rgba(0, 0, 0, 0); background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; baseline-shift: baseline; block-size: 0px; border-block-end-color: rgb(255, 0, 0); border-block-end-style: none; border-block-end-width: 0px; border-block-start-color: rgb(255, 0, 0); border-block-start-style:
  none; border-block-start-width: 0px; border-bottom-color: rgb(255, 0, 0); border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom-style: none; border-bottom-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image-outset: 0px; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-inline-end-color: rgb(255, 0, 0); border-inline-end-style: none; border-inline-end-width: 0px; border-inline-start-color: rgb(255, 0, 0); border-inline-start-style: none; border-inline-start-width: 0px; border-left-color: rgb(255, 0, 0); border-left-style: none; border-left-width: 0px; border-right-color: rgb(255, 0, 0); border-right-style: none; border-right-width: 0px; border-start-end-radius: 0px; border-start-start-radius: 0px; border-top-color: rgb(255, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-top-style: none; border-top-width: 0px; bott
 om: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgb(255, 0, 0); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgb(255, 0, 0); color-interpolation: sRGB; color-interpolation-filters: linearRGB; color-rendering: auto; color-scheme: auto; column-count: auto; column-fill: balance; column-gap: normal; column-rule-color: rgb(255, 0, 0); column-rule-style: none; column-rule-width: 0px; column-span: none; column-width: auto; content: ; counter-increment: none; counter-reset: none; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: block; dominant-baseline: auto; empty-cells: show; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex-basis: auto; flex-direction: row; flex-grow: 0; flex-shrink: 1; flex-wrap: nowrap; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: -webkit-standard; font-feature-setti
 ngs: normal; font-optical-sizing: auto; font-size: 13.333333015441895px; font-stretch: normal; font-style: normal; font-synthesis: style weight small-caps; font-variant-alternates: normal; font-variant-caps: normal; font-variant-east-asian: normal; font-variant-ligatures: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; glyph-orientation-horizontal: 0deg; glyph-orientation-vertical: auto; grid-auto-columns: auto; grid-auto-flow: row; grid-auto-rows: auto; grid-column-end: auto; grid-column-start: auto; grid-row-end: auto; grid-row-start: auto; grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; hanging-punctuation: none; height: 0px; image-orientation: from-image; image-rendering: auto; inline-size: 784px; inset-block-end: auto; inset-block-start: auto; inset-inline-end: auto; inset-inline-start: auto; isolation: auto; justify-content: normal; justify-items: normal; justify-self: 
 auto; kerning: 0; left: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: normal; list-style-image: none; list-style-position: outside; list-style-type: disc; margin-block-end: 0px; margin-block-start: 0px; margin-bottom: 0px; margin-inline-end: 0px; margin-inline-start: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; marker-end: none; marker-mid: none; marker-start: none; mask: none; mask-type: luminance; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; opacity: 1; order: 0; orphans: auto; outline-color: rgb(255, 0, 0); outline-offset: 0px; outline-style: none; outline-width: 0px; overflow-wrap: normal; overflow-x: visible; overflow-y: visible; overscroll-behavior-x: auto; overscroll-behavior-y: auto; padding-block-end: 0px; padding-
 block-start: 0px; padding-bottom: 0px; padding-inline-end: 0px; padding-inline-start: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; page-break-after: auto; page-break-before: auto; page-break-inside: auto; paint-order: normal; perspective: none; perspective-origin-x: ; perspective-origin-y: ; pointer-events: auto; position: static; quotes: auto; r: 0px; resize: none; right: auto; rotate: none; row-gap: normal; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-margin-block: 0px; scroll-margin-bottom: 0px; scroll-margin-inline: 0px; scroll-margin-left: 0px; scroll-margin-right: 0px; scroll-margin-top: 0px; scroll-padding-block: auto; scroll-padding-bottom: auto; scroll-padding-inline: auto; scroll-padding-left: auto; scroll-padding-right: auto; scroll-padding-top: auto; scroll-snap-align: none; scroll-snap-type: none; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; size: ; speak-as: normal; stop-color: rgb(0, 0, 0);
  stop-opacity: 1; stroke: none; stroke-color: rgba(0, 0, 0, 0); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 8; table-layout: auto; text-align: start; text-anchor: start; text-decoration: none; text-decoration-color: rgb(255, 0, 0); text-decoration-line: none; text-decoration-skip: auto; text-decoration-style: solid; text-decoration-thickness: auto; text-indent: 0px; text-orientation: mixed; text-overflow: clip; text-rendering: auto; text-shadow: none; text-transform: none; text-underline-offset: auto; text-underline-position: auto; top: auto; touch-action: auto; transform: none; transform-box: view-box; transform-origin-x: ; transform-origin-y: ; transform-origin-z: ; transform-style: flat; transition-delay: 0s; transition-duration: 0s; transition-property: all; transition-timing-function: ease; translate: none; unicode-bidi: normal; vector-effect: none; vertical-a
 lign: baseline; visibility: visible; white-space: normal; widows: auto; width: 784px; will-change: auto; word-break: normal; word-spacing: 0px; word-wrap: normal; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; -apple-color-filter: none; -apple-pay-button-style: black; -apple-pay-button-type: plain; -apple-trailing-word: auto; -webkit-appearance: none; -webkit-backdrop-filter: none; -webkit-backface-visibility: visible; -webkit-background-clip: border-box; -webkit-background-composite: source-over; -webkit-background-origin: padding-box; -webkit-background-size: auto; -webkit-border-fit: border; -webkit-border-horizontal-spacing: 0px; -webkit-border-image: none; -webkit-border-vertical-spacing: 0px; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-flex-group: 1; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-b
 ox-reflect: none; -webkit-box-shadow: none; -webkit-column-axis: auto; -webkit-column-break-after: auto; -webkit-column-break-before: auto; -webkit-column-break-inside: auto; -webkit-column-progression: normal; -webkit-cursor-visibility: auto; -webkit-font-kerning: auto; -webkit-font-smoothing: auto; -webkit-hyphenate-character: auto; -webkit-hyphenate-limit-after: auto; -webkit-hyphenate-limit-before: auto; -webkit-hyphenate-limit-lines: no-limit; -webkit-hyphens: manual; -webkit-initial-letter: normal; -webkit-line-align: none; -webkit-line-box-contain: block inline replaced; -webkit-line-clamp: none; -webkit-line-grid: none; -webkit-line-snap: none; -webkit-locale: auto; -webkit-margin-after-collapse: collapse; -webkit-margin-before-collapse: collapse; -webkit-margin-bottom-collapse: collapse; -webkit-margin-top-collapse: collapse; -webkit-mask-box-image: none; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webki
 t-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-clip: border-box; -webkit-mask-composite: source-over; -webkit-mask-image: none; -webkit-mask-origin: border-box; -webkit-mask-position-x: 0%; -webkit-mask-position-y: 0%; -webkit-mask-repeat: repeat; -webkit-mask-size: auto; -webkit-mask-source-type: alpha; -webkit-nbsp-mode: normal; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-ruby-position: before; -webkit-text-combine: none; -webkit-text-emphasis-color: rgb(255, 0, 0); -webkit-text-emphasis-position: over right; -webkit-text-emphasis-style: none; -webkit-text-fill-color: rgb(255, 0, 0); -webkit-text-orientation: mixed; -webkit-text-security: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-color: rgb(255, 0, 0); -webkit-text-stroke-width: 0px; -webkit-text-zoom: normal; -webkit-transform-style: flat; -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-user-select: text;"
+FAIL cssText on computed style declaration returns the empty string assert_equals: cssText is empty expected "" but got "align-content: normal; align-items: normal; align-self: auto; alignment-baseline: auto; all: ; alt: \"\"; animation-delay: 0s; animation-direction: normal; animation-duration: 0s; animation-fill-mode: none; animation-iteration-count: 1; animation-name: none; animation-play-state: running; animation-timing-function: ease; aspect-ratio: auto; background-attachment: scroll; background-blend-mode: normal; background-clip: border-box; background-color: rgba(0, 0, 0, 0); background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; baseline-shift: baseline; block-size: 0px; border-block-end-color: rgb(255, 0, 0); border-block-end-style: none; border-block-end-width: 0px; border-block-start-color: rgb(255, 0, 0); border-block-start-style: 
 none; border-block-start-width: 0px; border-bottom-color: rgb(255, 0, 0); border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom-style: none; border-bottom-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image-outset: 0px; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-inline-end-color: rgb(255, 0, 0); border-inline-end-style: none; border-inline-end-width: 0px; border-inline-start-color: rgb(255, 0, 0); border-inline-start-style: none; border-inline-start-width: 0px; border-left-color: rgb(255, 0, 0); border-left-style: none; border-left-width: 0px; border-right-color: rgb(255, 0, 0); border-right-style: none; border-right-width: 0px; border-start-end-radius: 0px; border-start-start-radius: 0px; border-top-color: rgb(255, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-top-style: none; border-top-width: 0px; botto
 m: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgb(255, 0, 0); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgb(255, 0, 0); color-interpolation: sRGB; color-interpolation-filters: linearRGB; color-rendering: auto; color-scheme: auto; column-count: auto; column-fill: balance; column-gap: normal; column-rule-color: rgb(255, 0, 0); column-rule-style: none; column-rule-width: 0px; column-span: none; column-width: auto; content: ; counter-increment: none; counter-reset: none; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: block; dominant-baseline: auto; empty-cells: show; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex-basis: auto; flex-direction: row; flex-grow: 0; flex-shrink: 1; flex-wrap: nowrap; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: -webkit-standard; font-feature-settin
 gs: normal; font-optical-sizing: auto; font-size: 13.333333015441895px; font-stretch: normal; font-style: normal; font-synthesis: style weight small-caps; font-variant-alternates: normal; font-variant-caps: normal; font-variant-east-asian: normal; font-variant-ligatures: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; glyph-orientation-horizontal: 0deg; glyph-orientation-vertical: auto; grid-auto-columns: auto; grid-auto-flow: row; grid-auto-rows: auto; grid-column-end: auto; grid-column-start: auto; grid-row-end: auto; grid-row-start: auto; grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; hanging-punctuation: none; height: 0px; image-orientation: from-image; image-rendering: auto; inline-size: 784px; inset-block-end: auto; inset-block-start: auto; inset-inline-end: auto; inset-inline-start: auto; isolation: auto; justify-content: normal; justify-items: normal; justify-self: a
 uto; kerning: 0; left: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: normal; list-style-image: none; list-style-position: outside; list-style-type: disc; margin-block-end: 0px; margin-block-start: 0px; margin-bottom: 0px; margin-inline-end: 0px; margin-inline-start: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; marker-end: none; marker-mid: none; marker-start: none; mask: none; mask-type: luminance; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; opacity: 1; order: 0; orphans: auto; outline-color: rgb(255, 0, 0); outline-offset: 0px; outline-style: none; outline-width: 0px; overflow-wrap: normal; overflow-x: visible; overflow-y: visible; overscroll-behavior-x: auto; overscroll-behavior-y: auto; padding-block-end: 0px; padding-b
 lock-start: 0px; padding-bottom: 0px; padding-inline-end: 0px; padding-inline-start: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; page-break-after: auto; page-break-before: auto; page-break-inside: auto; paint-order: normal; perspective: none; perspective-origin-x: ; perspective-origin-y: ; pointer-events: auto; position: static; quotes: auto; r: 0px; resize: none; right: auto; rotate: none; row-gap: normal; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-margin-block: 0px; scroll-margin-bottom: 0px; scroll-margin-inline: 0px; scroll-margin-left: 0px; scroll-margin-right: 0px; scroll-margin-top: 0px; scroll-padding-block: auto; scroll-padding-bottom: auto; scroll-padding-inline: auto; scroll-padding-left: auto; scroll-padding-right: auto; scroll-padding-top: auto; scroll-snap-align: none; scroll-snap-stop: normal; scroll-snap-type: none; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; size: ; speak-as: normal; 
 stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: none; stroke-color: rgba(0, 0, 0, 0); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 8; table-layout: auto; text-align: start; text-anchor: start; text-decoration: none; text-decoration-color: rgb(255, 0, 0); text-decoration-line: none; text-decoration-skip: auto; text-decoration-style: solid; text-decoration-thickness: auto; text-indent: 0px; text-orientation: mixed; text-overflow: clip; text-rendering: auto; text-shadow: none; text-transform: none; text-underline-offset: auto; text-underline-position: auto; top: auto; touch-action: auto; transform: none; transform-box: view-box; transform-origin-x: ; transform-origin-y: ; transform-origin-z: ; transform-style: flat; transition-delay: 0s; transition-duration: 0s; transition-property: all; transition-timing-function: ease; translate: none; unicode-bidi: normal; vector
 -effect: none; vertical-align: baseline; visibility: visible; white-space: normal; widows: auto; width: 784px; will-change: auto; word-break: normal; word-spacing: 0px; word-wrap: normal; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; -apple-color-filter: none; -apple-pay-button-style: black; -apple-pay-button-type: plain; -apple-trailing-word: auto; -webkit-appearance: none; -webkit-backdrop-filter: none; -webkit-backface-visibility: visible; -webkit-background-clip: border-box; -webkit-background-composite: source-over; -webkit-background-origin: padding-box; -webkit-background-size: auto; -webkit-border-fit: border; -webkit-border-horizontal-spacing: 0px; -webkit-border-image: none; -webkit-border-vertical-spacing: 0px; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-flex-group: 1; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-b
 ox-pack: start; -webkit-box-reflect: none; -webkit-box-shadow: none; -webkit-column-axis: auto; -webkit-column-break-after: auto; -webkit-column-break-before: auto; -webkit-column-break-inside: auto; -webkit-column-progression: normal; -webkit-cursor-visibility: auto; -webkit-font-kerning: auto; -webkit-font-smoothing: auto; -webkit-hyphenate-character: auto; -webkit-hyphenate-limit-after: auto; -webkit-hyphenate-limit-before: auto; -webkit-hyphenate-limit-lines: no-limit; -webkit-hyphens: manual; -webkit-initial-letter: normal; -webkit-line-align: none; -webkit-line-box-contain: block inline replaced; -webkit-line-clamp: none; -webkit-line-grid: none; -webkit-line-snap: none; -webkit-locale: auto; -webkit-margin-after-collapse: collapse; -webkit-margin-before-collapse: collapse; -webkit-margin-bottom-collapse: collapse; -webkit-margin-top-collapse: collapse; -webkit-mask-box-image: none; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-im
 age-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-clip: border-box; -webkit-mask-composite: source-over; -webkit-mask-image: none; -webkit-mask-origin: border-box; -webkit-mask-position-x: 0%; -webkit-mask-position-y: 0%; -webkit-mask-repeat: repeat; -webkit-mask-size: auto; -webkit-mask-source-type: alpha; -webkit-nbsp-mode: normal; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-ruby-position: before; -webkit-text-combine: none; -webkit-text-emphasis-color: rgb(255, 0, 0); -webkit-text-emphasis-position: over right; -webkit-text-emphasis-style: none; -webkit-text-fill-color: rgb(255, 0, 0); -webkit-text-orientation: mixed; -webkit-text-security: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-color: rgb(255, 0, 0); -webkit-text-stroke-width: 0px; -webkit-text-zoom: normal; -webkit-transform-style: flat; -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-user-select: text;&quo
 t;
 

Modified: trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt (272609 => 272610)


--- trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,5 +1,5 @@
 
-FAIL The scroll-snap-type on the root element is applied assert_equals: expected 500 but got 800
+PASS The scroll-snap-type on the root element is applied
 FAIL The writing-mode (vertical-lr) on the body is used assert_equals: inline should snap expected 500 but got 800
 PASS The writing-mode (horizontal-tb) on the body is used
 

Modified: trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt (272609 => 272610)


--- trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt	2021-02-09 21:52:39 UTC (rev 272610)
@@ -7,5 +7,5 @@
 PASS another cssText order (non-alphabetical order)
 PASS whitespaces in value
 PASS invalid property does not appear
-FAIL cssText on computed style declaration returns the empty string assert_equals: cssText is empty expected "" but got "align-content: normal; align-items: normal; align-self: auto; alignment-baseline: auto; all: ; alt: \"\"; animation-delay: 0s; animation-direction: normal; animation-duration: 0s; animation-fill-mode: none; animation-iteration-count: 1; animation-name: none; animation-play-state: running; animation-timing-function: ease; aspect-ratio: auto; background-attachment: scroll; background-blend-mode: normal; background-clip: border-box; background-color: rgba(0, 0, 0, 0); background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; baseline-shift: baseline; block-size: 0px; border-block-end-color: rgb(255, 0, 0); border-block-end-style: none; border-block-end-width: 0px; border-block-start-color: rgb(255, 0, 0); border-block-start-style:
  none; border-block-start-width: 0px; border-bottom-color: rgb(255, 0, 0); border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom-style: none; border-bottom-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image-outset: 0px; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-inline-end-color: rgb(255, 0, 0); border-inline-end-style: none; border-inline-end-width: 0px; border-inline-start-color: rgb(255, 0, 0); border-inline-start-style: none; border-inline-start-width: 0px; border-left-color: rgb(255, 0, 0); border-left-style: none; border-left-width: 0px; border-right-color: rgb(255, 0, 0); border-right-style: none; border-right-width: 0px; border-start-end-radius: 0px; border-start-start-radius: 0px; border-top-color: rgb(255, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-top-style: none; border-top-width: 0px; bott
 om: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgb(255, 0, 0); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgb(255, 0, 0); color-interpolation: sRGB; color-interpolation-filters: linearRGB; color-rendering: auto; color-scheme: auto; column-count: auto; column-fill: balance; column-gap: normal; column-rule-color: rgb(255, 0, 0); column-rule-style: none; column-rule-width: 0px; column-span: none; column-width: auto; content: ; counter-increment: none; counter-reset: none; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: block; dominant-baseline: auto; empty-cells: show; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex-basis: auto; flex-direction: row; flex-grow: 0; flex-shrink: 1; flex-wrap: nowrap; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: -webkit-standard; font-feature-setti
 ngs: normal; font-optical-sizing: auto; font-size: 13.333333015441895px; font-stretch: normal; font-style: normal; font-synthesis: style weight small-caps; font-variant-alternates: normal; font-variant-caps: normal; font-variant-east-asian: normal; font-variant-ligatures: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; glyph-orientation-horizontal: 0deg; glyph-orientation-vertical: auto; grid-auto-columns: auto; grid-auto-flow: row; grid-auto-rows: auto; grid-column-end: auto; grid-column-start: auto; grid-row-end: auto; grid-row-start: auto; grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; hanging-punctuation: none; height: 0px; image-orientation: from-image; image-rendering: auto; inline-size: 784px; inset-block-end: auto; inset-block-start: auto; inset-inline-end: auto; inset-inline-start: auto; isolation: auto; justify-content: normal; justify-items: normal; justify-self: 
 auto; kerning: 0; left: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: normal; list-style-image: none; list-style-position: outside; list-style-type: disc; margin-block-end: 0px; margin-block-start: 0px; margin-bottom: 0px; margin-inline-end: 0px; margin-inline-start: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; marker-end: none; marker-mid: none; marker-start: none; mask: none; mask-type: luminance; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; opacity: 1; order: 0; orphans: auto; outline-color: rgb(255, 0, 0); outline-offset: 0px; outline-style: none; outline-width: 0px; overflow-wrap: normal; overflow-x: visible; overflow-y: visible; overscroll-behavior-x: auto; overscroll-behavior-y: auto; padding-block-end: 0px; padding-
 block-start: 0px; padding-bottom: 0px; padding-inline-end: 0px; padding-inline-start: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; page-break-after: auto; page-break-before: auto; page-break-inside: auto; paint-order: normal; perspective: none; perspective-origin-x: ; perspective-origin-y: ; pointer-events: auto; position: static; quotes: auto; r: 0px; resize: none; right: auto; rotate: none; row-gap: normal; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-margin-block: 0px; scroll-margin-bottom: 0px; scroll-margin-inline: 0px; scroll-margin-left: 0px; scroll-margin-right: 0px; scroll-margin-top: 0px; scroll-padding-block: auto; scroll-padding-bottom: auto; scroll-padding-inline: auto; scroll-padding-left: auto; scroll-padding-right: auto; scroll-padding-top: auto; scroll-snap-align: none; scroll-snap-type: none; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; size: ; speak-as: normal; stop-color: rgb(0, 0, 0);
  stop-opacity: 1; stroke: none; stroke-color: rgba(0, 0, 0, 0); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 8; table-layout: auto; text-align: start; text-anchor: start; text-decoration: none; text-decoration-color: rgb(255, 0, 0); text-decoration-line: none; text-decoration-skip: auto; text-decoration-style: solid; text-decoration-thickness: auto; text-indent: 0px; text-orientation: mixed; text-overflow: clip; text-rendering: auto; text-shadow: none; text-transform: none; text-underline-offset: auto; text-underline-position: auto; top: auto; touch-action: auto; transform: none; transform-box: view-box; transform-origin-x: ; transform-origin-y: ; transform-origin-z: ; transform-style: flat; transition-delay: 0s; transition-duration: 0s; transition-property: all; transition-timing-function: ease; translate: none; unicode-bidi: normal; vector-effect: none; vertical-a
 lign: baseline; visibility: visible; white-space: normal; widows: auto; width: 784px; will-change: auto; word-break: normal; word-spacing: 0px; word-wrap: normal; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; -apple-color-filter: none; -apple-pay-button-style: black; -apple-pay-button-type: plain; -apple-trailing-word: auto; -webkit-appearance: none; -webkit-backdrop-filter: none; -webkit-backface-visibility: visible; -webkit-background-clip: border-box; -webkit-background-composite: source-over; -webkit-background-origin: padding-box; -webkit-background-size: auto; -webkit-border-fit: border; -webkit-border-horizontal-spacing: 0px; -webkit-border-image: none; -webkit-border-vertical-spacing: 0px; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-flex-group: 1; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-b
 ox-reflect: none; -webkit-box-shadow: none; -webkit-column-axis: auto; -webkit-column-break-after: auto; -webkit-column-break-before: auto; -webkit-column-break-inside: auto; -webkit-column-progression: normal; -webkit-cursor-visibility: auto; -webkit-font-kerning: auto; -webkit-font-smoothing: auto; -webkit-hyphenate-character: auto; -webkit-hyphenate-limit-after: auto; -webkit-hyphenate-limit-before: auto; -webkit-hyphenate-limit-lines: no-limit; -webkit-hyphens: manual; -webkit-initial-letter: normal; -webkit-line-align: none; -webkit-line-box-contain: block inline replaced; -webkit-line-clamp: none; -webkit-line-grid: none; -webkit-line-snap: none; -webkit-locale: auto; -webkit-margin-after-collapse: collapse; -webkit-margin-before-collapse: collapse; -webkit-margin-bottom-collapse: collapse; -webkit-margin-top-collapse: collapse; -webkit-mask-box-image: none; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webki
 t-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-clip: border-box; -webkit-mask-composite: source-over; -webkit-mask-image: none; -webkit-mask-origin: border-box; -webkit-mask-position-x: 0%; -webkit-mask-position-y: 0%; -webkit-mask-repeat: repeat; -webkit-mask-size: auto; -webkit-mask-source-type: alpha; -webkit-nbsp-mode: normal; -webkit-overflow-scrolling: auto; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-ruby-position: before; -webkit-text-combine: none; -webkit-text-emphasis-color: rgb(255, 0, 0); -webkit-text-emphasis-position: over right; -webkit-text-emphasis-style: none; -webkit-text-fill-color: rgb(255, 0, 0); -webkit-text-orientation: mixed; -webkit-text-security: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-color: rgb(255, 0, 0); -webkit-text-stroke-width: 0px; -webkit-text-zoom: normal; -webkit-touch-callout: default; -webkit-transform-style: flat; -webkit-user-drag: auto; -webkit-user-modify: 
 read-only; -webkit-user-select: text;"
+FAIL cssText on computed style declaration returns the empty string assert_equals: cssText is empty expected "" but got "align-content: normal; align-items: normal; align-self: auto; alignment-baseline: auto; all: ; alt: \"\"; animation-delay: 0s; animation-direction: normal; animation-duration: 0s; animation-fill-mode: none; animation-iteration-count: 1; animation-name: none; animation-play-state: running; animation-timing-function: ease; aspect-ratio: auto; background-attachment: scroll; background-blend-mode: normal; background-clip: border-box; background-color: rgba(0, 0, 0, 0); background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; baseline-shift: baseline; block-size: 0px; border-block-end-color: rgb(255, 0, 0); border-block-end-style: none; border-block-end-width: 0px; border-block-start-color: rgb(255, 0, 0); border-block-start-style: 
 none; border-block-start-width: 0px; border-bottom-color: rgb(255, 0, 0); border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom-style: none; border-bottom-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image-outset: 0px; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-inline-end-color: rgb(255, 0, 0); border-inline-end-style: none; border-inline-end-width: 0px; border-inline-start-color: rgb(255, 0, 0); border-inline-start-style: none; border-inline-start-width: 0px; border-left-color: rgb(255, 0, 0); border-left-style: none; border-left-width: 0px; border-right-color: rgb(255, 0, 0); border-right-style: none; border-right-width: 0px; border-start-end-radius: 0px; border-start-start-radius: 0px; border-top-color: rgb(255, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-top-style: none; border-top-width: 0px; botto
 m: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgb(255, 0, 0); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgb(255, 0, 0); color-interpolation: sRGB; color-interpolation-filters: linearRGB; color-rendering: auto; color-scheme: auto; column-count: auto; column-fill: balance; column-gap: normal; column-rule-color: rgb(255, 0, 0); column-rule-style: none; column-rule-width: 0px; column-span: none; column-width: auto; content: ; counter-increment: none; counter-reset: none; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: block; dominant-baseline: auto; empty-cells: show; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex-basis: auto; flex-direction: row; flex-grow: 0; flex-shrink: 1; flex-wrap: nowrap; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: -webkit-standard; font-feature-settin
 gs: normal; font-optical-sizing: auto; font-size: 13.333333015441895px; font-stretch: normal; font-style: normal; font-synthesis: style weight small-caps; font-variant-alternates: normal; font-variant-caps: normal; font-variant-east-asian: normal; font-variant-ligatures: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; glyph-orientation-horizontal: 0deg; glyph-orientation-vertical: auto; grid-auto-columns: auto; grid-auto-flow: row; grid-auto-rows: auto; grid-column-end: auto; grid-column-start: auto; grid-row-end: auto; grid-row-start: auto; grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; hanging-punctuation: none; height: 0px; image-orientation: from-image; image-rendering: auto; inline-size: 784px; inset-block-end: auto; inset-block-start: auto; inset-inline-end: auto; inset-inline-start: auto; isolation: auto; justify-content: normal; justify-items: normal; justify-self: a
 uto; kerning: 0; left: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: normal; list-style-image: none; list-style-position: outside; list-style-type: disc; margin-block-end: 0px; margin-block-start: 0px; margin-bottom: 0px; margin-inline-end: 0px; margin-inline-start: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; marker-end: none; marker-mid: none; marker-start: none; mask: none; mask-type: luminance; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; opacity: 1; order: 0; orphans: auto; outline-color: rgb(255, 0, 0); outline-offset: 0px; outline-style: none; outline-width: 0px; overflow-wrap: normal; overflow-x: visible; overflow-y: visible; overscroll-behavior-x: auto; overscroll-behavior-y: auto; padding-block-end: 0px; padding-b
 lock-start: 0px; padding-bottom: 0px; padding-inline-end: 0px; padding-inline-start: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; page-break-after: auto; page-break-before: auto; page-break-inside: auto; paint-order: normal; perspective: none; perspective-origin-x: ; perspective-origin-y: ; pointer-events: auto; position: static; quotes: auto; r: 0px; resize: none; right: auto; rotate: none; row-gap: normal; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-margin-block: 0px; scroll-margin-bottom: 0px; scroll-margin-inline: 0px; scroll-margin-left: 0px; scroll-margin-right: 0px; scroll-margin-top: 0px; scroll-padding-block: auto; scroll-padding-bottom: auto; scroll-padding-inline: auto; scroll-padding-left: auto; scroll-padding-right: auto; scroll-padding-top: auto; scroll-snap-align: none; scroll-snap-stop: normal; scroll-snap-type: none; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; size: ; speak-as: normal; 
 stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: none; stroke-color: rgba(0, 0, 0, 0); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 8; table-layout: auto; text-align: start; text-anchor: start; text-decoration: none; text-decoration-color: rgb(255, 0, 0); text-decoration-line: none; text-decoration-skip: auto; text-decoration-style: solid; text-decoration-thickness: auto; text-indent: 0px; text-orientation: mixed; text-overflow: clip; text-rendering: auto; text-shadow: none; text-transform: none; text-underline-offset: auto; text-underline-position: auto; top: auto; touch-action: auto; transform: none; transform-box: view-box; transform-origin-x: ; transform-origin-y: ; transform-origin-z: ; transform-style: flat; transition-delay: 0s; transition-duration: 0s; transition-property: all; transition-timing-function: ease; translate: none; unicode-bidi: normal; vector
 -effect: none; vertical-align: baseline; visibility: visible; white-space: normal; widows: auto; width: 784px; will-change: auto; word-break: normal; word-spacing: 0px; word-wrap: normal; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; -apple-color-filter: none; -apple-pay-button-style: black; -apple-pay-button-type: plain; -apple-trailing-word: auto; -webkit-appearance: none; -webkit-backdrop-filter: none; -webkit-backface-visibility: visible; -webkit-background-clip: border-box; -webkit-background-composite: source-over; -webkit-background-origin: padding-box; -webkit-background-size: auto; -webkit-border-fit: border; -webkit-border-horizontal-spacing: 0px; -webkit-border-image: none; -webkit-border-vertical-spacing: 0px; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-flex-group: 1; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-b
 ox-pack: start; -webkit-box-reflect: none; -webkit-box-shadow: none; -webkit-column-axis: auto; -webkit-column-break-after: auto; -webkit-column-break-before: auto; -webkit-column-break-inside: auto; -webkit-column-progression: normal; -webkit-cursor-visibility: auto; -webkit-font-kerning: auto; -webkit-font-smoothing: auto; -webkit-hyphenate-character: auto; -webkit-hyphenate-limit-after: auto; -webkit-hyphenate-limit-before: auto; -webkit-hyphenate-limit-lines: no-limit; -webkit-hyphens: manual; -webkit-initial-letter: normal; -webkit-line-align: none; -webkit-line-box-contain: block inline replaced; -webkit-line-clamp: none; -webkit-line-grid: none; -webkit-line-snap: none; -webkit-locale: auto; -webkit-margin-after-collapse: collapse; -webkit-margin-before-collapse: collapse; -webkit-margin-bottom-collapse: collapse; -webkit-margin-top-collapse: collapse; -webkit-mask-box-image: none; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-im
 age-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-clip: border-box; -webkit-mask-composite: source-over; -webkit-mask-image: none; -webkit-mask-origin: border-box; -webkit-mask-position-x: 0%; -webkit-mask-position-y: 0%; -webkit-mask-repeat: repeat; -webkit-mask-size: auto; -webkit-mask-source-type: alpha; -webkit-nbsp-mode: normal; -webkit-overflow-scrolling: auto; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-ruby-position: before; -webkit-text-combine: none; -webkit-text-emphasis-color: rgb(255, 0, 0); -webkit-text-emphasis-position: over right; -webkit-text-emphasis-style: none; -webkit-text-fill-color: rgb(255, 0, 0); -webkit-text-orientation: mixed; -webkit-text-security: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-color: rgb(255, 0, 0); -webkit-text-stroke-width: 0px; -webkit-text-zoom: normal; -webkit-touch-callout: default; -webkit-transform-style: flat; -webkit-user-drag: au
 to; -webkit-user-modify: read-only; -webkit-user-select: text;"
 

Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (272609 => 272610)


--- trunk/LayoutTests/platform/mac-wk1/TestExpectations	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations	2021-02-09 21:52:39 UTC (rev 272610)
@@ -450,6 +450,7 @@
 
 # WebKit1 frames use native scrollbars causing these tests to fail.
 imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-target-padding-001.html [ ImageOnlyFailure ]
+imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change.html [ Failure ]
 css3/scroll-snap/scroll-padding-mainframe-paging.html [ Failure ]
 
 ### END OF (2) Failures without bug reports

Modified: trunk/Source/WebCore/ChangeLog (272609 => 272610)


--- trunk/Source/WebCore/ChangeLog	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/ChangeLog	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,3 +1,70 @@
+2021-02-09  Martin Robinson  <[email protected]>
+
+        Implement scroll-snap-stop for scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=197744
+        <rdar://problem/50708356>
+
+        Reviewed by Simon Fraser.
+
+        No new tests. This is tested by existing, imported WPT tests:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/inheritance-expected.txt:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed-expected.txt:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid-expected.txt:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change-expected.txt:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-expected.txt:
+            imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt:
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::ComputedStyleExtractor::valueForPropertyInStyle): Add support the new CSS property.
+        * css/CSSPrimitiveValueMappings.h:
+        (WebCore::CSSPrimitiveValue::operator ScrollSnapAxisAlignType const): Ditto.
+        (WebCore::CSSPrimitiveValue::CSSPrimitiveValue): Ditto.
+        (WebCore::CSSPrimitiveValue::operator ScrollSnapStop const): ditto.
+        * css/CSSProperties.json: Ditto.
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::CSSPropertyParser::parseSingleValue): Ditto.
+        * page/scrolling/ScrollSnapOffsetsInfo.cpp:
+        (WebCore::indicesOfNearestSnapOffsets): Update to accept SnapOffset instead of a raw
+        value.
+        (WebCore::findFirstSnapStopOffsetBetweenOriginAndDestination): Added this helper which looks
+        between the scroll origin and destination to find a candidate scroll offset with ScrollSnapStop::Always.
+        This offset will always be chosen for directional scrolls.
+        (WebCore::closestSnapOffsetWithOffsetsAndRanges): Use new helper to find offsets with ScrollSnapStop::Always.
+        (WebCore::computeAxisProximitySnapOffsetRanges): Deal with SnapOffset instead of raw values.
+        (WebCore::updateSnapOffsetsForScrollableArea): Ditto.
+        (WebCore::convertOffsetInfo): Ditto.
+        * page/scrolling/ScrollSnapOffsetsInfo.h:
+        (WebCore::ScrollSnapOffsetsInfo::offsetsForAxis const): Ditto.
+        (WebCore::operator<<): Consolidated all TextStream implementations here instead of repeating
+        them throughout the code.
+        * platform/ScrollableArea.cpp:
+        (WebCore::ScrollableArea::setScrollSnapOffsetInfo): Set the scroll snap offset info all at once.
+        (WebCore::ScrollableArea::clearSnapOffsets): We no longer clear horizontal and vertical offsets separately.
+        (WebCore::ScrollableArea::nearestActiveSnapPoint): Access the offset value.
+        * platform/ScrollableArea.h:
+        * platform/cocoa/ScrollController.mm:
+        (WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset): Ditto.
+        * platform/cocoa/ScrollSnapAnimatorState.h:
+        (WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis const): Deal with SnapOffsets instead of raw values.
+        (WebCore::ScrollSnapAnimatorState::setSnapOffsetsAndPositionRangesForAxis): Ditto.
+        * platform/cocoa/ScrollSnapAnimatorState.mm:
+        * rendering/RenderLayerModelObject.cpp:
+        (WebCore::RenderLayerModelObject::styleDidChange): Update scroll snap information when the `scroll-snap-stop`
+        property changes. Also ensure that we update everything for FrameView's instead of only some properties.
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::initialScrollSnapStop): Added for new CSS property.
+        (WebCore::RenderStyle::scrollSnapStop const): Ditto.
+        (WebCore::RenderStyle::setScrollSnapStop): Ditto.
+        * rendering/style/RenderStyle.h:
+        * rendering/style/RenderStyleConstants.cpp:
+        (WebCore::operator<<): Ditto.
+        * rendering/style/RenderStyleConstants.h:
+        * rendering/style/StyleRareNonInheritedData.h:
+        * style/StyleBuilderConverter.h:
+        (WebCore::Style::BuilderConverter::convertScrollSnapStop): Ditto.
+        * testing/Internals.cpp:
+        (WebCore::appendOffsets): Handle the case where we are printing an offset with ScrollSnapStop::Always.
+
 2021-02-09  Chris Dumez  <[email protected]>
 
         Disallow alert/confirm/prompt in cross-origin-domain subframes

Modified: trunk/Source/WebCore/css/CSSComputedStyleDeclaration.cpp (272609 => 272610)


--- trunk/Source/WebCore/css/CSSComputedStyleDeclaration.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/css/CSSComputedStyleDeclaration.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -3847,10 +3847,12 @@
         case CSSPropertyScrollPaddingInline:
             return getCSSPropertyValuesFor2SidesShorthand(scrollPaddingInlineShorthand());
 #if ENABLE(CSS_SCROLL_SNAP)
+        case CSSPropertyScrollSnapAlign:
+            return valueForScrollSnapAlignment(style.scrollSnapAlign());
+        case CSSPropertyScrollSnapStop:
+            return CSSPrimitiveValue::create(style.scrollSnapStop());
         case CSSPropertyScrollSnapType:
             return valueForScrollSnapType(style.scrollSnapType());
-        case CSSPropertyScrollSnapAlign:
-            return valueForScrollSnapAlignment(style.scrollSnapAlign());
 #endif
 
 #if ENABLE(CSS_TRAILING_WORD)

Modified: trunk/Source/WebCore/css/CSSPrimitiveValueMappings.h (272609 => 272610)


--- trunk/Source/WebCore/css/CSSPrimitiveValueMappings.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/css/CSSPrimitiveValueMappings.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -5407,6 +5407,34 @@
     }
 }
 
+template<> inline CSSPrimitiveValue::CSSPrimitiveValue(ScrollSnapStop snapStop)
+    : CSSValue(PrimitiveClass)
+{
+    setPrimitiveUnitType(CSSUnitType::CSS_VALUE_ID);
+    switch (snapStop) {
+    case ScrollSnapStop::Normal:
+        m_value.valueID = CSSValueNormal;
+        break;
+    case ScrollSnapStop::Always:
+        m_value.valueID = CSSValueAlways;
+        break;
+    }
+}
+
+template<> inline CSSPrimitiveValue::operator ScrollSnapStop() const
+{
+    ASSERT(isValueID());
+    switch (m_value.valueID) {
+    case CSSValueNormal:
+        return ScrollSnapStop::Normal;
+    case CSSValueAlways:
+        return ScrollSnapStop::Always;
+    default:
+        ASSERT_NOT_REACHED();
+        return ScrollSnapStop::Normal;
+    }
+}
+
 #endif
 
 #if ENABLE(CSS_TRAILING_WORD)

Modified: trunk/Source/WebCore/css/CSSProperties.json (272609 => 272610)


--- trunk/Source/WebCore/css/CSSProperties.json	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/css/CSSProperties.json	2021-02-09 21:52:39 UTC (rev 272610)
@@ -6989,6 +6989,16 @@
                 "url": "https://www.w3.org/TR/css-scroll-snap-1/#propdef-scroll-snap-margin-inline-end"
             }
         },
+        "scroll-snap-stop": {
+            "codegen-properties": {
+                "converter": "ScrollSnapStop",
+                "enable-if": "ENABLE_CSS_SCROLL_SNAP"
+            },
+            "specification": {
+                "category": "css-scroll-snap",
+                "url": "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-stop"
+            }
+        },
         "shape-outside": {
             "codegen-properties": {
                 "aliases": [

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp (272609 => 272610)


--- trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -4175,6 +4175,8 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     case CSSPropertyScrollSnapAlign:
         return consumeScrollSnapAlign(m_range);
+    case CSSPropertyScrollSnapStop:
+        return consumeIdent<CSSValueAlways, CSSValueNormal>(m_range);
     case CSSPropertyScrollSnapType:
         return consumeScrollSnapType(m_range);
 #endif

Modified: trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp (272609 => 272610)


--- trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -79,13 +79,13 @@
 }
 
 template <typename LayoutType>
-static void indicesOfNearestSnapOffsets(LayoutType offset, const Vector<LayoutType>& snapOffsets, unsigned& lowerIndex, unsigned& upperIndex)
+static void indicesOfNearestSnapOffsets(LayoutType offset, const Vector<SnapOffset<LayoutType>>& snapOffsets, unsigned& lowerIndex, unsigned& upperIndex)
 {
     lowerIndex = 0;
     upperIndex = snapOffsets.size() - 1;
     while (lowerIndex < upperIndex - 1) {
         int middleIndex = (lowerIndex + upperIndex) / 2;
-        auto middleOffset = snapOffsets[middleIndex];
+        auto middleOffset = snapOffsets[middleIndex].offset;
         if (offset == middleOffset) {
             upperIndex = middleIndex;
             lowerIndex = middleIndex;
@@ -100,11 +100,48 @@
 }
 
 template <typename LayoutType>
-static std::pair<LayoutType, unsigned> closestSnapOffsetWithOffsetsAndRanges(const Vector<LayoutType>& snapOffsets, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, LayoutType scrollDestinationOffset, float velocity, Optional<LayoutType> originalOffsetForDirectionalSnapping)
+static Optional<unsigned> findFirstSnapStopOffsetBetweenOriginAndDestination(const Vector<SnapOffset<LayoutType>>& snapOffsets, LayoutType scrollOriginOffset, LayoutType scrollDestinationOffset)
 {
+    LayoutType difference = scrollDestinationOffset - scrollOriginOffset;
+    if (!difference)
+        return WTF::nullopt;
+
+    unsigned searchStartOffset = 0;
+    size_t iteration = 1;
+    if (difference < 0) {
+        searchStartOffset = snapOffsets.size() - 1;
+        iteration = -1;
+    }
+
+    auto isPast = [difference](LayoutType mark, LayoutType candidate) {
+        return (difference > 0 && candidate > mark) || (difference < 0 && candidate < mark);
+    };
+
+    for (size_t i = searchStartOffset; i >= 0 && i < snapOffsets.size(); i += iteration) {
+        auto offset = snapOffsets[i].offset;
+        if (isPast(scrollDestinationOffset, offset))
+            break;
+        if (snapOffsets[i].stop != ScrollSnapStop::Always)
+            continue;
+        if (isPast(scrollOriginOffset, offset))
+            return i;
+    }
+
+    return WTF::nullopt;
+}
+
+template <typename LayoutType>
+static std::pair<LayoutType, unsigned> closestSnapOffsetWithOffsetsAndRanges(const Vector<SnapOffset<LayoutType>>& snapOffsets, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, LayoutType scrollDestinationOffset, float velocity, Optional<LayoutType> originalOffsetForDirectionalSnapping)
+{
     if (snapOffsets.isEmpty())
         return std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
 
+    if (originalOffsetForDirectionalSnapping.hasValue()) {
+        auto firstSnapStopOffsetIndex = findFirstSnapStopOffsetBetweenOriginAndDestination(snapOffsets, *originalOffsetForDirectionalSnapping, scrollDestinationOffset);
+        if (firstSnapStopOffsetIndex.hasValue())
+            return std::make_pair(snapOffsets[*firstSnapStopOffsetIndex].offset, *firstSnapStopOffsetIndex);
+    }
+
     unsigned lowerSnapOffsetRangeIndex;
     unsigned upperSnapOffsetRangeIndex;
     indicesOfNearestSnapOffsetRanges<LayoutType>(scrollDestinationOffset, snapOffsetRanges, lowerSnapOffsetRangeIndex, upperSnapOffsetRangeIndex);
@@ -111,17 +148,17 @@
     if (lowerSnapOffsetRangeIndex == upperSnapOffsetRangeIndex && upperSnapOffsetRangeIndex != invalidSnapOffsetIndex)
         return std::make_pair(scrollDestinationOffset, invalidSnapOffsetIndex);
 
-    if (scrollDestinationOffset <= snapOffsets.first())
-        return std::make_pair(snapOffsets.first(), 0u);
+    if (scrollDestinationOffset <= snapOffsets.first().offset)
+        return std::make_pair(snapOffsets.first().offset, 0u);
 
-    if (scrollDestinationOffset >= snapOffsets.last())
-        return std::make_pair(snapOffsets.last(), snapOffsets.size() - 1);
+    if (scrollDestinationOffset >= snapOffsets.last().offset)
+        return std::make_pair(snapOffsets.last().offset, snapOffsets.size() - 1);
 
     unsigned lowerIndex;
     unsigned upperIndex;
     indicesOfNearestSnapOffsets<LayoutType>(scrollDestinationOffset, snapOffsets, lowerIndex, upperIndex);
-    LayoutType lowerSnapPosition = snapOffsets[lowerIndex];
-    LayoutType upperSnapPosition = snapOffsets[upperIndex];
+    LayoutType lowerSnapPosition = snapOffsets[lowerIndex].offset;
+    LayoutType upperSnapPosition = snapOffsets[upperIndex].offset;
     if (!std::abs(velocity)) {
         bool isCloserToLowerSnapPosition = scrollDestinationOffset - lowerSnapPosition <= upperSnapPosition - scrollDestinationOffset;
         return isCloserToLowerSnapPosition ? std::make_pair(lowerSnapPosition, lowerIndex) : std::make_pair(upperSnapPosition, upperIndex);
@@ -182,15 +219,8 @@
     }
 }
 
-template<typename T>
-TextStream& operator<<(TextStream& ts, const ScrollOffsetRange<T>& range)
+static void computeAxisProximitySnapOffsetRanges(const Vector<SnapOffset<LayoutUnit>>& snapOffsets, Vector<ScrollOffsetRange<LayoutUnit>>& offsetRanges, LayoutUnit scrollPortAxisLength)
 {
-    ts << "start: " << range.start << " end: " << range.end;
-    return ts;
-}
-
-static void computeAxisProximitySnapOffsetRanges(const Vector<LayoutUnit>& snapOffsets, Vector<ScrollOffsetRange<LayoutUnit>>& offsetRanges, LayoutUnit scrollPortAxisLength)
-{
     // This is an arbitrary choice for what it means to be "in proximity" of a snap offset. We should play around with
     // this and see what feels best.
     static const float ratioOfScrollPortAxisLengthToBeConsideredForProximity = 0.3;
@@ -206,8 +236,8 @@
     // with to see what feels best.
     LayoutUnit proximityDistance { ratioOfScrollPortAxisLengthToBeConsideredForProximity * scrollPortAxisLength };
     for (size_t index = 1; index < snapOffsets.size(); ++index) {
-        auto startOffset = snapOffsets[index - 1] + proximityDistance;
-        auto endOffset = snapOffsets[index] - proximityDistance;
+        auto startOffset = snapOffsets[index - 1].offset + proximityDistance;
+        auto endOffset = snapOffsets[index].offset - proximityDistance;
         if (startOffset < endOffset)
             offsetRanges.append({ startOffset, endOffset });
     }
@@ -222,12 +252,16 @@
         return;
     }
 
-    Vector<LayoutUnit> verticalSnapOffsets;
-    Vector<LayoutUnit> horizontalSnapOffsets;
-    Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges;
-    Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges;
-    HashSet<float> seenVerticalSnapOffsets;
-    HashSet<float> seenHorizontalSnapOffsets;
+    auto addOrUpdateStopForSnapOffset = [](HashMap<float, SnapOffset<LayoutUnit>>& offsets, SnapOffset<LayoutUnit> newOffset)
+    {
+        // If the offset already exists, we ensure that it has ScrollSnapStop::Always, when appropriate.
+        auto addResult = offsets.add(newOffset.offset, newOffset);
+        if (newOffset.stop == ScrollSnapStop::Always)
+            addResult.iterator->value.stop = ScrollSnapStop::Always;
+    };
+
+    HashMap<float, SnapOffset<LayoutUnit>> verticalSnapOffsetsMap;
+    HashMap<float, SnapOffset<LayoutUnit>> horizontalSnapOffsetsMap;
     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;
 
@@ -255,49 +289,52 @@
         scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollMargin(), InsetOrOutset::Outset);
         LOG_WITH_STREAM(ScrollSnap, stream << "    Considering scroll snap target area " << scrollSnapArea);
         auto alignment = child->style().scrollSnapAlign();
+        auto stop = child->style().scrollSnapStop();
         if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) {
             auto absoluteScrollXPosition = computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.maxX(), alignment.x, scrollerIsRTL) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.maxX(), alignment.x, scrollerIsRTL);
             auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ roundToInt(absoluteScrollXPosition), 0 }).x(), 0, maxScrollOffset.x());
-            if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) {
-                seenHorizontalSnapOffsets.add(absoluteScrollOffset);
-                horizontalSnapOffsets.append(absoluteScrollOffset);
-            }
+            addOrUpdateStopForSnapOffset(horizontalSnapOffsetsMap, { absoluteScrollOffset, stop });
         }
         if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) {
             auto absoluteScrollYPosition = computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.maxY(), alignment.y, false) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.maxY(), alignment.y, false);
             auto absoluteScrollOffset = clampTo<int>(scrollableArea.scrollOffsetFromPosition({ 0, roundToInt(absoluteScrollYPosition) }).y(), 0, maxScrollOffset.y());
-            if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) {
-                seenVerticalSnapOffsets.add(absoluteScrollOffset);
-                verticalSnapOffsets.append(absoluteScrollOffset);
-            }
+            addOrUpdateStopForSnapOffset(verticalSnapOffsetsMap, { absoluteScrollOffset, stop });
         }
     }
 
+    auto compareSnapOffsets = [](const SnapOffset<LayoutUnit>& a, const SnapOffset<LayoutUnit>& b)
+    {
+        return a.offset < b.offset;
+    };
+
+    Vector<SnapOffset<LayoutUnit>> horizontalSnapOffsets = copyToVector(horizontalSnapOffsetsMap.values());
+    Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges;
     if (!horizontalSnapOffsets.isEmpty()) {
-        std::sort(horizontalSnapOffsets.begin(), horizontalSnapOffsets.end());
+        std::sort(horizontalSnapOffsets.begin(), horizontalSnapOffsets.end(), compareSnapOffsets);
         if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
             computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width());
 
         LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offsets: " << horizontalSnapOffsets);
         LOG_WITH_STREAM(ScrollSnap, stream << " => Computed horizontal scroll snap offset ranges: " << horizontalSnapOffsetRanges);
+    }
 
-        scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets);
-        scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges);
-    } else
-        scrollableArea.clearHorizontalSnapOffsets();
-
+    Vector<SnapOffset<LayoutUnit>> verticalSnapOffsets = copyToVector(verticalSnapOffsetsMap.values());
+    Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges;
     if (!verticalSnapOffsets.isEmpty()) {
-        std::sort(verticalSnapOffsets.begin(), verticalSnapOffsets.end());
+        std::sort(verticalSnapOffsets.begin(), verticalSnapOffsets.end(), compareSnapOffsets);
         if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
             computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height());
 
         LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offsets: " << verticalSnapOffsets);
         LOG_WITH_STREAM(ScrollSnap, stream << " => Computed vertical scroll snap offset ranges: " << verticalSnapOffsetRanges);
+    }
 
-        scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets);
-        scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges);
-    } else
-        scrollableArea.clearVerticalSnapOffsets();
+    scrollableArea.setScrollSnapOffsetInfo({
+        horizontalSnapOffsets,
+        verticalSnapOffsets,
+        horizontalSnapOffsetRanges,
+        verticalSnapOffsetRanges
+    });
 }
 
 static float convertOffsetUnit(LayoutUnit input, float deviceScaleFactor)
@@ -313,12 +350,12 @@
 template <typename InputType, typename OutputType>
 static ScrollSnapOffsetsInfo<OutputType> convertOffsetInfo(const ScrollSnapOffsetsInfo<InputType>& input, float scaleFactor = 0.0)
 {
-    auto convertOffsets = [scaleFactor](const Vector<InputType>& input)
+    auto convertOffsets = [scaleFactor](const Vector<SnapOffset<InputType>>& input)
     {
-        Vector<OutputType> output;
+        Vector<SnapOffset<OutputType>> output;
         output.reserveInitialCapacity(input.size());
         for (auto& offset : input)
-            output.uncheckedAppend(convertOffsetUnit(offset, scaleFactor));
+            output.uncheckedAppend({ convertOffsetUnit(offset.offset, scaleFactor), offset.stop });
         return output;
     };
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h (272609 => 272610)


--- trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -32,7 +32,6 @@
 #include "StyleScrollSnapPoints.h"
 #include <utility>
 #include <wtf/Vector.h>
-#include <wtf/Vector.h>
 
 namespace WebCore {
 
@@ -42,6 +41,12 @@
 class RenderStyle;
 
 template <typename T>
+struct SnapOffset {
+    T offset;
+    ScrollSnapStop stop;
+};
+
+template <typename T>
 struct ScrollOffsetRange {
     T start;
     T end;
@@ -50,8 +55,8 @@
 template <typename T>
 struct ScrollSnapOffsetsInfo {
     WTF_MAKE_STRUCT_FAST_ALLOCATED;
-    Vector<T> horizontalSnapOffsets;
-    Vector<T> verticalSnapOffsets;
+    Vector<SnapOffset<T>> horizontalSnapOffsets;
+    Vector<SnapOffset<T>> verticalSnapOffsets;
 
     // Snap offset ranges represent non-empty ranges of scroll offsets in which scrolling may rest after scroll snapping.
     // These are used in two cases: (1) for proximity scroll snapping, where portions of areas between adjacent snap offsets
@@ -70,7 +75,7 @@
         return horizontalSnapOffsets.isEmpty() && verticalSnapOffsets.isEmpty();
     }
 
-    Vector<T> offsetsForAxis(ScrollEventAxis axis) const
+    Vector<SnapOffset<T>> offsetsForAxis(ScrollEventAxis axis) const
     {
         return axis == ScrollEventAxis::Vertical ? verticalSnapOffsets : horizontalSnapOffsets;
     }
@@ -101,6 +106,21 @@
 // the scrolling container's border box.
 void updateSnapOffsetsForScrollableArea(ScrollableArea&, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle, LayoutRect viewportRectInBorderBoxCoordinates);
 
+template <typename T> WTF::TextStream& operator<<(WTF::TextStream& ts, SnapOffset<T> offset)
+{
+    ts << offset.offset;
+    if (offset.stop == ScrollSnapStop::Always)
+        ts << " (always)";
+    return ts;
+}
+
+template<typename T>
+TextStream& operator<<(TextStream& ts, const ScrollOffsetRange<T>& range)
+{
+    ts << "start: " << range.start << " end: " << range.end;
+    return ts;
+}
+
 }; // namespace WebCore
 
 #endif // ENABLE(CSS_SCROLL_SNAP)

Modified: trunk/Source/WebCore/platform/ScrollableArea.cpp (272609 => 272610)


--- trunk/Source/WebCore/platform/ScrollableArea.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/platform/ScrollableArea.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -487,60 +487,23 @@
     return m_snapOffsetsInfo.get();
 }
 
-void ScrollableArea::setHorizontalSnapOffsets(const Vector<LayoutUnit>& horizontalSnapOffsets)
+void ScrollableArea::setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>& info)
 {
-    // Consider having a non-empty set of snap offsets as a cue to initialize the ScrollAnimator.
-    if (horizontalSnapOffsets.size())
-        scrollAnimator();
+    if (info.isEmpty()) {
+        clearSnapOffsets();
+        return;
+    }
 
-    ensureSnapOffsetsInfo().horizontalSnapOffsets = horizontalSnapOffsets;
-}
-
-void ScrollableArea::setVerticalSnapOffsets(const Vector<LayoutUnit>& verticalSnapOffsets)
-{
     // Consider having a non-empty set of snap offsets as a cue to initialize the ScrollAnimator.
-    if (verticalSnapOffsets.size())
-        scrollAnimator();
-
-    ensureSnapOffsetsInfo().verticalSnapOffsets = verticalSnapOffsets;
+    scrollAnimator();
+    ensureSnapOffsetsInfo() = info;
 }
 
-void ScrollableArea::setHorizontalSnapOffsetRanges(const Vector<ScrollOffsetRange<LayoutUnit>>& horizontalRanges)
-{
-    ensureSnapOffsetsInfo().horizontalSnapOffsetRanges = horizontalRanges;
-}
-
-void ScrollableArea::setVerticalSnapOffsetRanges(const Vector<ScrollOffsetRange<LayoutUnit>>& verticalRanges)
-{
-    ensureSnapOffsetsInfo().verticalSnapOffsetRanges = verticalRanges;
-}
-
 void ScrollableArea::clearSnapOffsets()
 {
-    clearHorizontalSnapOffsets();
-    clearVerticalSnapOffsets();
+    m_snapOffsetsInfo = nullptr;
 }
 
-void ScrollableArea::clearHorizontalSnapOffsets()
-{
-    if (!m_snapOffsetsInfo)
-        return;
-
-    m_snapOffsetsInfo->horizontalSnapOffsets = { };
-    m_snapOffsetsInfo->horizontalSnapOffsetRanges = { };
-    m_currentHorizontalSnapPointIndex = 0;
-}
-
-void ScrollableArea::clearVerticalSnapOffsets()
-{
-    if (!m_snapOffsetsInfo)
-        return;
-
-    m_snapOffsetsInfo->verticalSnapOffsets = { };
-    m_snapOffsetsInfo->verticalSnapOffsetRanges = { };
-    m_currentVerticalSnapPointIndex = 0;
-}
-
 bool ScrollableArea::usesScrollSnap() const
 {
     return !!m_snapOffsetsInfo;
@@ -559,12 +522,12 @@
     const auto& horizontal = m_snapOffsetsInfo->horizontalSnapOffsets;
     size_t activeHorizontalIndex = currentHorizontalSnapPointIndex();
     if (activeHorizontalIndex < horizontal.size())
-        correctedPosition.setX(horizontal[activeHorizontalIndex].toInt());
+        correctedPosition.setX(horizontal[activeHorizontalIndex].offset.toInt());
 
     const auto& vertical = m_snapOffsetsInfo->verticalSnapOffsets;
     size_t activeVerticalIndex = currentVerticalSnapPointIndex();
     if (activeVerticalIndex < vertical.size())
-        correctedPosition.setY(vertical[activeVerticalIndex].toInt());
+        correctedPosition.setY(vertical[activeVerticalIndex].offset.toInt());
 
     return correctedPosition;
 }

Modified: trunk/Source/WebCore/platform/ScrollableArea.h (272609 => 272610)


--- trunk/Source/WebCore/platform/ScrollableArea.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/platform/ScrollableArea.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -91,13 +91,8 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     WEBCORE_EXPORT const ScrollSnapOffsetsInfo<LayoutUnit>* snapOffsetInfo() const;
     virtual void updateSnapOffsets() { };
-    void setHorizontalSnapOffsets(const Vector<LayoutUnit>&);
-    void setVerticalSnapOffsets(const Vector<LayoutUnit>&);
-    void setHorizontalSnapOffsetRanges(const Vector<ScrollOffsetRange<LayoutUnit>>&);
-    void setVerticalSnapOffsetRanges(const Vector<ScrollOffsetRange<LayoutUnit>>&);
+    void setScrollSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>&);
     void clearSnapOffsets();
-    void clearHorizontalSnapOffsets();
-    void clearVerticalSnapOffsets();
     unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
     void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
     unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; }

Modified: trunk/Source/WebCore/platform/cocoa/ScrollController.mm (272609 => 272610)


--- trunk/Source/WebCore/platform/cocoa/ScrollController.mm	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/platform/cocoa/ScrollController.mm	2021-02-09 21:52:39 UTC (rev 272610)
@@ -881,7 +881,7 @@
     if (!snapOffsets.size())
         return;
 
-    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first()), snapOffsets.last());
+    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first().offset), snapOffsets.last().offset);
 
     unsigned activeIndex = snapState.snapOffsetInfo().closestSnapOffset(axis, clampedOffset, 0).second;
     if (activeIndex == activeScrollSnapIndexForAxis(axis))

Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h (272609 => 272610)


--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -52,7 +52,7 @@
 class ScrollSnapAnimatorState {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    const Vector<LayoutUnit>& snapOffsetsForAxis(ScrollEventAxis axis) const
+    const Vector<SnapOffset<LayoutUnit>>& snapOffsetsForAxis(ScrollEventAxis axis) const
     {
         return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsInfo.horizontalSnapOffsets : m_snapOffsetsInfo.verticalSnapOffsets;
     }
@@ -65,7 +65,7 @@
     const ScrollSnapOffsetsInfo<LayoutUnit>& snapOffsetInfo() const { return m_snapOffsetsInfo; }
     void setSnapOffsetInfo(const ScrollSnapOffsetsInfo<LayoutUnit>& newInfo) { m_snapOffsetsInfo = newInfo; }
 
-    void setSnapOffsetsAndPositionRangesForAxis(ScrollEventAxis axis, const Vector<LayoutUnit>& snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRanges)
+    void setSnapOffsetsAndPositionRangesForAxis(ScrollEventAxis axis, const Vector<SnapOffset<LayoutUnit>>& snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRanges)
     {
         if (axis == ScrollEventAxis::Horizontal) {
             m_snapOffsetsInfo.horizontalSnapOffsets = snapOffsets;

Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm (272609 => 272610)


--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm	2021-02-09 21:52:39 UTC (rev 272610)
@@ -100,13 +100,6 @@
     return clampTo<float>(startOffset, 0, maxScrollOffset);
 }
 
-template<typename T>
-TextStream& operator<<(TextStream& ts, const ScrollOffsetRange<T>& range)
-{
-    ts << "start: " << range.start << " end: " << range.end;
-    return ts;
-}
-
 TextStream& operator<<(TextStream& ts, const ScrollSnapAnimatorState& state)
 {
     ts << "ScrollSnapAnimatorState";

Modified: trunk/Source/WebCore/rendering/RenderLayerModelObject.cpp (272609 => 272610)


--- trunk/Source/WebCore/rendering/RenderLayerModelObject.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/RenderLayerModelObject.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -196,11 +196,10 @@
         }
     }
 
-    bool scrollMarginChanged =
-        oldStyle && oldStyle->scrollMargin() != newStyle.scrollMargin();
-    bool scrollAlignChanged =
-        oldStyle && oldStyle->scrollSnapAlign() != newStyle.scrollSnapAlign();
-    if (scrollMarginChanged || scrollAlignChanged) {
+    bool scrollMarginChanged = oldStyle && oldStyle->scrollMargin() != newStyle.scrollMargin();
+    bool scrollAlignChanged = oldStyle && oldStyle->scrollSnapAlign() != newStyle.scrollSnapAlign();
+    bool scrollSnapStopChanged = oldStyle && oldStyle->scrollSnapStop() != newStyle.scrollSnapStop();
+    if (scrollMarginChanged || scrollAlignChanged || scrollSnapStopChanged) {
         auto* scrollSnapBox = enclosingScrollableContainerForSnapping();
         if (scrollSnapBox && scrollSnapBox->layer()) {
             const RenderStyle& style = scrollSnapBox->style();
@@ -210,6 +209,8 @@
                     scrollableArea->updateScrollSnapState();
                 }
                 if (scrollSnapBox->isBody() || scrollSnapBox->isDocumentElementRenderer())
+                    scrollSnapBox->view().frameView().updateSnapOffsets();
+                    scrollSnapBox->view().frameView().updateScrollSnapState();
                     scrollSnapBox->view().frameView().updateScrollingCoordinatorScrollSnapProperties();
             }
         }

Modified: trunk/Source/WebCore/rendering/style/RenderStyle.cpp (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -2498,6 +2498,11 @@
     return { };
 }
 
+ScrollSnapStop RenderStyle::initialScrollSnapStop()
+{
+    return ScrollSnapStop::Normal;
+}
+
 const ScrollSnapType RenderStyle::scrollSnapType() const
 {
     return m_rareNonInheritedData->scrollSnapType;
@@ -2508,6 +2513,11 @@
     return m_rareNonInheritedData->scrollSnapAlign;
 }
 
+ScrollSnapStop RenderStyle::scrollSnapStop() const
+{
+    return m_rareNonInheritedData->scrollSnapStop;
+}
+
 void RenderStyle::setScrollSnapType(const ScrollSnapType type)
 {
     SET_VAR(m_rareNonInheritedData, scrollSnapType, type);
@@ -2518,6 +2528,11 @@
     SET_VAR(m_rareNonInheritedData, scrollSnapAlign, alignment);
 }
 
+void RenderStyle::setScrollSnapStop(const ScrollSnapStop stop)
+{
+    SET_VAR(m_rareNonInheritedData, scrollSnapStop, stop);
+}
+
 bool RenderStyle::hasSnapPosition() const
 {
     const ScrollSnapAlign& alignment = this->scrollSnapAlign();

Modified: trunk/Source/WebCore/rendering/style/RenderStyle.h (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/RenderStyle.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/RenderStyle.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -751,6 +751,7 @@
     bool hasSnapPosition() const;
     const ScrollSnapType scrollSnapType() const;
     const ScrollSnapAlign& scrollSnapAlign() const;
+    ScrollSnapStop scrollSnapStop() const;
 #endif
 
 #if ENABLE(TOUCH_EVENTS)
@@ -1293,6 +1294,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     void setScrollSnapType(const ScrollSnapType);
     void setScrollSnapAlign(const ScrollSnapAlign&);
+    void setScrollSnapStop(const ScrollSnapStop);
 #endif
 
 #if ENABLE(TOUCH_EVENTS)
@@ -1699,6 +1701,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     static ScrollSnapType initialScrollSnapType();
     static ScrollSnapAlign initialScrollSnapAlign();
+    static ScrollSnapStop initialScrollSnapStop();
 #endif
 
 #if ENABLE(CSS_TRAILING_WORD)

Modified: trunk/Source/WebCore/rendering/style/RenderStyleConstants.cpp (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/RenderStyleConstants.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/RenderStyleConstants.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1039,6 +1039,15 @@
     }
     return ts;
 }
+
+TextStream& operator<<(TextStream& ts, ScrollSnapStop stop)
+{
+    switch (stop) {
+    case ScrollSnapStop::Normal: ts << "normal"; break;
+    case ScrollSnapStop::Always: ts << "always"; break;
+    }
+    return ts;
+}
 #endif
 
 TextStream& operator<<(TextStream& ts, SpeakAs speakAs)

Modified: trunk/Source/WebCore/rendering/style/RenderStyleConstants.h (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/RenderStyleConstants.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/RenderStyleConstants.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include <initializer_list>
+#include <wtf/EnumTraits.h>
 
 namespace WTF {
 class TextStream;
@@ -1127,6 +1128,11 @@
     Center,
     End
 };
+
+enum class ScrollSnapStop : uint8_t {
+    Normal,
+    Always,
+};
 #endif
 
 #if ENABLE(CSS_TRAILING_WORD)
@@ -1284,6 +1290,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
 WTF::TextStream& operator<<(WTF::TextStream&, ScrollSnapAxis);
 WTF::TextStream& operator<<(WTF::TextStream&, ScrollSnapAxisAlignType);
+WTF::TextStream& operator<<(WTF::TextStream&, ScrollSnapStop);
 WTF::TextStream& operator<<(WTF::TextStream&, ScrollSnapStrictness);
 #endif
 WTF::TextStream& operator<<(WTF::TextStream&, SpeakAs);
@@ -1315,3 +1322,15 @@
 WTF::TextStream& operator<<(WTF::TextStream&, MathStyle);
 
 } // namespace WebCore
+
+#if ENABLE(CSS_SCROLL_SNAP)
+namespace WTF {
+template<> struct EnumTraits<WebCore::ScrollSnapStop> {
+    using values = EnumValues<
+        WebCore::ScrollSnapStop,
+        WebCore::ScrollSnapStop::Normal,
+        WebCore::ScrollSnapStop::Always
+    >;
+};
+}
+#endif

Modified: trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -141,6 +141,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     , scrollSnapType(o.scrollSnapType)
     , scrollSnapAlign(o.scrollSnapAlign)
+    , scrollSnapStop(o.scrollSnapStop)
 #endif
     , overscrollBehaviorX(o.overscrollBehaviorX)
     , overscrollBehaviorY(o.overscrollBehaviorY)
@@ -247,6 +248,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
         && scrollSnapType == o.scrollSnapType
         && scrollSnapAlign == o.scrollSnapAlign
+        && scrollSnapStop == o.scrollSnapStop
 #endif
         && overscrollBehaviorX == o.overscrollBehaviorX
         && overscrollBehaviorY == o.overscrollBehaviorY

Modified: trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.h (272609 => 272610)


--- trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -136,6 +136,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     ScrollSnapType scrollSnapType;
     ScrollSnapAlign scrollSnapAlign;
+    ScrollSnapStop scrollSnapStop { ScrollSnapStop::Normal };
 #endif
 
     unsigned overscrollBehaviorX : 2; // OverscrollBehavior

Modified: trunk/Source/WebCore/style/StyleBuilderConverter.h (272609 => 272610)


--- trunk/Source/WebCore/style/StyleBuilderConverter.h	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/style/StyleBuilderConverter.h	2021-02-09 21:52:39 UTC (rev 272610)
@@ -111,6 +111,7 @@
 #if ENABLE(CSS_SCROLL_SNAP)
     static ScrollSnapType convertScrollSnapType(BuilderState&, const CSSValue&);
     static ScrollSnapAlign convertScrollSnapAlign(BuilderState&, const CSSValue&);
+    static ScrollSnapStop convertScrollSnapStop(BuilderState&, const CSSValue&);
 #endif
     static GridTrackSize convertGridTrackSize(BuilderState&, const CSSValue&);
     static Vector<GridTrackSize> convertGridTrackSizeList(BuilderState&, const CSSValue&);
@@ -930,6 +931,12 @@
     return alignment;
 }
 
+inline ScrollSnapStop BuilderConverter::convertScrollSnapStop(BuilderState&, const CSSValue& value)
+{
+    ASSERT(is<CSSPrimitiveValue>(value));
+    return downcast<CSSPrimitiveValue>(value);
+}
+
 #endif
 
 inline GridLength BuilderConverter::createGridTrackBreadth(const CSSPrimitiveValue& primitiveValue, BuilderState& builderState)

Modified: trunk/Source/WebCore/testing/Internals.cpp (272609 => 272610)


--- trunk/Source/WebCore/testing/Internals.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebCore/testing/Internals.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -4637,7 +4637,7 @@
 
 #if ENABLE(CSS_SCROLL_SNAP)
 
-static void appendOffsets(StringBuilder& builder, const Vector<LayoutUnit>& snapOffsets)
+static void appendOffsets(StringBuilder& builder, const Vector<SnapOffset<LayoutUnit>>& snapOffsets)
 {
     bool justStarting = true;
 
@@ -4648,7 +4648,10 @@
         else
             justStarting = false;
 
-        builder.appendNumber(coordinate.toUnsigned());
+        builder.appendNumber(coordinate.offset.toUnsigned());
+        if (coordinate.stop == ScrollSnapStop::Always)
+            builder.appendLiteral(" (always)");
+
     }
     builder.appendLiteral(" }");
 }

Modified: trunk/Source/WebKit/ChangeLog (272609 => 272610)


--- trunk/Source/WebKit/ChangeLog	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebKit/ChangeLog	2021-02-09 21:52:39 UTC (rev 272610)
@@ -1,3 +1,20 @@
+2021-02-09  Martin Robinson  <[email protected]>
+
+        Implement scroll-snap-stop for scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=197744
+        <rdar://problem/50708356>
+
+        Reviewed by Simon Fraser.
+
+        * Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp: Add encoding and decoding support
+        for the SnapOffset struct.
+        (ArgumentCoder<SnapOffset<float>>::encode):
+        (ArgumentCoder<SnapOffset<float>>::decode):
+        * UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm: Update to use SnapOffset struct.
+        (WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling const):
+        (WebKit::RemoteScrollingCoordinatorProxy::hasActiveSnapPoint const):
+        (WebKit::RemoteScrollingCoordinatorProxy::nearestActiveContentInsetAdjustedSnapOffset const):
+
 2021-02-09  Per Arne  <[email protected]>
 
         [macOS] Deny mach-lookup to the distributed notifications service

Modified: trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp (272609 => 272610)


--- trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp	2021-02-09 21:52:39 UTC (rev 272610)
@@ -103,6 +103,12 @@
     static WARN_UNUSED_RETURN bool decode(Decoder&, ScrollSnapOffsetsInfo<float>&);
 };
 
+template<> struct ArgumentCoder<SnapOffset<float>> {
+    static void encode(Encoder&, const SnapOffset<float>&);
+    static WARN_UNUSED_RETURN bool decode(Decoder&, SnapOffset<float>&);
+};
+
+
 } // namespace IPC
 
 namespace WTF {
@@ -522,6 +528,22 @@
     return true;
 }
 
+void ArgumentCoder<SnapOffset<float>>::encode(Encoder& encoder, const SnapOffset<float>& offset)
+{
+    encoder << offset.offset;
+    encoder << offset.stop;
+}
+
+bool ArgumentCoder<SnapOffset<float>>::decode(Decoder& decoder, SnapOffset<float>& offset)
+{
+    if (!decoder.decode(offset.offset))
+        return false;
+    if (!decoder.decode(offset.stop))
+        return false;
+    return true;
+}
+
+
 void ArgumentCoder<ScrollSnapOffsetsInfo<float>>::encode(Encoder& encoder, const ScrollSnapOffsetsInfo<float>& info)
 {
     encoder << info.horizontalSnapOffsets;

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm (272609 => 272610)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm	2021-02-09 21:51:43 UTC (rev 272609)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm	2021-02-09 21:52:39 UTC (rev 272610)
@@ -214,7 +214,7 @@
     ScrollingTreeNode* root = m_scrollingTree->rootNode();
     if (root && root->isFrameScrollingNode()) {
         ScrollingTreeFrameScrollingNode* rootScrollingNode = static_cast<ScrollingTreeFrameScrollingNode*>(root);
-        const Vector<float>& snapOffsets = rootScrollingNode->snapOffsetsInfo().offsetsForAxis(axis);
+        const auto& snapOffsets = rootScrollingNode->snapOffsetsInfo().offsetsForAxis(axis);
         unsigned currentIndex = axis == ScrollEventAxis::Horizontal ? m_currentHorizontalSnapPointIndex : m_currentVerticalSnapPointIndex;
         return snapOffsets.size() && (currentIndex < snapOffsets.size() || currentIndex == invalidSnapOffsetIndex);
     }
@@ -244,8 +244,8 @@
         return false;
 
     ScrollingTreeFrameScrollingNode& rootScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*root);
-    const Vector<float>& horizontal = rootScrollingNode.snapOffsetsInfo().horizontalSnapOffsets;
-    const Vector<float>& vertical = rootScrollingNode.snapOffsetsInfo().verticalSnapOffsets;
+    const auto& horizontal = rootScrollingNode.snapOffsetsInfo().horizontalSnapOffsets;
+    const auto& vertical = rootScrollingNode.snapOffsetsInfo().verticalSnapOffsets;
 
     if (horizontal.isEmpty() && vertical.isEmpty())
         return false;
@@ -265,15 +265,15 @@
     ScrollingTreeNode* root = m_scrollingTree->rootNode();
     ASSERT(root && is<ScrollingTreeFrameScrollingNode>(root));
     ScrollingTreeFrameScrollingNode& rootScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*root);
-    const Vector<float>& horizontal = rootScrollingNode.snapOffsetsInfo().horizontalSnapOffsets;
-    const Vector<float>& vertical = rootScrollingNode.snapOffsetsInfo().verticalSnapOffsets;
+    const auto& horizontal = rootScrollingNode.snapOffsetsInfo().horizontalSnapOffsets;
+    const auto& vertical = rootScrollingNode.snapOffsetsInfo().verticalSnapOffsets;
 
     // The bounds checking with maxScrollOffsets is to ensure that we won't interfere with rubber-banding when scrolling to the edge of the page.
     if (!horizontal.isEmpty() && m_currentHorizontalSnapPointIndex < horizontal.size())
-        activePoint.x = horizontal[m_currentHorizontalSnapPointIndex] * m_webPageProxy.displayedContentScale();
+        activePoint.x = horizontal[m_currentHorizontalSnapPointIndex].offset * m_webPageProxy.displayedContentScale();
 
     if (!vertical.isEmpty() && m_currentVerticalSnapPointIndex < vertical.size()) {
-        float potentialSnapPosition = vertical[m_currentVerticalSnapPointIndex] * m_webPageProxy.displayedContentScale();
+        float potentialSnapPosition = vertical[m_currentVerticalSnapPointIndex].offset * m_webPageProxy.displayedContentScale();
         potentialSnapPosition -= topInset;
         activePoint.y = potentialSnapPosition;
     }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to