Scroll-snap points do not handle margins and padding propertly
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 May 2015 02:53:00 +0000 (02:53 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 May 2015 02:53:00 +0000 (02:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=144647
<rdar://problem/20829473>

Reviewed by Simon Fraser.

Source/WebCore:

The calculation of scroll snap points was incorrect because it did not account for margins or padding.
This was fixed by using the "paddingBoxRect" to represent the overall size of the view area, and
subtracting the relevant padding when computing the size of the scroll snap offsets.

Extend testing internals with accessor methods to retrieve string representations of the scroll snap
offsets and scroll snap coordinates computed during layout. These values are used in the new
'css3/scroll-snap/scroll-snap-offsets-and-coordinates.html' test.

New tests:
* css3/scroll-snap/scroll-snap-offsets-and-coordinates.html: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html: Added.

* css/LengthFunctions.h: Expose the 'valueForLength' method so we can use it in WebCoreTestSupport.
* dom/Node.h: Expose the 'renderBox' method so we can use it in WebCoreTestSupport.
* page/scrolling/AxisScrollSnapOffsets.cpp:
(WebCore::appendChildSnapOffsets): Use 'contentBoxRect' for overall size, so that we don't use padding
as part of our offset calculations.
(WebCore::updateSnapOffsetsForScrollableArea): Ditto.
* rendering/RenderBox.h: Expose 'canBeScrolledAndHasScrollableArea' for use in WebCoreTestSupport.
* rendering/style/RenderStyle.h: Expose 'scrollSnapCoordinates' for use in WebCoreTestSupport.
* testing/Internals.cpp:
(WebCore::appendOffsets): Helper function.
(WebCore::Internals::scrollSnapOffsets): Added.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Add new scroll-snap tests that cover elements with borders, padding, and rotation. Also correct the
cause of the 'scroll-snap-mandatory-overflow.html' test flakiness by making sure the mouse is always
inside the element when starting new wheel gestures.

Also add a new 'generic' test that confirms that we have expected results for scroll-snap coordinates
and calculated scroll-snap-offset values.

* css3/scroll-snap/scroll-snap-offsets.html: Added.
* platform/mac-wk2/TestExpectations: Remove flakiness annotation for scroll-snap-mandatory-overflow.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html:
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html:
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding-expected.txt: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated-expected.txt: Added.
* platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183906 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/scroll-snap/scroll-snap-offsets-expected.txt [new file with mode: 0644]
LayoutTests/css3/scroll-snap/scroll-snap-offsets.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/TestExpectations
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/LengthFunctions.h
Source/WebCore/dom/Node.h
Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 011a0a84b52c0cc55f60077a5f2af1227e650edd..1466a2d7120742af3ce4520800748de9732b606f 100644 (file)
@@ -1,3 +1,29 @@
+2015-05-06  Brent Fulgham  <bfulgham@apple.com>
+
+        Scroll-snap points do not handle margins and padding propertly
+        https://bugs.webkit.org/show_bug.cgi?id=144647
+        <rdar://problem/20829473>
+
+        Reviewed by Simon Fraser.
+
+        Add new scroll-snap tests that cover elements with borders, padding, and rotation. Also correct the
+        cause of the 'scroll-snap-mandatory-overflow.html' test flakiness by making sure the mouse is always
+        inside the element when starting new wheel gestures.
+
+        Also add a new 'generic' test that confirms that we have expected results for scroll-snap coordinates
+        and calculated scroll-snap-offset values.
+        * css3/scroll-snap/scroll-snap-offsets.html: Added.
+        * platform/mac-wk2/TestExpectations: Remove flakiness annotation for scroll-snap-mandatory-overflow.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html:
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html:
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding-expected.txt: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated-expected.txt: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html: Added.
+
 2015-05-06  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r183894.
diff --git a/LayoutTests/css3/scroll-snap/scroll-snap-offsets-expected.txt b/LayoutTests/css3/scroll-snap/scroll-snap-offsets-expected.txt
new file mode 100644 (file)
index 0000000..d79f5ee
--- /dev/null
@@ -0,0 +1,18 @@
+Tests that the scroll-snap feature works properly in overflow regions.
+ PASS successfullyParsed is true
+
+TEST COMPLETE
+Scroll-snap offsets for horizontalTarget: horizontal = { 0, 30, 60, 90, 120, 150 }, vertical = { 0, 15 }
+Scroll-snap offsets for verticalTarget: horizontal = { 0, 15 }, vertical = { 0, 30, 60, 90, 120, 150 }
+Scroll-snap offsets for horizontalBorderedTarget: horizontal = { 0, 30, 60, 90, 120, 150 }, vertical = { 0, 15 }
+Scroll-snap offsets for verticalBorderedTarget: horizontal = { 0, 15 }, vertical = { 0, 30, 60, 90, 120, 150 }
+Scroll-snap offsets for horizontalPaddedTarget: horizontal = { 0, 30, 60, 90, 120, 150, 170 }, vertical = { 0, 39 }
+Scroll-snap offsets for verticalPaddedTarget: horizontal = { 0, 35 }, vertical = { 0, 30, 60, 90, 120, 150, 174 }
+Scroll-snap offsets for horizontalBorderedPaddedTarget: horizontal = { 0, 30, 60, 90, 120, 150, 170 }, vertical = { 0, 39 }
+Scroll-snap offsets for verticalBorderedPaddedTarget: horizontal = { 0, 35 }, vertical = { 0, 30, 60, 90, 120, 150, 174 }
+Scroll-snap offsets for horizontalRotatedTarget: horizontal = { 0, 30, 60, 90, 120, 150, 170 }, vertical = { 0, 39 }
+Scroll-snap offsets for verticalRotatedTarget: horizontal = { 0, 35 }, vertical = { 0, 30, 60, 90, 120, 150, 174 }
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/css3/scroll-snap/scroll-snap-offsets.html b/LayoutTests/css3/scroll-snap/scroll-snap-offsets.html
new file mode 100644 (file)
index 0000000..708c0e2
--- /dev/null
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <style>
+            .bordered {
+                border-top: 20px solid black;
+                border-bottom: 10px solid black;
+                border-left: 15px solid black;
+                border-right: 9px solid black;
+            }
+            .padded {
+                padding-left: 20px;
+                padding-right: 10px;
+                padding-top: 15px;
+                padding-bottom: 9px;
+            }
+            .horizontalGallery {
+                width: 30px;
+                height: 30px;
+                overflow-y: hidden;
+                overflow-x: auto;
+                margin: 2px;
+                -webkit-overflow-scrolling: touch;
+                -webkit-scroll-snap-points-x: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+                -webkit-scroll-snap-coordinate: 50% 50%;
+            }
+            .horizontalGalleryDrawer {
+                width: 180px;
+                height: 30px;
+            }
+            .verticalGallery {
+                width: 30px;
+                height: 30px;
+                display: inline-block;
+                overflow-x: hidden;
+                overflow-y: auto;
+                margin: 2px;
+                -webkit-overflow-scrolling: touch;
+                -webkit-scroll-snap-points-y: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+                -webkit-scroll-snap-coordinate: 50% 50%;
+            }
+            .verticalGalleryDrawer {
+                width: 30px;
+                height: 180px;
+            }
+            .colorBox {
+                height: 30px;
+                width: 30px;
+                float: left;
+            }
+            #itemH0, #itemV0 { background-color: red; }
+            #itemH1, #itemV1 { background-color: green; }
+            #itemH2, #itemV2 { background-color: blue; }
+            #itemH3, #itemV3 { background-color: aqua; }
+            #itemH4, #itemV4 { background-color: yellow; }
+            #itemH5, #itemV5 { background-color: fuchsia; }
+        </style>
+        <script src="../../resources/js-test-pre.js"></script>
+        <script>
+        function reportResult(horizontalTargetID, verticalTargetID)
+        {
+            var horizontalTarget = document.getElementById(horizontalTargetID);
+            var verticalTarget = document.getElementById(verticalTargetID);
+
+            debug("Scroll-snap offsets for " + horizontalTargetID + ": " + window.internals.scrollSnapOffsets(horizontalTarget));
+            debug("Scroll-snap offsets for " + verticalTargetID + ": " + window.internals.scrollSnapOffsets(verticalTarget));
+        }
+
+        function runTest()
+        {
+            reportResult('horizontalTarget', 'verticalTarget');
+            reportResult('horizontalBorderedTarget', 'verticalBorderedTarget');
+            reportResult('horizontalPaddedTarget', 'verticalPaddedTarget');
+            reportResult('horizontalBorderedPaddedTarget', 'verticalBorderedPaddedTarget');
+            reportResult('horizontalRotatedTarget', 'verticalRotatedTarget')
+
+            finishJSTest();
+            testRunner.notifyDone();
+        }
+
+        function onLoad()
+        {
+            if (window.testRunner) {
+                window.jsTestIsAsync = true;
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+                setTimeout(runTest, 0);
+            }
+        }
+        </script>
+    </head>
+    <body onload="onLoad();">
+        <div style="position: relative; width: 300px">
+            <div>Tests that the scroll-snap feature works properly in overflow regions.</div>
+            <div class="horizontalGallery" id="horizontalTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery" id="verticalTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="horizontalGallery bordered" id="horizontalBorderedTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery bordered" id="verticalBorderedTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="horizontalGallery padded" id="horizontalPaddedTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery padded" id="verticalPaddedTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="horizontalGallery bordered padded" id="horizontalBorderedPaddedTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery bordered padded" id="verticalBorderedPaddedTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="horizontalGallery bordered padded" id="horizontalRotatedTarget" style="-webkit-transform: rotate(20deg)">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery bordered padded" id="verticalRotatedTarget" style="-webkit-transform: rotate(-20deg)">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div id="console"></div>
+        </div>
+        <script src="../../resources/js-test-post.js"></script>
+    </body>
+</html>
index 49219c759e0f366043e1d48ce30a92e5f28ef12d..23375d1e39577c7989c062dd93d31729501199d9 100644 (file)
@@ -523,10 +523,3 @@ webkit.org/b/141085 [ Yosemite ] http/tests/media/video-query-url.html [ Pass Ti
 ########################################
 
 http/tests/contentextensions [ Pass ]
-
-########################################
-### START OF (1) New scroll-snap flake
-platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html [ Timeout Pass ]
-
-### END OF (1) scroll-snap flake
-########################################
index f6c061547a1f60afb5e23762e79390b856499cc8..bc260bc99784a480b5a66cf4628c61730075fb28 100644 (file)
                 dy = -1;
             }
 
-            var startPosX = iframeTarget.offsetLeft + 20;
-            var startPosY = iframeTarget.offsetTop + 20;
+            var windowPosition = locationInWindowCoordinates(iframeTarget);
+
+            var startPosX = windowPosition.x + 0.5 * iframeTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * iframeTarget.clientHeight;
             eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
 
             var windowPosition = locationInWindowCoordinates(iframeTarget);
 
-            var startPosX = windowPosition.x + iframeTarget.clientWidth - 10;
-            var startPosY = windowPosition.y + 50;
+            var startPosX = windowPosition.x + 0.5 * iframeTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * iframeTarget.clientHeight;
             eventSender.mouseMoveTo(startPosX, startPosY);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt
new file mode 100644 (file)
index 0000000..31a0666
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that the scroll-snap feature works properly in overflow regions.
+ PASS successfullyParsed is true
+
+TEST COMPLETE
+Testing scroll-snap glide for horizontalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for horizontalTarget:
+PASS div honored snap points.
+Testing scroll-snap glide for verticalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for verticalTarget:
+PASS div honored snap points.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html
new file mode 100644 (file)
index 0000000..ed0a2cc
--- /dev/null
@@ -0,0 +1,239 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <style>
+            .horizontalGallery {
+                width: 300px;
+                height: 300px;
+                overflow-y: hidden;
+                overflow-x: auto;
+                border-top: 20px solid black;
+                border-bottom: 10px solid black;
+                border-left: 15px solid black;
+                border-right: 9px solid black;
+                margin-bottom: 2px;
+                -webkit-overflow-scrolling: touch;
+                -webkit-scroll-snap-points-x: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .horizontalGalleryDrawer {
+                width: 1800px;
+                height: 300px;
+            }
+            .verticalGallery {
+                width: 300px;
+                height: 300px;
+                display: inline-block;
+                overflow-x: hidden;
+                overflow-y: auto;
+                border-top: 20px solid black;
+                border-bottom: 10px solid black;
+                border-left: 15px solid black;
+                border-right: 9px solid black;
+                margin-top: 2px;
+                -webkit-overflow-scrolling: touch;
+                -webkit-scroll-snap-points-y: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .verticalGalleryDrawer {
+                width: 300px;
+                height: 1800px;
+            }
+            .colorBox {
+                height: 300px;
+                width: 300px;
+                float: left;
+            }
+            #itemH0, #itemV0 { background-color: red; }
+            #itemH1, #itemV1 { background-color: green; }
+            #itemH2, #itemV2 { background-color: blue; }
+            #itemH3, #itemV3 { background-color: aqua; }
+            #itemH4, #itemV4 { background-color: yellow; }
+            #itemH5, #itemV5 { background-color: fuchsia; }
+        </style>
+        <script src="../../../../../resources/js-test-pre.js"></script>
+        <script>
+        var divScrollPositionBeforeGlide;
+        var divScrollPositionBeforeSnap;
+
+        function locationInWindowCoordinates(element)
+        {
+            var position = {};
+            position.x = element.offsetLeft;
+            position.y = element.offsetTop;
+
+            while (element.offsetParent) {
+                position.x = position.x + element.offsetParent.offsetLeft;
+                position.y = position.y + element.offsetParent.offsetTop;
+                if (element == document.getElementsByTagName("body")[0])
+                    break;
+
+                element = element.offsetParent;
+            }
+
+            return position;
+        }
+
+        function finishTest()
+        {
+            finishJSTest();
+            testRunner.notifyDone();            
+        }
+
+        function checkForScrollSnap(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var actualPosition = divTarget.scrollTop;
+            if (targetLabel == 'horizontalTarget')
+                actualPosition = divTarget.scrollLeft;
+
+            // The div should have snapped back to the previous position
+            if (actualPosition != divScrollPositionBeforeSnap)
+                testFailed("div did not snap back to proper location for " + targetLabel +". Expected " + divScrollPositionBeforeSnap + ", but got " + actualPosition);
+            else
+                testPassed("div honored snap points.");
+
+            if (targetLabel == 'horizontalTarget')
+                setTimeout(scrollGlideTest('verticalTarget'), 0);
+            else   
+                finishTest();
+        }
+
+        function scrollSnapTest(targetLabel)
+        {
+            debug("Testing scroll-snap snap for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeSnap = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeSnap = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
+            eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollSnap(targetLabel); });
+        }
+
+        function checkForScrollGlide(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var actualPosition = divTarget.scrollTop;
+            var expectedPosition = divTarget.clientHeight;
+            if (targetLabel == 'horizontalTarget') {
+                actualPosition = divTarget.scrollLeft;
+                expectedPosition = divTarget.clientWidth;
+            }
+
+            // The div should have scrolled (glided) to the next snap point.
+            if (actualPosition == expectedPosition)
+                testPassed("div scrolled to next window.");
+            else
+                testFailed("div did not honor snap points. Expected " + expectedPosition + ", but got " + actualPosition);
+
+            setTimeout(scrollSnapTest(targetLabel), 0);
+        }
+
+        function scrollGlideTest(targetLabel)
+        {
+            debug("Testing scroll-snap glide for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeGlide = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeGlide = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + divTarget.clientWidth - 10;
+            var startPosY = windowPosition.y + 50;
+            eventSender.mouseMoveTo(startPosX, startPosY);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'begin', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollGlide(targetLabel); });
+        }
+
+        function onLoad()
+        {
+            if (window.eventSender) {
+                window.jsTestIsAsync = true;
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+
+                eventSender.monitorWheelEvents();
+                setTimeout(scrollGlideTest('horizontalTarget'), 0);
+            } else {
+                var messageLocationH = document.getElementById('itemH0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>This test is better run under DumpRenderTree.<br/>To manually test it, place the mouse pointer<br/>"
+                    + "inside the red region at the top of the page,<br/>and then use the mouse wheel or a two-finger<br/>swipe to make a"
+                    + "small swipe gesture with<br/>some momentum.<br/><br/>"
+                    + "The region should scroll to show a green region.<br/><br/>"
+                    + "Next, perform a small scroll gesture that does<br/>not involve momentum. You should begin to<br/>see one of the colors "
+                    + "to the side of the current<br/>green box. When you release the wheel, the<br/>region should scroll back to a single color.";
+                messageLocationH.appendChild(message);
+
+                var messageLocationV = document.getElementById('itemV0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>You should also be able to repeat these tests steps for this vertical region.<br/>"
+                messageLocationV.appendChild(message);
+            }
+        }
+        </script>
+    </head>
+    <body onload="onLoad();">
+        <div style="position: relative; width: 300px">
+            <div>Tests that the scroll-snap feature works properly in overflow regions.</div>
+            <div class="horizontalGallery" id="horizontalTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery" id="verticalTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div id="console"></div>
+        </div>
+        <script src="../../../../../resources/js-test-post.js"></script>
+    </body>
+</html>
\ No newline at end of file
index b02cafb9edbd2b21284587867028fc5d015f46ea..e31bfec9bee9f0398208fb712640c88125c44072 100644 (file)
                 dy = -1;
             }
 
-            var startPosX = divTarget.offsetLeft + 20;
-            var startPosY = divTarget.offsetTop + 20;
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
             eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
 
             var windowPosition = locationInWindowCoordinates(divTarget);
 
-            var startPosX = windowPosition.x + divTarget.clientWidth - 10;
-            var startPosY = windowPosition.y + 50;
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
             eventSender.mouseMoveTo(startPosX, startPosY);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
             eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding-expected.txt b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding-expected.txt
new file mode 100644 (file)
index 0000000..31a0666
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that the scroll-snap feature works properly in overflow regions.
+ PASS successfullyParsed is true
+
+TEST COMPLETE
+Testing scroll-snap glide for horizontalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for horizontalTarget:
+PASS div honored snap points.
+Testing scroll-snap glide for verticalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for verticalTarget:
+PASS div honored snap points.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html
new file mode 100644 (file)
index 0000000..b7258f5
--- /dev/null
@@ -0,0 +1,239 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <style>
+            .horizontalGallery {
+                width: 300px;
+                height: 300px;
+                overflow-y: hidden;
+                overflow-x: auto;
+                padding-left: 20px;
+                padding-right: 10px;
+                padding-top: 15px;
+                padding-bottom: 9px;
+                margin-bottom: 2px;
+                -webkit-scroll-snap-points-x: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .horizontalGalleryDrawer {
+                width: 1800px;
+                height: 300px;
+            }
+            .verticalGallery {
+                width: 300px;
+                height: 300px;
+                display: inline-block;
+                overflow-x: hidden;
+                overflow-y: auto;
+                padding-left: 20px;
+                padding-right: 10px;
+                padding-top: 15px;
+                padding-bottom: 9px;
+                margin-top: 2px;
+                -webkit-scroll-snap-points-y: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .verticalGalleryDrawer {
+                width: 300px;
+                height: 1800px;
+            }
+            .colorBox {
+                height: 300px;
+                width: 300px;
+                float: left;
+            }
+            #itemH0, #itemV0 { background-color: red; }
+            #itemH1, #itemV1 { background-color: green; }
+            #itemH2, #itemV2 { background-color: blue; }
+            #itemH3, #itemV3 { background-color: aqua; }
+            #itemH4, #itemV4 { background-color: yellow; }
+            #itemH5, #itemV5 { background-color: fuchsia; }
+        </style>
+        <script src="../../../../../resources/js-test-pre.js"></script>
+        <script>
+        var divScrollPositionBeforeGlide;
+        var divScrollPositionBeforeSnap;
+
+        function locationInWindowCoordinates(element)
+        {
+            var position = {};
+            position.x = element.offsetLeft;
+            position.y = element.offsetTop;
+
+            while (element.offsetParent) {
+                position.x = position.x + element.offsetParent.offsetLeft;
+                position.y = position.y + element.offsetParent.offsetTop;
+                if (element == document.getElementsByTagName("body")[0])
+                    break;
+
+                element = element.offsetParent;
+            }
+
+            return position;
+        }
+
+        function finishTest()
+        {
+            finishJSTest();
+            testRunner.notifyDone();            
+        }
+
+        function checkForScrollSnap(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var actualPosition = divTarget.scrollTop;
+            if (targetLabel == 'horizontalTarget')
+                actualPosition = divTarget.scrollLeft;
+
+            // The div should have snapped back to the previous position
+            if (actualPosition != divScrollPositionBeforeSnap)
+                testFailed("div did not snap back to proper location for " + targetLabel +". Expected " + divScrollPositionBeforeSnap + ", but got " + actualPosition);
+            else
+                testPassed("div honored snap points.");
+
+            if (targetLabel == 'horizontalTarget')
+                setTimeout(scrollGlideTest('verticalTarget'), 0);
+            else   
+                finishTest();
+        }
+
+        function scrollSnapTest(targetLabel)
+        {
+            debug("Testing scroll-snap snap for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeSnap = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeSnap = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
+            eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollSnap(targetLabel); });
+        }
+
+        function checkForScrollGlide(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var style = window.getComputedStyle(divTarget, null);
+
+            var actualPosition = divTarget.scrollTop;
+            var expectedPosition = divTarget.clientHeight - style.getPropertyValue('padding-top').replace("px", "") - style.getPropertyValue('padding-bottom').replace("px", "");
+            if (targetLabel == 'horizontalTarget') {
+                actualPosition = divTarget.scrollLeft;
+                expectedPosition = divTarget.clientWidth - style.getPropertyValue('padding-right').replace("px", "") - style.getPropertyValue('padding-left').replace("px", "");
+            }
+
+            // The div should have scrolled (glided) to the next snap point.
+            if (actualPosition == expectedPosition)
+                testPassed("div scrolled to next window.");
+            else
+                testFailed("div did not honor snap points. Expected " + expectedPosition + ", but got " + actualPosition);
+
+            setTimeout(scrollSnapTest(targetLabel), 0);
+        }
+
+        function scrollGlideTest(targetLabel)
+        {
+            debug("Testing scroll-snap glide for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeGlide = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeGlide = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + divTarget.clientWidth - 10;
+            var startPosY = windowPosition.y + 50;
+            eventSender.mouseMoveTo(startPosX, startPosY);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'begin', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollGlide(targetLabel); });
+        }
+
+        function onLoad()
+        {
+            if (window.eventSender) {
+                window.jsTestIsAsync = true;
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+
+                eventSender.monitorWheelEvents();
+                setTimeout(scrollGlideTest('horizontalTarget'), 0);
+            } else {
+                var messageLocationH = document.getElementById('itemH0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>This test is better run under DumpRenderTree.<br/>To manually test it, place the mouse pointer<br/>"
+                    + "inside the red region at the top of the page,<br/>and then use the mouse wheel or a two-finger<br/>swipe to make a"
+                    + "small swipe gesture with<br/>some momentum.<br/><br/>"
+                    + "The region should scroll to show a green region.<br/><br/>"
+                    + "Next, perform a small scroll gesture that does<br/>not involve momentum. You should begin to<br/>see one of the colors "
+                    + "to the side of the current<br/>green box. When you release the wheel, the<br/>region should scroll back to a single color.";
+                messageLocationH.appendChild(message);
+
+                var messageLocationV = document.getElementById('itemV0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>You should also be able to repeat these tests steps for this vertical region.<br/>"
+                messageLocationV.appendChild(message);
+            }
+        }
+        </script>
+    </head>
+    <body onload="onLoad();">
+        <div style="position: relative; width: 300px">
+            <div>Tests that the scroll-snap feature works properly in overflow regions.</div>
+            <div class="horizontalGallery" id="horizontalTarget">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery" id="verticalTarget">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div id="console"></div>
+        </div>
+        <script src="../../../../../resources/js-test-post.js"></script>
+    </body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated-expected.txt b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated-expected.txt
new file mode 100644 (file)
index 0000000..31a0666
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that the scroll-snap feature works properly in overflow regions.
+ PASS successfullyParsed is true
+
+TEST COMPLETE
+Testing scroll-snap glide for horizontalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for horizontalTarget:
+PASS div honored snap points.
+Testing scroll-snap glide for verticalTarget:
+PASS div scrolled to next window.
+Testing scroll-snap snap for verticalTarget:
+PASS div honored snap points.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html b/LayoutTests/platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html
new file mode 100644 (file)
index 0000000..a5c674d
--- /dev/null
@@ -0,0 +1,231 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <style>
+            .horizontalGallery {
+                width: 300px;
+                height: 300px;
+                overflow-y: hidden;
+                overflow-x: auto;
+                border: 2px solid black;
+                margin-bottom: 15px;
+                -webkit-scroll-snap-points-x: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .horizontalGalleryDrawer {
+                width: 1800px;
+                height: 300px;
+            }
+            .verticalGallery {
+                width: 300px;
+                height: 300px;
+                display: inline-block;
+                overflow-x: hidden;
+                overflow-y: auto;
+                border: 2px solid black;
+                margin-top: 15px;
+                -webkit-scroll-snap-points-y: repeat(100%);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .verticalGalleryDrawer {
+                width: 300px;
+                height: 1800px;
+            }
+            .colorBox {
+                height: 300px;
+                width: 300px;
+                float: left;
+            }
+            #itemH0, #itemV0 { background-color: red; }
+            #itemH1, #itemV1 { background-color: green; }
+            #itemH2, #itemV2 { background-color: blue; }
+            #itemH3, #itemV3 { background-color: aqua; }
+            #itemH4, #itemV4 { background-color: yellow; }
+            #itemH5, #itemV5 { background-color: fuchsia; }
+        </style>
+        <script src="../../../../../resources/js-test-pre.js"></script>
+        <script>
+        var divScrollPositionBeforeGlide;
+        var divScrollPositionBeforeSnap;
+
+        function locationInWindowCoordinates(element)
+        {
+            var position = {};
+            position.x = element.offsetLeft;
+            position.y = element.offsetTop;
+
+            while (element.offsetParent) {
+                position.x = position.x + element.offsetParent.offsetLeft;
+                position.y = position.y + element.offsetParent.offsetTop;
+                if (element == document.getElementsByTagName("body")[0])
+                    break;
+
+                element = element.offsetParent;
+            }
+
+            return position;
+        }
+
+        function finishTest()
+        {
+            finishJSTest();
+            testRunner.notifyDone();            
+        }
+
+        function checkForScrollSnap(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var actualPosition = divTarget.scrollTop;
+            if (targetLabel == 'horizontalTarget')
+                actualPosition = divTarget.scrollLeft;
+
+            // The div should have snapped back to the previous position
+            if (actualPosition != divScrollPositionBeforeSnap)
+                testFailed("div did not snap back to proper location for " + targetLabel +". Expected " + divScrollPositionBeforeSnap + ", but got " + actualPosition);
+            else
+                testPassed("div honored snap points.");
+
+            if (targetLabel == 'horizontalTarget')
+                setTimeout(scrollGlideTest('verticalTarget'), 0);
+            else   
+                finishTest();
+        }
+
+        function scrollSnapTest(targetLabel)
+        {
+            debug("Testing scroll-snap snap for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeSnap = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeSnap = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
+            eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollSnap(targetLabel); });
+        }
+
+        function checkForScrollGlide(targetLabel)
+        {
+            var divTarget = document.getElementById(targetLabel);
+
+            var actualPosition = divTarget.scrollTop;
+            var expectedPosition = divTarget.clientHeight;
+            if (targetLabel == 'horizontalTarget') {
+                actualPosition = divTarget.scrollLeft;
+                expectedPosition = divTarget.clientWidth;
+            }
+
+            // The div should have scrolled (glided) to the next snap point.
+            if (actualPosition == expectedPosition)
+                testPassed("div scrolled to next window.");
+            else
+                testFailed("div did not honor snap points. Expected " + expectedPosition + ", but got " + actualPosition);
+
+            setTimeout(scrollSnapTest(targetLabel), 0);
+        }
+
+        function scrollGlideTest(targetLabel)
+        {
+            debug("Testing scroll-snap glide for " + targetLabel + ":");
+            var divTarget = document.getElementById(targetLabel);
+            var dx = 0;
+            var dy = 0;
+            if (targetLabel == 'horizontalTarget') {
+                divScrollPositionBeforeGlide = divTarget.scrollLeft;
+                dx = -1;
+            } else {
+                divScrollPositionBeforeGlide = divTarget.scrollTop;
+                dy = -1;
+            }
+
+            var windowPosition = locationInWindowCoordinates(divTarget);
+
+            var startPosX = windowPosition.x + 0.5 * divTarget.clientWidth;
+            var startPosY = windowPosition.y + 0.5 * divTarget.clientHeight;
+            eventSender.mouseMoveTo(startPosX, startPosY);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'began', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'changed', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'begin', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(dx, dy, 'none', 'continue', false);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end', false);
+            eventSender.callAfterScrollingCompletes(function() { return checkForScrollGlide(targetLabel); });
+        }
+
+        function onLoad()
+        {
+            if (window.eventSender) {
+                window.jsTestIsAsync = true;
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+
+                eventSender.monitorWheelEvents();
+                setTimeout(scrollGlideTest('horizontalTarget'), 0);
+            } else {
+                var messageLocationH = document.getElementById('itemH0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>This test is better run under DumpRenderTree.<br/>To manually test it, place the mouse pointer<br/>"
+                    + "inside the red region at the top of the page,<br/>and then use the mouse wheel or a two-finger<br/>swipe to make a"
+                    + "small swipe gesture with<br/>some momentum.<br/><br/>"
+                    + "The region should scroll to show a green region.<br/><br/>"
+                    + "Next, perform a small scroll gesture that does<br/>not involve momentum. You should begin to<br/>see one of the colors "
+                    + "to the side of the current<br/>green box. When you release the wheel, the<br/>region should scroll back to a single color.";
+                messageLocationH.appendChild(message);
+
+                var messageLocationV = document.getElementById('itemV0');
+                var message = document.createElement('div');
+                message.innerHTML = "<p>You should also be able to repeat these tests steps for this vertical region.<br/>"
+                messageLocationV.appendChild(message);
+            }
+        }
+        </script>
+    </head>
+    <body onload="onLoad();">
+        <div style="position: relative; width: 300px">
+            <div>Tests that the scroll-snap feature works properly in overflow regions.</div>
+            <div class="horizontalGallery" id="horizontalTarget" style="-webkit-transform: rotate(20deg)">
+                <div class="horizontalGalleryDrawer">
+                    <div id="itemH0" class="colorBox"></div>
+                    <div id="itemH1" class="colorBox"></div>
+                    <div id="itemH2" class="colorBox"></div>
+                    <div id="itemH3" class="colorBox"></div>
+                    <div id="itemH4" class="colorBox"></div>
+                    <div id="itemH5" class="colorBox"></div>
+                </div>
+            </div>
+            <div class="verticalGallery" id="verticalTarget" style="-webkit-transform: rotate(-20deg)">
+                <div class="verticalGalleryDrawer">
+                    <div id="itemV0" class="colorBox"></div>
+                    <div id="itemV1" class="colorBox"></div>
+                    <div id="itemV2" class="colorBox"></div>
+                    <div id="itemV3" class="colorBox"></div>
+                    <div id="itemV4" class="colorBox"></div>
+                    <div id="itemV5" class="colorBox"></div>
+                </div>
+            </div>
+            <div id="console"></div>
+        </div>
+        <script src="../../../../../resources/js-test-post.js"></script>
+    </body>
+</html>
\ No newline at end of file
index edab5bf207fb6ee665671cb1712d7738213b304b..b7de552968dfb975e05fb9347f41331c814ef45a 100644 (file)
@@ -1,3 +1,39 @@
+2015-05-06  Brent Fulgham  <bfulgham@apple.com>
+
+        Scroll-snap points do not handle margins and padding propertly
+        https://bugs.webkit.org/show_bug.cgi?id=144647
+        <rdar://problem/20829473>
+
+        Reviewed by Simon Fraser.
+
+        The calculation of scroll snap points was incorrect because it did not account for margins or padding.
+        This was fixed by using the "paddingBoxRect" to represent the overall size of the view area, and
+        subtracting the relevant padding when computing the size of the scroll snap offsets.
+
+        Extend testing internals with accessor methods to retrieve string representations of the scroll snap
+        offsets and scroll snap coordinates computed during layout. These values are used in the new
+        'css3/scroll-snap/scroll-snap-offsets-and-coordinates.html' test.
+
+        New tests:
+        * css3/scroll-snap/scroll-snap-offsets-and-coordinates.html: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html: Added.
+        * platform/mac-wk2/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html: Added.
+
+        * css/LengthFunctions.h: Expose the 'valueForLength' method so we can use it in WebCoreTestSupport.
+        * dom/Node.h: Expose the 'renderBox' method so we can use it in WebCoreTestSupport.
+        * page/scrolling/AxisScrollSnapOffsets.cpp:
+        (WebCore::appendChildSnapOffsets): Use 'contentBoxRect' for overall size, so that we don't use padding
+        as part of our offset calculations.
+        (WebCore::updateSnapOffsetsForScrollableArea): Ditto.
+        * rendering/RenderBox.h: Expose 'canBeScrolledAndHasScrollableArea' for use in WebCoreTestSupport.
+        * rendering/style/RenderStyle.h: Expose 'scrollSnapCoordinates' for use in WebCoreTestSupport.
+        * testing/Internals.cpp:
+        (WebCore::appendOffsets): Helper function.
+        (WebCore::Internals::scrollSnapOffsets): Added.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2015-05-06  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Clean up TextRun constructors
index 20a4fc5893a8e8d418efdc91bf91718a23af47ca..57e4fa52e1d42cbf7a1a34d592e30dd8104797c2 100644 (file)
@@ -35,7 +35,7 @@ struct LengthSize;
 int minimumIntValueForLength(const Length&, LayoutUnit maximumValue, bool roundPercentages = false);
 int intValueForLength(const Length&, LayoutUnit maximumValue);
 LayoutUnit minimumValueForLength(const Length&, LayoutUnit maximumValue, bool roundPercentages = false);
-LayoutUnit valueForLength(const Length&, LayoutUnit maximumValue);
+WEBCORE_EXPORT LayoutUnit valueForLength(const Length&, LayoutUnit maximumValue);
 float floatValueForLength(const Length&, LayoutUnit maximumValue);
 WEBCORE_EXPORT float floatValueForLength(const Length&, float maximumValue);
 FloatSize floatSizeForLengthSize(const LengthSize&, const FloatSize&);
index e45a53a7db2231334d71b2e4214cd971114887e1..77a1ed852f8884f2c523e30e694ce3225ae78bec 100644 (file)
@@ -444,7 +444,7 @@ public:
     }
 
     // Use these two methods with caution.
-    RenderBox* renderBox() const;
+    WEBCORE_EXPORT RenderBox* renderBox() const;
     RenderBoxModelObject* renderBoxModelObject() const;
     
     // Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement).
index bcd1a8c1c08ccc353b7b91cfc255b4577b54831f..75926306406e18559587a553ddf822ea424405c8 100644 (file)
@@ -43,12 +43,17 @@ static void appendChildSnapOffsets(HTMLElement& parent, bool shouldAddHorizontal
     // FIXME: Instead of traversing all children, register children with snap coordinates before appending to snapOffsetSubsequence.
     for (auto& child : childrenOfType<Element>(parent)) {
         if (RenderBox* box = child.renderBox()) {
-            LayoutUnit viewWidth = box->width();
-            LayoutUnit viewHeight = box->height();
+            const auto& scrollSnapCoordinates = box->style().scrollSnapCoordinates();
+            if (scrollSnapCoordinates.isEmpty())
+                continue;
+
+            LayoutRect viewSize = box->contentBoxRect();
+            LayoutUnit viewWidth = viewSize.width();
+            LayoutUnit viewHeight = viewSize.height();
             FloatPoint position = box->localToContainerPoint(FloatPoint(), parent.renderBox());
             LayoutUnit left = position.x();
             LayoutUnit top = position.y();
-            for (auto& coordinate : box->style().scrollSnapCoordinates()) {
+            for (auto& coordinate : scrollSnapCoordinates) {
                 LayoutUnit lastPotentialSnapPositionX = left + valueForLength(coordinate.width(), viewWidth);
                 if (shouldAddHorizontalChildOffsets && lastPotentialSnapPositionX > 0)
                     horizontalSnapOffsetSubsequence.append(lastPotentialSnapPositionX);
@@ -126,8 +131,9 @@ void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElem
         return;
     }
 
-    LayoutUnit viewWidth = scrollingElementBox.width();
-    LayoutUnit viewHeight = scrollingElementBox.height();
+    LayoutRect viewSize = scrollingElementBox.contentBoxRect();
+    LayoutUnit viewWidth = viewSize.width();
+    LayoutUnit viewHeight = viewSize.height();
     LayoutUnit scrollWidth = scrollingElementBox.scrollWidth();
     LayoutUnit scrollHeight = scrollingElementBox.scrollHeight();
     bool canComputeHorizontalOffsets = scrollWidth > 0 && viewWidth > 0 && viewWidth < scrollWidth;
@@ -145,7 +151,7 @@ void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElem
     Vector<LayoutUnit> verticalSnapOffsetSubsequence;
 
     bool scrollSnapPointsXUsesElements = styleUsesElements(ScrollEventAxis::Horizontal, scrollingElementStyle);
-    bool scrollSnapPointsYUsesElements = styleUsesElements(ScrollEventAxis::Vertical , scrollingElementStyle);
+    bool scrollSnapPointsYUsesElements = styleUsesElements(ScrollEventAxis::Vertical, scrollingElementStyle);
 
     if (scrollSnapPointsXUsesElements || scrollSnapPointsYUsesElements) {
         bool shouldAddHorizontalChildOffsets = scrollSnapPointsXUsesElements && canComputeHorizontalOffsets;
index 337cc5cec9a3264728e34b9fec2e15980e05da97..6cfa25db85f7088c801c092c16b5676098ab1e6c 100644 (file)
@@ -463,7 +463,7 @@ public:
     int scrollbarLogicalHeight() const { return style().isHorizontalWritingMode() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); }
     virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Element** stopElement = nullptr, RenderBox* startBox = nullptr, const IntPoint& wheelEventAbsolutePoint = IntPoint());
     virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Element** stopElement = nullptr);
-    bool canBeScrolledAndHasScrollableArea() const;
+    WEBCORE_EXPORT bool canBeScrolledAndHasScrollableArea() const;
     virtual bool canBeProgramaticallyScrolled() const;
     virtual void autoscroll(const IntPoint&);
     bool canAutoscroll() const;
index 34ec4b342953a597496a7f4b85d22e8af5719969..dbc2c43de5b65f89dafc3b7eed3744c18f4c0a03 100644 (file)
@@ -1112,7 +1112,7 @@ public:
     const ScrollSnapPoints* scrollSnapPointsX() const;
     const ScrollSnapPoints* scrollSnapPointsY() const;
     const LengthSize& scrollSnapDestination() const;
-    const Vector<LengthSize>& scrollSnapCoordinates() const;
+    WEBCORE_EXPORT const Vector<LengthSize>& scrollSnapCoordinates() const;
 #endif
 
 #if ENABLE(TOUCH_EVENTS)
index 225cd08ac2a7178522307043d295c9b535ca9d0f..4ca3402d57df71d3ae00bbf78e6c922f04c61f80 100644 (file)
@@ -2777,4 +2777,61 @@ MockContentFilterSettings& Internals::mockContentFilterSettings()
 }
 #endif
 
+#if ENABLE(CSS_SCROLL_SNAP)
+static void appendOffsets(StringBuilder& builder, const Vector<LayoutUnit>& snapOffsets)
+{
+    bool justStarting = true;
+
+    builder.append("{ ");
+    for (auto& coordinate : snapOffsets) {
+        if (!justStarting)
+            builder.append(", ");
+        else
+            justStarting = false;
+        
+        builder.append(String::number(coordinate.toUnsigned()));
+    }
+    builder.append(" }");
+}
+    
+String Internals::scrollSnapOffsets(Element* element, ExceptionCode& ec)
+{
+    if (!element) {
+        ec = INVALID_ACCESS_ERR;
+        return String();
+    }
+
+    if (!element->renderBox())
+        return String();
+
+    RenderBox& box = *element->renderBox();
+    if (!box.canBeScrolledAndHasScrollableArea()) {
+        ec = INVALID_ACCESS_ERR;
+        return String();
+    }
+
+    if (!box.layer())
+        return String();
+    
+    ScrollableArea& scrollableArea = *box.layer();
+    
+    StringBuilder result;
+
+    if (scrollableArea.horizontalSnapOffsets()) {
+        result.append("horizontal = ");
+        appendOffsets(result, *scrollableArea.horizontalSnapOffsets());
+    }
+
+    if (scrollableArea.verticalSnapOffsets()) {
+        if (result.length())
+            result.append(", ");
+
+        result.append("vertical = ");
+        appendOffsets(result, *scrollableArea.verticalSnapOffsets());
+    }
+
+    return result.toString();
+}
+#endif
+
 }
index 84a28eff3d49a8b8f8db01f047086ba91b878436..1bab978289f3abd9779b847532e1fdca17802f4d 100644 (file)
@@ -400,6 +400,10 @@ public:
     MockContentFilterSettings& mockContentFilterSettings();
 #endif
 
+#if ENABLE(CSS_SCROLL_SNAP)
+    String scrollSnapOffsets(Element*, ExceptionCode&);
+#endif
+
 private:
     explicit Internals(Document*);
     Document* contextDocument() const;
index 25fed495949ff8641052e7a60445735f9e724731..02cd08445f472ec8328938552f9c3f0a2792bc9a 100644 (file)
@@ -359,4 +359,8 @@ enum ResourceLoadPriority {
     void queueMicroTask(long testNumber);
 
     [Conditional=CONTENT_FILTERING] readonly attribute MockContentFilterSettings mockContentFilterSettings;
+
+#if defined(ENABLE_CSS_SCROLL_SNAP) && ENABLE_CSS_SCROLL_SNAP
+    [RaisesException] DOMString scrollSnapOffsets(Element element);
+#endif
 };