summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-position/sticky
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/css/css-position/sticky')
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-002.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-003.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top-ref.html21
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top.html53
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn-ref.html50
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn.html58
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table-ref.html48
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table.html71
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-details-crash.html10
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001-ref.html2
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001.html18
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002-ref.html9
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002.html17
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-003.html18
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004-ref.html9
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004.html17
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-002.html53
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-003.html44
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe-ref.html93
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe.html54
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-ref.html64
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor.html89
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-001.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-002.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-003.html50
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-004.html51
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox-ref.html63
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox.html83
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset-ref.html40
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset.html80
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-get-bounding-client-rect.html99
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-grid-ref.html76
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-grid.html95
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink-ref.html37
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container-ref.html40
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container.html40
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-inflow-position.html58
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-inline-ref.html69
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-inline.html109
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-input-box-gets-focused-after-scroll.html30
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2-ref.html56
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2.tentative.html59
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-ref.html51
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top.tentative.html54
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-left-002.html96
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-left-003.html96
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-left.html43
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-margins.html49
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-bottom.html68
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline-ref.html74
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline.html119
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-left.html75
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-right.html69
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table-ref.html66
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table.html133
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-thead-th.html133
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-top.html74
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-overflow.html44
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-print.html13
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-top-left.html78
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container-ref.html38
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-hidden.html86
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-padding.html54
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-parsing.html59
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering-ref.html104
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering.html159
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-right-002.html96
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-right-003.html96
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-right.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-root-scroller.html31
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-offset-clamp-crash.html20
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition-ref.html10
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition.html20
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html33
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html33
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scrollIntoView.html41
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-scrolled-remove-sibling.html99
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-002.html47
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-ref.html17
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context.html46
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts-ref.html49
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts.html76
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom.html109
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left.html118
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right.html118
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top.html109
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom.html124
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom.html127
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left.html118
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right.html118
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top.html127
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top.html124
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top-ref.html62
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-top-002.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-top-003.html121
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom-003.html102
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom.html57
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-top.html43
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms-translate.html45
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms.html49
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes-ref.html58
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes.html73
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-bottom-002-ref.html78
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-fixed-ancestor-002-ref.html20
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-left-002-ref.html74
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-right-002-ref.html68
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-002-ref.html83
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-and-bottom-003-ref.html83
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/sticky-after-input.html49
-rw-r--r--testing/web-platform/tests/css/css-position/sticky/support/100x100-red.pngbin0 -> 510 bytes
126 files changed, 8272 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-002.html
new file mode 100644
index 0000000000..980de2e651
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-002.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with bottom offset specified with px unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-bottom-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: left 75px;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: left 50px;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left top;
+ }
+
+ div.vertical-spacer
+ {
+ height: 100px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ bottom: 100px;
+ height: 100px;
+ position: sticky;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-003.html
new file mode 100644
index 0000000000..649dfc9d36
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom-003.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with bottom offset specified with percentage unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-bottom-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: left 75px;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: left 50px;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left top;
+ }
+
+ div.vertical-spacer
+ {
+ height: 100px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ bottom: 40%;
+ height: 100px;
+ position: sticky;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom.html
new file mode 100644
index 0000000000..9cfae6d3ba
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-bottom.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the bottom constraint</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their bottom anchor after scrolling" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('bottom', 25);
+ elements.scroller.scrollTop = 300;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+}, 'before reaching the sticking point the sticky box should not be offset');
+
+test(() => {
+ const elements = setupStickyTest('bottom', 25);
+ elements.scroller.scrollTop = 100;
+
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const nonStickyBottomY = nonStickyTopY + elements.sticky.clientHeight;
+ const targetBottomY = elements.scroller.clientHeight +
+ elements.scroller.scrollTop - 25;
+ const stickyOffset = nonStickyBottomY - targetBottomY;
+
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY - stickyOffset);
+}, 'after reaching the sticking point the sticky box should be offset');
+
+test(() => {
+ const elements = setupStickyTest('bottom', 25);
+ elements.scroller.scrollTop = 0;
+ assert_equals(elements.sticky.offsetTop, elements.container.offsetTop);
+}, 'the sticky box should not be pushed outside its containing block');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top-ref.html
new file mode 100644
index 0000000000..8ccc1548ef
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Reference for sticky elements should invalidate when top/left/bottom/right changes</title>
+
+<style>
+.box {
+ /* Triggers promotion without creating stacking context. */
+ backface-visibility: hidden;
+ background: green;
+ position: sticky;
+ top: 200px;
+ width: 100px;
+ height: 100px;
+}
+
+.spacer {
+ height: 200vh;
+}
+</style>
+
+<div class="box"></div>
+<div class="spacer"></div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top.html
new file mode 100644
index 0000000000..6a327ccd56
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-change-top.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+<title>Sticky elements should invalidate when top/left/bottom/right changes</title>
+<link rel="match" href="position-sticky-change-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<link rel="help" href="https://crbug.com/939632">
+<meta name="assert" content="This test checks that sticky elements are invalidated correctly when top/left/bottom/right change "/>
+
+<script src="/common/reftest-wait.js"></script>
+<script src="../resources/ref-rectangle.js"></script>
+
+<!--
+ It is important for this test that the sticky element is viewport-bound, and
+ that multiple animation frames pass before changing the style.
+-->
+<style>
+.marker {
+ background: red;
+ position: absolute;
+ top: 200px;
+ height: 100px;
+ width: 100px;
+}
+
+.sticky {
+ /* Triggers promotion without creating stacking context. */
+ backface-visibility: hidden;
+ background: green;
+ position: sticky;
+ top: 0;
+ width: 100px;
+ height: 100px;
+}
+
+.spacer {
+ height: 200vh;
+}
+</style>
+<div class="marker"></div>
+
+<div class="sticky"></div>
+<div class="spacer"></div>
+
+<script>
+requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.querySelector('.sticky').style.setProperty('top', '200px');
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+ takeScreenshot();
+ });
+});
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn-ref.html
new file mode 100644
index 0000000000..ccbeaa8ca7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn-ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Reference for multicolumn under position:sticky should be positioned correctly</title>
+<style>
+ body {
+ margin: 0;
+ }
+ #scroller {
+ overflow-y: scroll;
+ width: 200px;
+ height: 200px;
+ }
+ #relative {
+ position: relative;
+ top: 100px;
+ margin: 10px;
+ }
+ #child {
+ width: 100px;
+ height: 100px;
+ background: green;
+ }
+ #contents {
+ position: relative;
+ top: 10%;
+ left: 10%;
+ width: 80%;
+ height: 80%;
+ background: lightgreen;
+ }
+ #spacer {
+ height: 400px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="relative">
+ <div id="child">
+ <div id="contents"></div>
+ </div>
+ </div>
+ <div id="spacer"></div>
+</div>
+
+<div>You should see a light green box above with a dark green border, no blue should be visible.</div>
+
+<script>
+ window.addEventListener('load', function() {
+ scroller.scrollTop = 100;
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn.html
new file mode 100644
index 0000000000..14bb695f1a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-child-multicolumn.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Multicolumn under position:sticky should be positioned correctly</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<link rel="match" href="position-sticky-child-multicolumn-ref.html" />
+<link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org" />
+<meta name="assert" content="This test checks that a multicolumn element is positioned relative to a sticky position" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+ body {
+ margin: 0;
+ }
+ #scroller {
+ overflow-y: scroll;
+ width: 200px;
+ height: 200px;
+ }
+ #sticky {
+ position: sticky;
+ top: 10px;
+ margin: 10px;
+ }
+ #multicolumn {
+ width: 100px;
+ height: 100px;
+ background: green;
+ columns: 1;
+ }
+ #contents {
+ margin-left: 10%;
+ margin-top: 10%;
+ width: 80%;
+ height: 80%;
+ background: lightgreen;
+ }
+ #spacer {
+ height: 400px;
+ }
+</style>
+
+<div id="scroller">
+ <div id="sticky">
+ <div id="multicolumn">
+ <div id="contents"></div>
+ </div>
+ </div>
+ <div id="spacer"></div>
+</div>
+
+<div>You should see a light green box above with a dark green border, no blue should be visible.</div>
+
+<script>
+ window.addEventListener('load', function() {
+ scroller.scrollTop = 100;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table-ref.html
new file mode 100644
index 0000000000..32115516ef
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table-ref.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>position:sticky should work for elements with display: table* containing blocks</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky works for elements with containing blocks that have display: table*" />
+
+<style>
+.group {
+ display: inline-block;
+}
+
+.container {
+ height: 1000px;
+ width: 50px;
+ margin-right: 10px;
+}
+
+.scroll-container {
+ height: 300px;
+ width: 500px;
+ overflow: hidden;
+}
+
+.sticky {
+ height: 50px;
+ width: 50px;
+ background: green;
+}
+</style>
+
+<div class="scroll-container">
+ <div class="group">
+ <div class="container" style="display: table-cell;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+
+ <div class="group">
+ <div class="container" style="display: table-row;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+
+ <div class="group">
+ <div class="container" style="display: table;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table.html
new file mode 100644
index 0000000000..e9c016e938
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-contained-by-display-table.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>position:sticky should work for elements with display: table* containing blocks</title>
+<link rel="match" href="position-sticky-contained-by-display-table-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky works for elements with containing blocks that have display: table*" />
+
+<style>
+.group {
+ display: inline-block;
+}
+
+.container {
+ height: 1000px;
+ width: 50px;
+ margin-right: 10px;
+}
+
+#scroll-container {
+ height: 300px;
+ width: 500px;
+ overflow: hidden;
+ position: relative;
+}
+
+.sticky {
+ position: sticky;
+ height: 50px;
+ width: 50px;
+ background: green;
+ top: 0;
+}
+
+#scroll-indicator {
+ display: inline-block;
+ position: absolute;
+ top: 0px;
+ width: 300px;
+ height: 50px;
+ background: red;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroll-container').scrollTop = 300;
+});
+</script>
+
+
+<div id="scroll-container">
+ <div class="group">
+ <div class="container" style="display: table-cell;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+
+ <div class="group">
+ <div class="container" style="display: table-row;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+
+ <div class="group">
+ <div class="container" style="display: table;">
+ <div class="sticky"></div>
+ </div>
+ </div>
+
+ <!-- This is here to validate that the container does scroll. -->
+ <div id="scroll-indicator" style=""></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-details-crash.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-details-crash.html
new file mode 100644
index 0000000000..65b7ea4880
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-details-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1470149">
+<details>
+ <div id="target" style="position: sticky; top: 0px;"></div>
+</details>
+<script>
+document.execCommand('selectAll');
+document.getElementById('target').remove();
+document.body.style.height = '100%';
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001-ref.html
new file mode 100644
index 0000000000..0d760d18dc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; background: green"></div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001.html
new file mode 100644
index 0000000000..dd58327388
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-001.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>CSS Positioned Layout Test: a sticky element contained by fixed under a scroller not containing fixed</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="help" href="https://crbug.com/881555">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3185">
+<link rel="match" href="position-sticky-escape-scroller-001-ref.html">
+<div id="scroller" style="width: 200px; height: 100px; overflow: hidden">
+ <div style="height: 100px; background: red"></div>
+ <div style="position: fixed; height: 400px">
+ <div style="position: sticky; top: 0; width: 200px; height: 100px; background: green"></div>
+ </div>
+ <div style="height: 100px; background: red"></div>
+ <div style="height: 100px; background: green"></div>
+ <div style="height: 1000px; background: red"></div>
+</div>
+<script>
+scroller.scrollTop = 200;
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002-ref.html
new file mode 100644
index 0000000000..2fa0a2a9ed
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>body { margin: 0 }</style>
+<div style="height: 3200px"></div>
+<div style="position: fixed; top: 100px; left: 0;
+ width: 200px; height: 200px; background: green"></div>
+<script>
+window.scrollTo(0, 2000);
+</script>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002.html
new file mode 100644
index 0000000000..225012b9ec
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-002.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSS Positioned Layout Test: a sticky element contained by fixed under a scroller not containing fixed</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="help" href="https://crbug.com/881555">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3185">
+<link rel="match" href="position-sticky-escape-scroller-002-ref.html">
+<style>body { margin: 0 }</style>
+<div id="scroller" style="width: 200px; height: 200px; overflow: scroll">
+ <div style="position: fixed; top: 100px; height: 2000px">
+ <div style="position: sticky; top: 0; width: 200px; height: 200px; background: green"></div>
+ </div>
+ <div style="height: 1000px; background: red"></div>
+</div>
+<div style="height: 3000px"></div>
+<script>
+window.scrollTo(0, 2000);
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-003.html
new file mode 100644
index 0000000000..434b279705
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-003.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>CSS Positioned Layout Test: a sticky element contained by absolute under a scroller not containing absolute</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="help" href="https://crbug.com/881555">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3185">
+<link rel="match" href="position-sticky-escape-scroller-001-ref.html">
+<div id="scroller" style="width: 200px; height: 100px; overflow: hidden">
+ <div style="height: 100px; background: red"></div>
+ <div style="position: absolute; height: 400px">
+ <div style="position: sticky; top: 0; width: 200px; height: 100px; background: green"></div>
+ </div>
+ <div style="height: 100px; background: red"></div>
+ <div style="height: 100px; background: green"></div>
+ <div style="height: 1000px; background: red"></div>
+</div>
+<script>
+scroller.scrollTop = 200;
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004-ref.html
new file mode 100644
index 0000000000..fa9f2b7dc0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>body { margin: 0 }</style>
+<div style="height: 3200px"></div>
+<div style="position: fixed; top: 0; left: 0;
+ width: 200px; height: 200px; background: green"></div>
+<script>
+window.scrollTo(0, 1000);
+</script>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004.html
new file mode 100644
index 0000000000..ed14d62ac4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-escape-scroller-004.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSS Positioned Layout Test: a sticky element contained by absolute under a scroller not containing absolute</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="help" href="https://crbug.com/881555">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3185">
+<link rel="match" href="position-sticky-escape-scroller-004-ref.html">
+<style>body { margin: 0 }</style>
+<div id="scroller" style="width: 200px; height: 200px; overflow: scroll">
+ <div style="position: absolute; top: 100px; height: 2000px">
+ <div style="position: sticky; top: 0; width: 200px; height: 200px; background: green"></div>
+ </div>
+ <div style="height: 1000px; background: red"></div>
+</div>
+<div style="height: 3000px"></div>
+<script>
+window.scrollTo(0, 1000);
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-002.html
new file mode 100644
index 0000000000..dc3c383ea5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-002.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Positioned Layout Test: a sticky element inside a fixed ancestor</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="match" href="reference/position-sticky-fixed-ancestor-002-ref.html">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+
+ <meta name="flags" content="">
+
+ <style>
+ html, body, div
+ {
+ height: 100%;
+ margin: 0;
+ width: 100%;
+ }
+
+ div#positioned-container
+ {
+ background-color: red;
+ color: yellow;
+ font-size: 40vh;
+ position: absolute;
+ }
+
+ div#sticky
+ {
+ background-color: green;
+ bottom: 0;
+ color: white;
+ position: sticky;
+ }
+ </style>
+
+ <script type="text/javascript">
+ function startTest()
+ {
+ document.getElementById("positioned-container").style.position = "fixed";
+ }
+ </script>
+
+ <body onload="startTest();">
+
+ <div id="positioned-container">
+
+ <div id="vertical-spacer">FAIL</div>
+
+ <div id="sticky">PASS</div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-003.html
new file mode 100644
index 0000000000..382112cae4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-003.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Positioned Layout Test: a sticky element inside a fixed ancestor</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="match" href="reference/position-sticky-fixed-ancestor-002-ref.html">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+
+ <meta name="flags" content="">
+
+ <style>
+ html, body, div
+ {
+ height: 100%;
+ margin: 0;
+ width: 100%;
+ }
+
+ div#fixed-container
+ {
+ background-color: red;
+ color: yellow;
+ font-size: 40vh;
+ position: fixed;
+ }
+
+ div#sticky
+ {
+ background-color: green;
+ bottom: 0;
+ color: white;
+ position: sticky;
+ }
+ </style>
+
+ <div id="fixed-container">
+
+ <div id="vertical-spacer">FAIL</div>
+
+ <div id="sticky">PASS</div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe-ref.html
new file mode 100644
index 0000000000..37372ceca1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe-ref.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>Sticky elements inside fixed ancestors and iframe shouldn't account for scroll</title>
+
+<style>
+ body,html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ iframe {
+ margin: 10px;
+ width: 90%;
+ height: 90%;
+ border: 1px solid black;
+ }
+
+ .spacer {
+ height: 120vh;
+ }
+</style>
+
+<div class="spacer"></div>
+<iframe srcdoc="
+ <!DOCTYPE html>
+ <title>Reference for sticky elements inside fixed ancestors shouldn't account for scroll</title>
+ <style>
+ body,html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ .sticky {
+ background: green;
+ width: 100px;
+ height: 10%;
+ }
+
+ .spacer {
+ height: calc(25vh - 10%);
+ }
+ .long {
+ height: 600vh;
+ }
+
+ .position-parent {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ top: 100vh;
+ background-color: lightgrey;
+ }
+
+ .container {
+ width: 100px;
+ height: 100%;
+ background-color: grey;
+ }
+
+ button {
+ position: fixed;
+ left: 20px;
+ top: 20px;
+ }
+
+ .fixed {
+ position: fixed;
+ top: 25vh;
+ }
+ </style>
+
+ <div class='position-parent fixed'>
+ <div class='container'>
+ <div class='spacer'></div>
+ <div class='sticky'></div>
+ </div>
+ </div>
+ <div class='long'></div>
+ <button id='button'>Toggle Fixed</button>
+ <script>
+ window.scrollTo(0, document.querySelector('.long').clientHeight);
+ </script>
+"></iframe>
+<div class="spacer"></div>
+
+<script>
+ const child = document.querySelector('iframe');
+ child.scrollIntoView();
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe.html
new file mode 100644
index 0000000000..e50f5c829b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-iframe.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+<title>Sticky elements inside fixed ancestors and iframe shouldn't account for scroll</title>
+<link rel="match" href="position-sticky-fixed-ancestor-iframe-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<link rel="help" href="https://crbug.com/1019142">
+<meta name="assert" content="This test checks that a sticky element inside a fixed subtree and iframe doesn't scroll with the viewport "/>
+
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+ body,html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ iframe {
+ margin: 10px;
+ width: 90%;
+ height: 90%;
+ border: 1px solid black;
+ }
+
+ .spacer {
+ height: 120vh;
+ }
+</style>
+
+<div class="spacer"></div>
+<iframe src="../resources/position-sticky-fixed-ancestor-iframe-child.html"></iframe>
+<div class="spacer"></div>
+
+<script>
+ const child = document.querySelector('iframe');
+ child.scrollIntoView();
+ window.onload = () => {
+ const childDoc = child.contentDocument;
+ function toggleFixed() {
+ childDoc.querySelector('.position-parent').classList.toggle('fixed');
+ }
+
+ childDoc.getElementById('button').addEventListener('click', toggleFixed);
+
+ requestAnimationFrame(() => {
+ childDoc.scrollingElement.scrollTop = childDoc.querySelector('.long').clientHeight;
+ requestAnimationFrame(() => {
+ toggleFixed();
+ takeScreenshot();
+ });
+ });
+ };
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-ref.html
new file mode 100644
index 0000000000..fe404b5bab
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor-ref.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>Reference for sticky elements inside fixed ancestors shouldn't account for scroll</title>
+<style>
+ body,html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ .sticky {
+ background: green;
+ width: 100px;
+ height: 10%;
+ }
+
+ .spacer {
+ height: calc(25vh - 10%);
+ }
+ .long {
+ height: 600vh;
+ }
+
+ .position-parent {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ top: 100vh;
+ background-color: lightgrey;
+ }
+
+ .container {
+ width: 100px;
+ height: 100%;
+ background-color: grey;
+ }
+
+ button {
+ position: fixed;
+ left: 20px;
+ top: 20px;
+ }
+
+ .fixed {
+ position: fixed;
+ top: 25vh;
+ }
+</style>
+
+<div>You should see a green box below. No blue should be visible.</div>
+<div class="position-parent fixed">
+ <div class="container">
+ <div class="spacer"></div>
+ <div class="sticky"></div>
+ </div>
+</div>
+<div class="long"></div>
+<button id="button">Toggle Fixed</button>
+
+<script>
+ window.scrollTo(0, document.querySelector('.long').clientHeight);
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor.html
new file mode 100644
index 0000000000..52760992b1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fixed-ancestor.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html class='reftest-wait'>
+<title>Sticky elements inside fixed ancestors shouldn't account for scroll</title>
+<link rel="match" href="position-sticky-fixed-ancestor-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<link rel="help" href="https://crbug.com/1019142">
+<meta name="assert" content="This test checks that a sticky element inside a fixed subtree doesn't scroll with the viewport "/>
+
+<script src="/common/reftest-wait.js"></script>
+<script src="../resources/ref-rectangle.js"></script>
+
+
+<style>
+ body,html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ .sticky {
+ background: green;
+ position: sticky;
+ bottom: 50vh;
+ width: 100px;
+ height: 10%;
+ }
+
+ .spacer {
+ height: 90%;
+ }
+ .long {
+ height: 600vh;
+ }
+
+ .position-parent {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ top: 100vh;
+ background-color: lightgrey;
+ }
+
+ .container {
+ width: 100px;
+ height: 100%;
+ background-color: grey;
+ }
+
+ button {
+ position: fixed;
+ left: 20px;
+ top: 20px;
+ }
+
+ .fixed {
+ position: fixed;
+ top: 25vh;
+ }
+</style>
+
+<div>You should see a green box below. No blue should be visible.</div>
+<div class="position-parent">
+ <div class="container">
+ <div class="spacer"></div>
+ <div class="sticky"></div>
+ </div>
+</div>
+<div class="long"></div>
+<button id="button">Toggle Fixed</button>
+<script>
+ function toggleFixed() {
+ document.querySelector('.position-parent').classList.toggle('fixed');
+ }
+
+ document.getElementById('button').addEventListener('click', toggleFixed);
+
+ requestAnimationFrame(() => {
+ window.scrollTo(0, document.querySelector('.long').clientHeight);
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+ requestAnimationFrame(() => {
+ toggleFixed();
+ takeScreenshot();
+ });
+ });
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-001.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-001.html
new file mode 100644
index 0000000000..1b1f9d0afb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-001.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#stickypos-insets">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1377072">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="This test verifies that the sticky flex item reserves its in-flow space in the scroll container's overflow area.">
+
+<style>
+#flex-scroller {
+ overflow: hidden;
+ inline-size: 100px;
+ block-size: 100px;
+ display: flex;
+ flex-direction: column;
+ background-color: green;
+}
+
+#non-sticky {
+ flex: 0 0 80px;
+ background-color: red;
+}
+
+#sticky {
+ position: sticky;
+ flex: 0 0 100px;
+ bottom: 0;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="flex-scroller">
+ <div id="non-sticky"></div>
+ <div id="sticky"></div>
+</div>
+
+<script>
+// Scroll the scroll container down to the bottom.
+document.getElementById("flex-scroller").scrollTop="1000";
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-002.html
new file mode 100644
index 0000000000..5b4c81df67
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-002.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#stickypos-insets">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1377072">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="This test verifies that the sticky flex item reserves its in-flow space in the scroll container's overflow area.">
+
+<style>
+#flex-scroller {
+ overflow: hidden;
+ inline-size: 100px;
+ block-size: 100px;
+ display: flex;
+ flex-direction: row;
+ background-color: green;
+}
+
+#non-sticky {
+ flex: 0 0 80px;
+ background-color: red;
+}
+
+#sticky {
+ position: sticky;
+ flex: 0 0 100px;
+ right: 0;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="flex-scroller">
+ <div id="non-sticky"></div>
+ <div id="sticky"></div>
+</div>
+
+<script>
+// Scroll the scroll container to the far right.
+document.getElementById("flex-scroller").scrollLeft="1000";
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-003.html
new file mode 100644
index 0000000000..50e0e25980
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-003.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#stickypos-insets">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1377072">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="This test verifies that the sticky flex item reserves its in-flow space in the scroll container's overflow area.">
+
+<style>
+#scroller {
+ overflow: hidden;
+ inline-size: 100px;
+ block-size: 100px;
+ background-color: green;
+}
+
+#flex {
+ display: flex;
+ flex-direction: column;
+
+ /* Use a small block-size so that the flex items overflow the flex container.
+ It's necessary to trigger the bug. */
+ block-size: 10px;
+}
+
+#non-sticky {
+ flex: 0 0 80px;
+ background-color: red;
+}
+
+#sticky {
+ position: sticky;
+ flex: 0 0 100px;
+ bottom: 0;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="scroller">
+ <div id="flex">
+ <div id="non-sticky"></div>
+ <div id="sticky"></div>
+ </div>
+</div>
+
+<script>
+// Scroll the scroll container down to the bottom.
+document.getElementById("scroller").scrollTop="1000";
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-004.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-004.html
new file mode 100644
index 0000000000..68b933a319
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flex-item-004.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#stickypos-insets">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1377072">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="This test verifies that the sticky flex item reserves its in-flow space in the scroll container's overflow area.">
+
+<style>
+#scroller {
+ overflow: hidden;
+ inline-size: 100px;
+ block-size: 100px;
+ background-color: green;
+}
+
+#flex {
+ display: flex;
+ flex-direction: row;
+
+ /* Use a small inline-size so that the flex items overflow the flex container.
+ It's necessary to trigger the bug. */
+ inline-size: 10px;
+ block-size: 100px;
+}
+
+#non-sticky {
+ flex: 0 0 80px;
+ background-color: red;
+}
+
+#sticky {
+ position: sticky;
+ flex: 0 0 100px;
+ right: 0;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="scroller">
+ <div id="flex">
+ <div id="non-sticky"></div>
+ <div id="sticky"></div>
+ </div>
+</div>
+
+<script>
+// Scroll the scroll container to the far right.
+document.getElementById("scroller").scrollLeft="1000";
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox-ref.html
new file mode 100644
index 0000000000..b78c784de1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should work correctly with flexbox</title>
+
+<style>
+.scroller {
+ overflow: scroll;
+ width: 350px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.flex-container {
+ width: 600px;
+ position: relative;
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.green {
+ background-color: green;
+}
+
+.flex-item {
+ height: 85px;
+ width: 100px;
+ display: flex;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<p>You should see three green boxes of varying size below. There should be no red or blue.</p>
+
+<div id="scroller1" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox.html
new file mode 100644
index 0000000000..951de1e71c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-flexbox.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>position:sticky elements should work correctly with flexbox</title>
+<link rel="match" href="position-sticky-flexbox-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements interoperate correctly with flexbox" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.scroller {
+ overflow: scroll;
+ width: 350px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.flex-container {
+ width: 600px;
+ position: relative;
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.sticky {
+ position: sticky;
+ left: 50px;
+}
+
+.green {
+ background-color: green;
+}
+
+.flex-item {
+ width: 100px;
+ height: 85px;
+ display: flex;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ width: 100px;
+ height: 85px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<p>You should see three green boxes of varying size below. There should be no red or blue.</p>
+
+<div id="scroller1" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset-ref.html
new file mode 100644
index 0000000000..8b7a1f8a19
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<style>
+ .container {
+ width: 100px;
+ height: 100px;
+ overflow-y: scroll;
+ background: lightgreen;
+ display: inline-block;
+ }
+</style>
+
+<div class="container">
+ <div style="height: calc(300px + 50px + 10.10px);"></div>
+</div>
+
+<div class="container">
+ <div style="height: calc(300px + 50px + 10.25px);"></div>
+</div>
+
+<div class="container">
+ <div style="height: calc(300px + 50px + 10.50px);"></div>
+</div>
+
+<div class="container">
+ <div style="height: calc(300px + 50px + 10.75px);"></div>
+</div>
+
+<div class="container">
+ <div style="height: calc(300px + 50px + 10.90px);"></div>
+</div>
+
+<script>
+ window.onload = function() {
+ var containers = document.getElementsByClassName('container');
+ for (let i = 0; i < containers.length; i++) {
+ containers[i].scrollTo(0, 20);
+ }
+ };
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset.html
new file mode 100644
index 0000000000..79c29f4e87
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-fractional-offset.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="Position sticky with a fractional offset should not show a gap" />
+<link rel="match" href="position-sticky-fractional-offset-ref.html" />
+
+<style>
+ .sticky-container {
+ width: 100px;
+ height: 100px;
+ overflow-y: scroll;
+ background: red;
+ display: inline-block;
+ }
+
+ .sticky {
+ position: sticky;
+ top: 0;
+ height: 50px;
+ background: lightgreen;
+ }
+
+ .force-scroll {
+ height: 300px;
+ background: lightgreen;
+ }
+</style>
+
+<div class="sticky-container">
+ <div style="height: 10.10px;"></div>
+ <div class="sticky"></div>
+ <div class="force-scroll"></div>
+</div>
+
+<div class="sticky-container">
+ <div style="height: 10.25px;"></div>
+ <div class="sticky"></div>
+ <div class="force-scroll"></div>
+</div>
+
+<div class="sticky-container">
+ <div style="height: 10.50px;"></div>
+ <div class="sticky"></div>
+ <div class="force-scroll"></div>
+</div>
+
+<div class="sticky-container">
+ <div style="height: 10.75px;"></div>
+ <div class="sticky"></div>
+ <div class="force-scroll"></div>
+</div>
+
+<div class="sticky-container">
+ <div style="height: 10.90px;"></div>
+ <div class="sticky"></div>
+ <div class="force-scroll"></div>
+</div>
+
+<script>
+ window.onload = function() {
+ // Start with all containers scrolled to the top.
+ var containers = document.getElementsByClassName('sticky-container');
+ for (let i = 0; i < containers.length; i++) {
+ containers[i].scrollTo(0, 0);
+ }
+
+ // Wait for a full frame, then scroll all containers down so the sticky
+ // elements are stuck to the container. There should be no visible gap
+ // where the container's red background color is visible.
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ for (let i = 0; i < containers.length; i++) {
+ containers[i].scrollTo(0, 20);
+ }
+ document.documentElement.classList.remove('reftest-wait');
+ });
+ });
+ };
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-get-bounding-client-rect.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-get-bounding-client-rect.html
new file mode 100644
index 0000000000..18b2acfe16
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-get-bounding-client-rect.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>Sticky positioned element should be observable by getBoundingClientRect.</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that sticky positioned element
+should be observable by getBoundingClientRect." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.spacer {
+ width: 2000px;
+ height: 2000px;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ left: 20px;
+}
+</style>
+
+<div id="scroller1" class="container">
+ <div id="sticky1" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var sticky = document.getElementById('sticky1');
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+ document.getElementById('scroller1').scrollTop = 100;
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+ sticky.style.position = 'relative';
+ assert_equals(sticky.getBoundingClientRect().top, -50);
+ sticky.style.position = 'sticky';
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+}, 'sticky positioned element should be observable by getBoundingClientRect.');
+</script>
+
+<div id="scroller2" class="container">
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller2');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var sticky = document.createElement('div');
+ sticky.className = 'sticky box';
+ scroller.insertBefore(sticky, scroller.querySelector('.spacer'));
+
+ var sticky_bounds = sticky.getBoundingClientRect();
+ var scroller_bounds = scroller.getBoundingClientRect();
+ assert_equals(sticky_bounds.top, scroller_bounds.top + 50);
+ assert_equals(sticky_bounds.left, scroller_bounds.left + 20);
+}, 'getBoundingClientRect should be correct for sticky after script insertion');
+</script>
+
+<div id="scroller3" class="container">
+ <div id="sticky3" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller3');
+ var sticky = document.getElementById('sticky3');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var div = document.createElement('div');
+ div.style.height = '65px';
+ scroller.insertBefore(div, sticky);
+
+ var sticky_bounds = sticky.getBoundingClientRect();
+ var scroller_bounds = scroller.getBoundingClientRect();
+ assert_equals(sticky_bounds.top, scroller_bounds.top + 50);
+ assert_equals(sticky_bounds.left, scroller_bounds.left + 20);
+}, 'getBoundingClientRect should be correct for sticky after script-caused layout');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid-ref.html
new file mode 100644
index 0000000000..123eed311b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should work correctly with grid layout</title>
+
+<style>
+.scroller {
+ position: relative;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 300px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.grid-container {
+ display: grid;
+ grid-template-columns: 25% 25% 25% 25%;
+ grid-template-rows: 100%;
+ width: 400px;
+ height: 90px;
+}
+
+.green {
+ background-color: green;
+}
+
+.grid-item {
+ width: 100%;
+ height: 100%;
+ grid-row: 1;
+}
+
+.padding {
+ height: 1px;
+ width: 700px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 300;
+});
+</script>
+
+<p>You should see three green boxes of varying size below. There should be no red or blue.</p>
+
+<div id="scroller1" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="grid-item" style="grid-column: 2;"></div>
+ <div class="grid-item" style="grid-column: 3;"></div>
+ <div class="green grid-item" style="grid-column: 4;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid.html
new file mode 100644
index 0000000000..607e7c2936
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-grid.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>position:sticky elements should work correctly with grid layout</title>
+<link rel="match" href="position-sticky-grid-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements interoperate correctly with grid" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.scroller {
+ position: relative;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 300px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.grid-container {
+ display: grid;
+ grid-template-columns: 25% 25% 25% 25%;
+ grid-template-rows: 100%;
+ width: 400px;
+ height: 90px;
+}
+
+.sticky {
+ position: sticky;
+ left: 50px;
+}
+
+.green {
+ background-color: green;
+}
+
+.grid-item {
+ width: 100%;
+ height: 100%;
+ grid-row: 1;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ width: 100px;
+ height: 90px;
+}
+
+.padding {
+ height: 1px;
+ width: 700px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 300;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<p>You should see three green boxes of varying size below. There should be no red or blue.</p>
+
+<div id="scroller1" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink-ref.html
new file mode 100644
index 0000000000..88d45a7e6d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Reference for sticky hyperlink should work on high dpi devices</title>
+
+<style>
+body {
+ margin: 0;
+}
+
+.scroller {
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.positioned {
+ position: relative;
+ top: 100px;
+ will-change: transform;
+}
+
+.spacer {
+ height: 700px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.querySelector('.scroller').scrollTop = 100;
+});
+</script>
+
+<div>You should see a sticky link at the top of the scrollable area.</div>
+
+<div class='scroller'>
+ <a href='#' class='positioned'>Link</a>
+ <div class='spacer'></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink.html
new file mode 100644
index 0000000000..cc8de9a822
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-hyperlink.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Sticky element with hyperlink should work</title>
+<link rel="match" href="position-sticky-hyperlink-ref.html">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<meta name="assert" content="This test checks that sticky hyperlink works.">
+<meta name="fuzzy" content="maxDifference=0-5; totalPixels=0-80">
+
+<style>
+body {
+ margin: 0;
+}
+
+.scroller {
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.sticky {
+ position: sticky;
+ top: 0;
+ will-change: transform;
+}
+
+.spacer {
+ height: 700px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.querySelector('.scroller').scrollTop = 100;
+});
+</script>
+
+<div>You should see a sticky link at the top of the scrollable area.</div>
+
+<div class='scroller'>
+ <a href='#' class='sticky'>Link</a>
+ <div class='spacer'></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container-ref.html
new file mode 100644
index 0000000000..7d8e148995
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+ .modal {
+ bottom: 0;
+ left: 0;
+ right: 0;
+ position: fixed;
+ }
+
+ .modal-dialog {
+ position: relative;
+ overflow: auto;
+ max-height: 400px;
+ transform: translateY(0);
+ }
+
+ .modal-content {
+ background-color: purple;
+ height: 300px;
+ }
+
+ .modal-footer {
+ height: 100px;
+ background-color: blue;
+ }
+
+ .additional-content {
+ height: 100px;
+ background-color: purple;
+ }
+</style>
+<div class="modal">
+ <div class="modal-dialog">
+ <div class="modal-content"></div>
+ <div class="modal-footer"></div>
+ <div class="additional-content"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container.html
new file mode 100644
index 0000000000..359ec2fd33
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-in-fixed-container.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<link rel="match" href="position-sticky-in-fixed-container-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#sticky-pos">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1854010">
+<style>
+ .modal {
+ bottom: 0;
+ left: 0;
+ right: 0;
+ position: fixed;
+ }
+
+ .modal-dialog {
+ position: relative;
+ overflow: auto;
+ max-height: 400px;
+ transform: translateY(0);
+ }
+
+ .modal-content {
+ background-color: purple;
+ height: 400px;
+ }
+
+ .modal-footer {
+ height: 100px;
+ background-color: blue;
+
+ position:sticky;
+ bottom: 0;
+ }
+</style>
+<div class="modal">
+ <div class="modal-dialog">
+ <div class="modal-content"></div>
+ <div class="modal-footer"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-inflow-position.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inflow-position.html
new file mode 100644
index 0000000000..a6774b7a71
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inflow-position.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>position:sticky elements should not affect the flow position of other elements</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements do not affect the flow position of other elements" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.scroller {
+ position: relative;
+ height: 200px;
+ width: 100px;
+ overflow: scroll;
+}
+
+#sticky {
+ background-color: green;
+ position: sticky;
+ top: 150px;
+}
+
+#before {
+ background-color: fuchsia;
+}
+
+#after {
+ background-color: orange;
+}
+
+.box {
+ height: 50px;
+ width: 50px;
+}
+
+.padding {
+ height: 500px;
+}
+</style>
+
+<div class="scroller">
+ <div id="before" class="box"></div>
+ <div id="sticky" class="box"></div>
+ <div id="after" class="box"></div>
+ <div class="padding"></div>
+</div>
+
+<script>
+test(() => {
+ // The sticky element is pushed to be stuck 150 pixels from the top.
+ assert_equals(sticky.offsetTop, 150);
+
+ // Neither 'before' or 'after' should be affected by the change in the sticky
+ // element's location.
+ assert_equals(before.offsetTop, 0);
+ assert_equals(after.offsetTop, before.clientHeight + sticky.clientHeight);
+}, 'sticky offset should not affect the position of other elements.');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline-ref.html
new file mode 100644
index 0000000000..d3b4869afe
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline-ref.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky should work for inline elements</title>
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ color: green;
+}
+
+.inline {
+ display: inline;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 150px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 175px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 275px;">XXX</div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline.html
new file mode 100644
index 0000000000..6bd49befe5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-inline.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<title>position:sticky should work for inline elements</title>
+<link rel="match" href="position-sticky-inline-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky works for inline elements" />
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ color: red;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ color: green;
+}
+
+.inline {
+ display: inline;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator inline" style="top: 150px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator inline" style="top: 175px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator inline" style="top: 275px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-input-box-gets-focused-after-scroll.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-input-box-gets-focused-after-scroll.html
new file mode 100644
index 0000000000..438547dbb7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-input-box-gets-focused-after-scroll.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Focusing on visible sticky input box should scroll to unshifted sticky position.</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that focusing on visible sticky
+positioned input box should not scroll the page." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ height: 2000px;
+}
+
+input {
+ position: sticky;
+ top: 10px;
+}
+</style>
+
+<input type="text" id="input"/>
+
+<script>
+test(() => {
+ var input = document.getElementById('input');
+ window.scrollTo(0, 100);
+ input.focus();
+ assert_equals(window.scrollY, 0);
+}, 'Focusing on visible sticky input box should reset the scroll to unshifted sticky position.');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2-ref.html
new file mode 100644
index 0000000000..1890063fb9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2-ref.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>CSS Position Test Reference: Test position:sticky element with large top in an overflow scroll container</title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+ <style>
+ .scroll {
+ border: 5px solid blue;
+ padding: 5px 3px 0 8px;
+ overflow: auto;
+ height: 200px;
+ width: 200px;
+ position: relative;
+ }
+
+ .block {
+ width: 150px;
+ height: 200px;
+ background: yellow;
+ position: absolute;
+ top: 55px;
+ }
+
+ .sticky {
+ position: absolute;
+ background: purple;
+ width: 50px;
+ height: 50px;
+ top: 205px;
+ z-index: 1;
+ }
+ </style>
+ <script>
+ function runTest() {
+ document.getElementById("scroll2").scrollTop = 50;
+ }
+ </script>
+
+ <body onload="runTest();">
+ <div class="scroll">
+ <span>
+ <div class="sticky"></div>
+ </span>
+ <div class="block"></div>
+ </div>
+
+ <div class="scroll" id="scroll2">
+ <span>
+ <div class="sticky"></div>
+ </span>
+ <div class="block"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2.tentative.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2.tentative.html
new file mode 100644
index 0000000000..1cf9c0dfb4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-2.tentative.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>CSS Position Test: Test position:sticky element with large top in an overflow container</title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+ <link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1598112">
+ <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2794">
+ <link rel="match" href="position-sticky-large-top-2-ref.html">
+ <meta name="assert" content="This test verifies the position of a position:sticky element with large top value can be reached by scrolling the overflow container.">
+
+ <style>
+ .scroll {
+ border: 5px solid blue;
+ padding: 5px 3px 0 8px;
+ overflow: auto;
+ height: 200px;
+ width: 200px;
+ }
+
+ .block {
+ width: 150px;
+ height: 200px;
+ background: yellow;
+ }
+
+ .sticky {
+ position: sticky;
+ background: purple;
+ width: 50px;
+ height: 50px;
+ top: 200px;
+ }
+ </style>
+ <script>
+ function runTest() {
+ document.getElementById("scroll2").scrollTop = 50;
+ }
+ </script>
+
+ <body onload="runTest();">
+ <!-- test before scroll -->
+ <div class="scroll">
+ <span>
+ <div class="sticky"></div>
+ </span>
+ <div class="block"></div>
+ </div>
+
+ <!-- test after scroll -->
+ <div class="scroll" id="scroll2">
+ <span>
+ <div class="sticky"></div>
+ </span>
+ <div class="block"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-ref.html
new file mode 100644
index 0000000000..b50bae2260
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top-ref.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>CSS Position Test Reference: Test position:sticky element with large top in an overflow scroll container</title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+ <style>
+ .scroll {
+ border: 5px solid blue;
+ overflow: auto;
+ height: 200px;
+ width: 200px;
+ position: relative;
+ }
+
+ .block {
+ width: 100%;
+ height: 200px;
+ background: yellow;
+ position: absolute;
+ top: 50px;
+ }
+
+ .sticky {
+ position: absolute;
+ background: purple;
+ width: 50px;
+ height: 50px;
+ top: 200px;
+ z-index: 1;
+ }
+ </style>
+ <script>
+ function runTest() {
+ document.getElementById("scroll2").scrollTop = 50;
+ }
+ </script>
+
+ <body onload="runTest();">
+ <div class="scroll">
+ <div class="sticky"></div>
+ <div class="block"></div>
+ </div>
+
+ <div class="scroll" id="scroll2">
+ <div class="sticky"></div>
+ <div class="block"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top.tentative.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top.tentative.html
new file mode 100644
index 0000000000..b00a0d1396
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-large-top.tentative.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>CSS Position Test: Test position:sticky element with large top in an overflow container</title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+ <link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1598112">
+ <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2794">
+ <link rel="match" href="position-sticky-large-top-ref.html">
+ <meta name="assert" content="This test verifies the position of a position:sticky element with large top value can be reached by scrolling the overflow container.">
+
+ <style>
+ .scroll {
+ border: 5px solid blue;
+ overflow: auto;
+ height: 200px;
+ width: 200px;
+ }
+
+ .block {
+ width: 100%;
+ height: 200px;
+ background: yellow;
+ }
+
+ .sticky {
+ position: sticky;
+ background: purple;
+ width: 50px;
+ height: 50px;
+ top: 200px;
+ }
+ </style>
+ <script>
+ function runTest() {
+ document.getElementById("scroll2").scrollTop = 50;
+ }
+ </script>
+
+ <body onload="runTest();">
+ <!-- test before scroll -->
+ <div class="scroll">
+ <div class="sticky"></div>
+ <div class="block"></div>
+ </div>
+
+ <!-- test after scroll -->
+ <div class="scroll" id="scroll2">
+ <div class="sticky"></div>
+ <div class="block"></div>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-002.html
new file mode 100644
index 0000000000..868b58adb0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-002.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with left offset specified with px unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-left-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: right top;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: 100px top;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: 50px top;
+ }
+
+ div.horizontal-spacer
+ {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+ }
+
+ div.content
+ {
+ display: inline-block;
+ height: 100%;
+ width: 300px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ left: 100px;
+ position: sticky;
+ vertical-align: top;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 150; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-003.html
new file mode 100644
index 0000000000..3114981a69
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left-003.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with left offset specified with percentage unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-left-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: right top;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: 100px top;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: 50px top;
+ }
+
+ div.horizontal-spacer
+ {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+ }
+
+ div.content
+ {
+ display: inline-block;
+ height: 100%;
+ width: 300px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ left: 40%;
+ position: sticky;
+ vertical-align: top;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 150; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-left.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left.html
new file mode 100644
index 0000000000..1fc2e8f88d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-left.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the left constraint</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their left anchor after scrolling" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('left', 50);
+ elements.scroller.scrollLeft = 100;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+}, 'before reaching the sticking point the sticky box should not be offset');
+
+test(() => {
+ const elements = setupStickyTest('left', 50);
+ elements.scroller.scrollLeft = 200;
+
+ // This math actually cancels to sticky.offsetLeft == (scroller.scrollLeft + 50),
+ // but for clarity the calculations are left explicit.
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ const targetLeftX = elements.scroller.scrollLeft + 50;
+ const stickyOffset = targetLeftX - nonStickyLeftX;
+
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX + stickyOffset);
+}, 'after reaching the sticking point the sticky box should be offset');
+
+test(() => {
+ const elements = setupStickyTest('left', 50);
+ elements.scroller.scrollLeft = 300;
+ const maxOffsetInContainer = elements.container.offsetLeft +
+ elements.container.clientWidth - elements.sticky.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, maxOffsetInContainer);
+}, 'the sticky box should not be pushed outside its containing block');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-margins.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-margins.html
new file mode 100644
index 0000000000..7a9cf09faa
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-margins.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>position:sticky elements should properly interact with margins</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="position:sticky elements should ignore margins when sticking, but consider them when making sure sticky elements do not escape their containing block" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.margin = '15px';
+ elements.scroller.scrollTop = 100;
+ assert_equals(elements.sticky.offsetTop,
+ elements.container.offsetTop + elements.filler.clientHeight + 15);
+}, 'Before sticking, the margin should be obeyed.');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.margin = '15px';
+
+ elements.scroller.scrollTop = 200;
+
+ // This math cancels to sticky.offsetTop == (scroller.scrollTop + 50), but
+ // for clarity the calculations are left explicit.
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const targetTopY = elements.scroller.scrollTop + 50;
+ const stickyOffset = targetTopY - nonStickyTopY;
+
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY + stickyOffset);
+}, 'Whilst stuck, the margin is irrelevant.');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.margin = '15px';
+
+ elements.scroller.scrollTop = 300;
+
+ const maxOffsetInContainer = elements.container.offsetTop +
+ elements.container.clientHeight - elements.sticky.clientHeight;
+ assert_equals(elements.sticky.offsetTop, maxOffsetInContainer - 15);
+}, 'The margin is taken into account when making sure the sticky element ' +
+ 'does not escape its container');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-bottom.html
new file mode 100644
index 0000000000..34fb3b32fc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-bottom.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Nested bottom-constrained position:sticky elements should render correctly</title>
+
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a bottom constraint render correctly" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupNestedStickyTest('bottom', 25, 35);
+ elements.scroller.scrollTop = 300;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+ // The inner sticky should not be offset from the outer.
+ const nonStickyInnerTopY = elements.sticky.clientHeight -
+ elements.innerSticky.clientHeight;
+ assert_equals(elements.innerSticky.offsetTop, nonStickyInnerTopY);
+}, 'before reaching the sticking point, neither sticky box should be offset');
+
+test(() => {
+ const elements = setupNestedStickyTest('bottom', 25, 50);
+ elements.scroller.scrollTop = 150;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+ assert_equals(elements.innerSticky.offsetTop, 50);
+}, 'the inner sticky can stick before the outer one if necessary');
+
+test(() => {
+ const elements = setupNestedStickyTest('bottom', 25, 35);
+ elements.scroller.scrollTop = 100;
+
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const nonStickyBottomY = nonStickyTopY + elements.sticky.clientHeight;
+ const targetBottomY = elements.scroller.clientHeight +
+ elements.scroller.scrollTop - 25;
+ const stickyOffset = nonStickyBottomY - targetBottomY;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY - stickyOffset);
+
+ // The inner sticky has similar math, but its offsetTop is relative to the
+ // sticky element and in this test is (height - the difference between the
+ // top values).
+ assert_equals(elements.innerSticky.offsetTop, 40);
+}, 'both sticky boxes can be stuck at the same time');
+
+test(() => {
+ const elements = setupNestedStickyTest('bottom', 25, 75);
+ elements.scroller.scrollTop = 0;
+ assert_equals(elements.sticky.offsetTop, elements.container.offsetTop);
+ assert_equals(elements.innerSticky.offsetTop, 0);
+}, 'neither sticky can escape their containing block');
+
+test(() => {
+ const elements = setupNestedStickyTest('bottom', 25, 300);
+ elements.scroller.scrollTop = 200;
+ // It doesn't matter how big the inner sticky offset is, it cannot escape its
+ // containing block (the outer sticky).
+ assert_equals(elements.innerSticky.offsetTop, 0);
+}, 'the inner sticky cannot be pushed outside the outer sticky');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline-ref.html
new file mode 100644
index 0000000000..4ad6dee5d0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline-ref.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>Reference for nested inline position:sticky elements should render correctly</title>
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.outerIndicator {
+ color: green;
+ position: absolute;
+ left: 0;
+}
+
+.innerIndicator {
+ color: yellow;
+ position: absolute;
+ left: 25px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+});
+</script>
+
+<div>You should see three green and three yellow rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 150px;">X</div>
+ <div class="innerIndicator" style="top: 150px;">XX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 175px;">X</div>
+ <div class="innerIndicator" style="top: 185px;">XX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 275px;">X</div>
+ <div class="innerIndicator" style="top: 275px;">XX</div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline.html
new file mode 100644
index 0000000000..92eda147bd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-inline.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<title>Nested inline position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-inline-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested inline position:sticky elements render correctly" />
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.outerIndicator {
+ color: red;
+ position: absolute;
+ left: 0;
+}
+
+.innerIndicator {
+ color: red;
+ position: absolute;
+ left: 25px;
+}
+
+.outerSticky {
+ display: inline;
+ color: green;
+ position: sticky;
+ top: 50px;
+}
+
+.innerSticky {
+ display: inline;
+ color: yellow;
+ position: sticky;
+ top: 60px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green and three yellow rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="top: 150px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 0;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="top: 175px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 10px;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="top: 200px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 0;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-left.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-left.html
new file mode 100644
index 0000000000..b92d45319b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-left.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<title>Nested left-constrained position:sticky elements should render correctly</title>
+
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a left constraint render correctly" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupNestedStickyTest('left', 50, 60);
+ elements.scroller.scrollLeft = 100;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+ // The inner sticky should not be offset from the outer.
+ assert_equals(elements.innerSticky.offsetLeft, 0);
+}, 'before reaching the sticking point, neither sticky box should be offset');
+
+test(() => {
+ const elements = setupNestedStickyTest('left', 50, 60);
+ elements.scroller.scrollLeft = 145;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+ assert_equals(elements.innerSticky.offsetLeft, 5);
+}, 'the inner sticky can stick before the outer one if necessary');
+
+test(() => {
+ const elements = setupNestedStickyTest('left', 50, 60);
+ elements.scroller.scrollLeft = 200;
+
+ // This math cancels to sticky.offsetLeft == (scroller.scrollLeft + 50), but
+ // for clarity the calculations are left explicit.
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ const targetLeftX = elements.scroller.scrollLeft + 50;
+ const stickyOffset = targetLeftX - nonStickyLeftX;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX + stickyOffset);
+
+ // The inner sticky has similar math, but its offsetLeft is relative to the
+ // sticky element and in this test is the difference between the left values.
+ assert_equals(elements.innerSticky.offsetLeft, 10);
+}, 'both sticky boxes can be stuck at the same time');
+
+test(() => {
+ const elements = setupNestedStickyTest('left', 50, 60);
+ elements.scroller.scrollLeft = 300;
+ const maxOffsetInContainer = elements.container.offsetLeft +
+ elements.container.clientWidth - elements.sticky.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, maxOffsetInContainer);
+ const maxOffsetInOuterSticky = elements.sticky.clientWidth -
+ elements.innerSticky.clientWidth;
+ assert_equals(elements.innerSticky.offsetLeft, maxOffsetInOuterSticky);
+}, 'neither sticky can escape their containing block');
+
+test(() => {
+ const elements = setupNestedStickyTest('left', 50, 300);
+ elements.scroller.scrollLeft = 100;
+ // The outer sticky has not stuck yet.
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+ // But the inner sticky still cannot escape the outer sticky (as it is the
+ // containing block).
+ const maxOffsetInOuterSticky = elements.sticky.clientWidth -
+ elements.innerSticky.clientWidth;
+ assert_equals(elements.innerSticky.offsetLeft, maxOffsetInOuterSticky);
+}, 'the inner sticky cannot be pushed outside the outer sticky');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-right.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-right.html
new file mode 100644
index 0000000000..0f7e43f35a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-right.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>Nested right-constrained position:sticky elements should render correctly</title>
+
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a right constraint render correctly" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupNestedStickyTest('right', 25, 35);
+ elements.scroller.scrollLeft = 200;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+ // The inner sticky should not be offset from the outer.
+ const nonStickyInnerLeftX = elements.sticky.clientWidth -
+ elements.innerSticky.clientWidth;
+ assert_equals(elements.innerSticky.offsetLeft, nonStickyInnerLeftX);
+}, 'before reaching the sticking point, neither sticky box should be offset');
+
+test(() => {
+ const elements = setupNestedStickyTest('right', 25, 50);
+ elements.scroller.scrollLeft = 150;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+ assert_equals(elements.innerSticky.offsetLeft, 50);
+}, 'the inner sticky can stick before the outer one if necessary');
+
+test(() => {
+ const elements = setupNestedStickyTest('right', 25, 35);
+ elements.scroller.scrollLeft = 100;
+
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ const nonStickyBottomX = nonStickyLeftX + elements.sticky.clientWidth;
+ const targetBottomX = elements.scroller.clientWidth +
+ elements.scroller.scrollLeft - 25;
+ const stickyOffset = nonStickyBottomX - targetBottomX;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX - stickyOffset);
+
+ // The inner sticky has similar math, but its offsetLeft is relative to the
+ // sticky element and in this test is (height - the difference between the
+ // top values).
+ assert_equals(elements.innerSticky.offsetLeft, 40);
+}, 'both sticky boxes can be stuck at the same time');
+
+test(() => {
+ const elements = setupNestedStickyTest('right', 25, 100);
+ elements.scroller.scrollLeft = 0;
+ assert_equals(elements.sticky.offsetLeft, elements.container.offsetLeft);
+ assert_equals(elements.innerSticky.offsetLeft, 0);
+}, 'neither sticky can escape their containing block');
+
+test(() => {
+ const elements = setupNestedStickyTest('right', 25, 500);
+ elements.scroller.scrollLeft = 200;
+ // It doesn't matter how big the inner sticky offset is, it cannot escape its
+ // containing block (the outer sticky).
+ assert_equals(elements.innerSticky.offsetLeft, 0);
+}, 'the inner sticky cannot be pushed outside the outer sticky');
+
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table-ref.html
new file mode 100644
index 0000000000..36fabc3845
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table-ref.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Reference for nested position:sticky table elements should render correctly</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: green;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 100px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 250px;"></div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table.html
new file mode 100644
index 0000000000..b2a8bf1e87
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-table.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<title>Nested position:sticky table elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-table-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky table elements render correctly" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+td, th {
+ height: 50px;
+ width: 50px;
+ padding: 0;
+}
+
+th {
+ background: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: red;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-thead-th.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-thead-th.html
new file mode 100644
index 0000000000..51f58b7591
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-thead-th.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<title>Nested position:sticky table elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-table-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky table elements render correctly" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+td, th {
+ height: 50px;
+ width: 50px;
+ padding: 0;
+}
+
+th {
+ background: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: red;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green rectangles below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr>
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr>
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr>
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-top.html
new file mode 100644
index 0000000000..90dc52dba8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-nested-top.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>Nested top-constrainted position:sticky elements should render correctly</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a top constraint render correctly" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupNestedStickyTest('top', 50, 60);
+ elements.scroller.scrollTop = 100;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+ // The inner sticky should not be offset from the outer.
+ assert_equals(elements.innerSticky.offsetTop, 0);
+}, 'before reaching the sticking point, neither sticky box should be offset');
+
+test(() => {
+ const elements = setupNestedStickyTest('top', 50, 60);
+ elements.scroller.scrollTop = 145;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+ assert_equals(elements.innerSticky.offsetTop, 5);
+}, 'the inner sticky can stick before the outer one if necessary');
+
+test(() => {
+ const elements = setupNestedStickyTest('top', 50, 60);
+ elements.scroller.scrollTop = 200;
+
+ // This math cancels to sticky.offsetTop == (scroller.scrollTop + 50), but
+ // for clarity the calculations are left explicit.
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const targetTopY = elements.scroller.scrollTop + 50;
+ const stickyOffset = targetTopY - nonStickyTopY;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY + stickyOffset);
+
+ // The inner sticky has similar math, but its offsetTop is relative to the
+ // sticky element and in this test is the difference between the top values.
+ assert_equals(elements.innerSticky.offsetTop, 10);
+}, 'both sticky boxes can be stuck at the same time');
+
+test(() => {
+ const elements = setupNestedStickyTest('top', 50, 60);
+ elements.scroller.scrollTop = 300;
+ const maxOffsetInContainer = elements.container.offsetTop +
+ elements.container.clientHeight - elements.sticky.clientHeight;
+ assert_equals(elements.sticky.offsetTop, maxOffsetInContainer);
+ const maxOffsetInOuterSticky = elements.sticky.clientHeight -
+ elements.innerSticky.clientHeight;
+ assert_equals(elements.innerSticky.offsetTop, maxOffsetInOuterSticky);
+}, 'neither sticky can escape their containing block');
+
+test(() => {
+ const elements = setupNestedStickyTest('top', 50, 300);
+ elements.scroller.scrollTop = 100;
+ // The outer sticky has not stuck yet.
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+ // But the inner sticky still cannot escape the outer sticky (as it is the
+ // containing block).
+ const maxOffsetInOuterSticky = elements.sticky.clientHeight -
+ elements.innerSticky.clientHeight;
+ assert_equals(elements.innerSticky.offsetTop, maxOffsetInOuterSticky);
+}, 'the inner sticky cannot be pushed outside the outer sticky');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-overflow.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-overflow.html
new file mode 100644
index 0000000000..4882e110eb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-overflow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Sticky positioning can cause overflow but must be accessible.</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that a sticky positioned element
+does not extend overflow" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.container {
+ overflow-y: scroll;
+ width: 100px;
+ height: 100px;
+}
+
+.box {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 200px; /* Forces the sticky position element below the overflow. */
+}
+</style>
+
+<div id="scroller1" class="container">
+ <div class="sticky box"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller1');
+ var sticky = scroller.querySelector('.sticky');
+
+ var stickyOffset = sticky.offsetTop -
+ scroller.scrollTop - scroller.offsetTop;
+ assert_equals(stickyOffset, 50);
+ assert_equals(scroller.scrollHeight, 100);
+}, 'sticky position offset should be contained by scrolling box');
+
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-print.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-print.html
new file mode 100644
index 0000000000..3954bbcf44
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-print.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1485969">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="scroller" style="position:relative; width:200px; height:100px; overflow:hidden;">
+ <div style="position:absolute; width:1000px;">
+ <div style="position:sticky; left:0; height:100px; width:100px; background:green;"></div>
+ </div>
+ <div style="width:100px; height:100px; background:red;"></div>
+</div>
+<script>
+document.getElementById('scroller').scrollLeft = 100;
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-top-left.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-top-left.html
new file mode 100644
index 0000000000..ade9e108cf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-offset-top-left.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<title>Sticky positioned element should be observable by offsetTop and offsetLeft</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that a sticky positioned element
+should be observable by offsetTop/offsetLeft." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ position: relative; /* Required for offsetTop/offsetLeft tests. */
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.spacer {
+ width: 2000px;
+ height: 2000px;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ left: 20px;
+}
+</style>
+
+<div id="scroller1" class="container">
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller1');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var sticky = document.createElement('div');
+ sticky.className = 'sticky box';
+ scroller.insertBefore(sticky, scroller.querySelector('.spacer'));
+
+ assert_equals(sticky.offsetTop, scroller.scrollTop + 50);
+ assert_equals(sticky.offsetLeft, scroller.scrollLeft + 20);
+}, 'offsetTop/offsetLeft should be correct for sticky after script insertion');
+</script>
+
+<div id="scroller2" class="container">
+ <div id="sticky2" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(function() {
+ var scroller = document.getElementById('scroller2');
+ var sticky = document.getElementById('sticky2');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var div = document.createElement('div');
+ div.style.height = '65px';
+ scroller.insertBefore(div, sticky);
+
+ assert_equals(sticky.offsetTop, scroller.scrollTop + 50);
+ assert_equals(sticky.offsetLeft, scroller.scrollLeft + 20);
+}, 'offsetTop/offsetLeft should be correct for sticky after script-caused layout');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container-ref.html
new file mode 100644
index 0000000000..2a29b435a7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container-ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<title>Sticky elements should not consider overflow: clip containers as possible scroll ancestor</title>
+<style>
+body {
+ margin: 0;
+ overflow: hidden; /* hide scrollbars */
+}
+
+#container {
+ height: 300px;
+ overflow-y: scroll;
+}
+
+#overflowClipContainer {
+ overflow: visible;
+ height: 600px;
+}
+
+#sticky {
+ position: sticky;
+ top: 0;
+ height: 50px;
+ background-color: yellow;
+}
+</style>
+<script>
+function doTest()
+{
+ container.scrollTo(0, 50);
+}
+window.addEventListener('load', doTest, false);
+</script>
+<div id="container">
+ <div id="overflowClipContainer">
+ <div id="sticky"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container.html
new file mode 100644
index 0000000000..63356349af
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-clip-container.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<title>Sticky elements should not consider overflow: clip containers as possible scroll ancestor</title>
+<link rel="match" href="position-sticky-overflow-clip-container-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that sticky elements do not consider overflow:clip containers as possible scroll ancestor"/>
+<style>
+body {
+ margin: 0;
+ overflow: hidden; /* hide scrollbars */
+}
+
+#container {
+ height: 300px;
+ overflow: auto;
+}
+
+#overflowClipContainer {
+ overflow: clip;
+ height: 600px;
+}
+
+#sticky {
+ position: sticky;
+ top: 0;
+ height: 50px;
+ background-color: yellow;
+}
+</style>
+<script>
+function doTest()
+{
+ container.scrollTo(0, 50);
+}
+window.addEventListener('load', doTest, false);
+</script>
+<div id="container">
+ <div id="overflowClipContainer">
+ <div id="sticky"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-hidden.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-hidden.html
new file mode 100644
index 0000000000..622ca8fbef
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-hidden.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect an overflow:hidden ancestor</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements adhere to an overflow:hidden ancestor" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const outer_scroller = document.createElement('div');
+ outer_scroller.style.width = '100px';
+ outer_scroller.style.height = '100px';
+ outer_scroller.style.overflow = 'scroll';
+
+ const inner_scroller = document.createElement('div');
+ inner_scroller.style.width = '80%';
+ inner_scroller.style.height = '200px';
+ inner_scroller.style.overflow = 'hidden';
+
+ const sticky = document.createElement('div');
+ sticky.style.width = '20px';
+ sticky.style.height = '20px';
+ sticky.style.position = 'sticky';
+ sticky.style.top = '0';
+ sticky.style.background = 'red';
+
+ const spacer = document.createElement('div');
+ spacer.style.height = '500px';
+
+ inner_scroller.appendChild(sticky);
+ inner_scroller.appendChild(spacer);
+ outer_scroller.appendChild(inner_scroller);
+ document.body.appendChild(outer_scroller);
+
+ outer_scroller.scrollTop = 50;
+
+ // The sticky should attach to the inner scroller, and so should not stick.
+ assert_equals(sticky.offsetTop, inner_scroller.offsetTop);
+}, 'A sticky element should attach to an overflow:hidden ancestor');
+
+// This tests a specific bug in Firefox where the sticky element incorrectly
+// started sticking when inside a table. See https://bugzilla.mozilla.org/show_bug.cgi?id=1488810
+test(() => {
+ const outer_scroller = document.createElement('div');
+ outer_scroller.style.width = '100px';
+ outer_scroller.style.height = '100px';
+ outer_scroller.style.overflow = 'scroll';
+
+ const table = document.createElement('div');
+ table.style.display = 'table';
+
+ const tr = document.createElement('div');
+ tr.style.display = 'table-row';
+
+ const inner_scroller = document.createElement('div');
+ inner_scroller.style.display = 'table-cell';
+ inner_scroller.style.overflow = 'hidden';
+
+ const sticky = document.createElement('div');
+ sticky.style.width = '20px';
+ sticky.style.height = '20px';
+ sticky.style.position = 'sticky';
+ sticky.style.top = '0';
+ sticky.style.background = 'red';
+
+ const spacer = document.createElement('div');
+ spacer.style.height = '500px';
+
+ inner_scroller.appendChild(sticky);
+ inner_scroller.appendChild(spacer);
+ tr.append(inner_scroller);
+ table.appendChild(tr);
+ outer_scroller.appendChild(table);
+ document.body.appendChild(outer_scroller);
+
+ outer_scroller.scrollTop = 50;
+
+ // The sticky should attach to the inner scroller, and so should not stick.
+ assert_equals(sticky.offsetTop, inner_scroller.offsetTop);
+}, 'A sticky element should attach to an overflow:hidden ancestor inside a table');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-padding.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-padding.html
new file mode 100644
index 0000000000..71b74e7886
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-overflow-padding.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect padding on their ancestor overflow element</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements respect padding on their ancestor overflow element" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.scroller.style.padding = '20px 0';
+
+ // Before sticking; the element isn't within the padding range.
+ elements.scroller.scrollTop = 150;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+}, 'A sticky element should not be affected by ancestor padding until it ' +
+ 'reaches it');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.top = '0';
+ elements.scroller.style.padding = '20px 0';
+
+ elements.scroller.scrollTop = 200;
+
+ // This math cancels to sticky.offsetTop == (scroller.scrollTop + 50), but
+ // for clarity the calculations are left explicit.
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const targetTopY = elements.scroller.scrollTop;
+ const stickyOffset = targetTopY - nonStickyTopY;
+
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY + stickyOffset + 20);
+}, 'A sticky element should be offset by ancestor padding even when stuck');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.top = '0';
+ elements.scroller.style.padding = '20px 0';
+
+ elements.scroller.scrollTop = 315;
+ const maxOffsetInContainer = elements.container.offsetTop +
+ elements.container.clientHeight - elements.sticky.clientHeight;
+ assert_equals(elements.sticky.offsetTop, maxOffsetInContainer);
+}, 'Ancestor overflow padding does not allow a sticky element to escape its ' +
+ 'container');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-parsing.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-parsing.html
new file mode 100644
index 0000000000..b147429e69
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-parsing.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>Position value 'sticky' should be a valid value</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#position-property" />
+<meta name="assert" content="This test checks that setting position to 'sticky'
+should be allowed." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- We need something to create elements in. -->
+<body></body>
+
+<script>
+const displayTypes = [
+ 'block',
+ 'inline',
+ 'run-in',
+ 'flow',
+ 'flow-root',
+ 'table',
+ 'flex',
+ 'grid',
+ 'ruby',
+ 'subgrid',
+ 'list-item',
+ 'table-row-group',
+ 'table-header-group',
+ 'table-footer-group',
+ 'table-row',
+ 'table-cell',
+ 'table-caption',
+ // Sticky does not apply to table-column or table-column-group, but the
+ // computed value should still be sticky.
+ 'table-column',
+ 'table-column-group',
+ 'ruby-base',
+ 'ruby-text',
+ 'ruby-base-container',
+ 'ruby-text-container',
+ 'contents',
+ 'none',
+];
+
+test(() => {
+ for (displayValue of displayTypes) {
+ let div = document.createElement('div');
+ let style = `position: sticky; display: ${displayValue};`;
+ div.setAttribute('style', style);
+ document.body.appendChild(div);
+
+ // We only check display values that the browser under test recognizes.
+ if (div.style.display == displayValue) {
+ assert_equals(getComputedStyle(div).position, 'sticky',
+ `Expected sticky to be valid for display: ${displayValue}`);
+ }
+ document.body.removeChild(div);
+ }
+}, 'The value of sticky for the position property should be parsed correctly');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering-ref.html
new file mode 100644
index 0000000000..b4411c3bed
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering-ref.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should be rendered at their sticky offset</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.inlineGroup {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.inlineGroup .scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 500px;
+}
+
+.inlineGroup .contents {
+ height: 100%;
+ width: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+
+.inlineGroup .box {
+ height: 100%;
+ width: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 125;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller3').scrollLeft = 125;
+ document.getElementById('scroller4').scrollLeft = 75;
+});
+</script>
+
+<div>You should see four green squares below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 175px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 125px;"></div>
+ </div>
+ </div>
+</div>
+
+<!-- Force break to make sure we are within 800px wide. -->
+<div></div>
+
+<div class="inlineGroup">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 175px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="inlineGroup">
+ <div id="scroller4" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 150px;"></div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering.html
new file mode 100644
index 0000000000..a4e03c59d8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-rendering.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<title>position:sticky elements should be rendered at their sticky offset</title>
+<link rel="match" href="position-sticky-rendering-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements are rendered correctly" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.inlineGroup {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.inlineGroup .scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 500px;
+}
+
+.inlineGroup .contents {
+ height: 100%;
+ width: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.inlineGroup .prepadding {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.inlineGroup .container {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.filler {
+ height: 100px;
+}
+
+.inlineGroup .filler {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+
+.inlineGroup .box {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 125;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller3').scrollLeft = 125;
+ document.getElementById('scroller4').scrollLeft = 75;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see four green squares below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 175px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div style="top: 50px;" class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 125px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div style="bottom: 25px;" class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<!-- Force break to make sure we are within 800px wide. -->
+<div></div>
+
+<div class="inlineGroup">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="left: 175px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div style="left: 50px;" class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div class="inlineGroup">
+ <div id="scroller4" class="scroller">
+ <div class="indicator box" style="left: 150px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="filler"></div><div style="right: 25px;" class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-002.html
new file mode 100644
index 0000000000..e83189a498
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-002.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with right offset specified with px unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-right-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: 75px top;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: 50px top;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left top;
+ }
+
+ div.horizontal-spacer
+ {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+ }
+
+ div.content
+ {
+ display: inline-block;
+ height: 100%;
+ width: 300px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ position: sticky;
+ right: 100px;
+ vertical-align: top;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-003.html
new file mode 100644
index 0000000000..ea712a99da
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right-003.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with right offset specified with percentage unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-right-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: 75px top;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: 50px top;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left top;
+ }
+
+ div.horizontal-spacer
+ {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+ }
+
+ div.content
+ {
+ display: inline-block;
+ height: 100%;
+ width: 300px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ position: sticky;
+ right: 40%;
+ vertical-align: top;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer"></div><div class="content"><div class="horizontal-spacer"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer"></div></div><div class="horizontal-spacer"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-right.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right.html
new file mode 100644
index 0000000000..82d0e9af1d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-right.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the right constraint</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their right anchor after scrolling" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('right', 25);
+ elements.scroller.scrollLeft = 200;
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX);
+}, 'before reaching the sticking point the sticky box should not be offset');
+
+test(() => {
+ const elements = setupStickyTest('right', 25);
+ elements.scroller.scrollLeft = 75;
+
+ const nonStickyLeftX = elements.container.offsetLeft +
+ elements.filler.clientWidth;
+ const nonStickyRightX = nonStickyLeftX + elements.sticky.clientWidth;
+ const targetRightX = elements.scroller.clientWidth +
+ elements.scroller.scrollLeft - 25;
+ const stickyOffset = nonStickyRightX - targetRightX;
+
+ assert_equals(elements.sticky.offsetLeft, nonStickyLeftX - stickyOffset);
+}, 'after reaching the sticking point the sticky box should be offset');
+
+test(() => {
+ const elements = setupStickyTest('right', 25);
+ elements.scroller.scrollLeft = 15;
+ assert_equals(elements.sticky.offsetLeft, elements.container.offsetLeft);
+}, 'the sticky box should not be pushed outside its containing block');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-root-scroller.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-root-scroller.html
new file mode 100644
index 0000000000..596fd9b240
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-root-scroller.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>position:sticky should operate correctly for the root scroller</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements work when using the root (document) scroller" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ /* Assumption: 3000px is taller than any user agents test window size. */
+ height: 3000px;
+}
+
+#sticky {
+ position: sticky;
+ top: 50px;
+ width: 200px;
+ height: 200px;
+ background-color: green;
+}
+</style>
+
+<div id="sticky"></div>
+
+<script>
+test(() => {
+ window.scrollTo(0, 700);
+ assert_equals(sticky.offsetTop, 700 + 50);
+}, 'Sticky elements work with the root (document) scroller');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-offset-clamp-crash.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-offset-clamp-crash.html
new file mode 100644
index 0000000000..ec6fb90cca
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-offset-clamp-crash.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1498297">
+<style>
+* {
+ margin-top: 100px;
+ padding-left: 100px;
+ position: sticky;
+ bottom: 0px;
+}
+</style>
+<select id="select1" multiple="multiple" style="writing-mode: vertical-rl">
+ <optgroup id="optgroup">text</optgroup>
+</select>
+<select id="select2" >text</select>
+<script>
+document.body.offsetTop;
+select2.add(optgroup);
+document.body.offsetTop;
+select1.length = 1;
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition-ref.html
new file mode 100644
index 0000000000..487a2322fc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Test that style mutation of contain:strict plus position:sticky updates sticky position</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<div id="scroller" style="width: 200px; height: 200px; overflow-y: scroll;
+ will-change: transform;">
+ <div id="scrollbar" style="width: 100px; height: 100px; position: sticky; background: lightblue;
+ top: 5px; left: 50px; contain: strict;"></div>
+ <div style="width: 100px; height: 500px;"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition.html
new file mode 100644
index 0000000000..b75275c64f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-reposition.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Test that style mutation of contain:strict plus position:sticky updates sticky position</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+<link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+<link rel="match" href="position-sticky-scroll-reposition-ref.html">
+<div id="scroller" style="width: 200px; height: 200px; overflow-y: scroll;
+ will-change: transform;">
+ <div id="sticky" style="width: 100px; height: 100px; position: sticky; background: lightblue;
+ top: 50px; left: 50px; contain: strict;"></div>
+ <div style="width: 100px; height: 500px;"></div>
+</div>
+<script src="/common/reftest-wait.js"></script>
+<script>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ sticky.style.top = '5px';
+ takeScreenshot();
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html
new file mode 100644
index 0000000000..d9fe863d80
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>position:sticky should operate correctly</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+
+
+
+<div style="position: fixed;">There should be text visible below.</div>
+<div style="height: 200px;width:600px;position: fixed;top: 0px;">
+ <div style="position: relative;top: 100px;">
+ <div style="height: 150px;width: 500px;position: absolute;backface-visibility: hidden;background: white;">
+ </div>
+ <div style="overflow: hidden;">
+ <a style="position: relative;">THIS SHOULD STAY VISIBLE<BR>IF YOU SCROLL DOWN</a>
+ </div>
+ </div>
+</div>
+<div style="height: 2200px;">
+</div>
+<script>
+ window.onload = function() {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ window.scrollTo(0,300);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ };
+</script>
+</html>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html
new file mode 100644
index 0000000000..fb7edfe498
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>position:sticky should operate correctly</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the combination of position:sticky, overflow clip, and out-of-flow descendants are properly displayed when scrolled" />
+<link rel="match" href="position-sticky-scroll-with-clip-and-abspos-ref.html">
+
+<div style="position: fixed;">There should be text visible below.</div>
+<div style="height: 200px;width:600px;position: sticky;top: 0px;">
+ <div style="position: relative;top: 100px;">
+ <div style="height: 150px;width: 500px;position: absolute;backface-visibility: hidden;background: white;">
+ </div>
+ <div style="overflow: hidden;">
+ <a style="position: relative;">THIS SHOULD STAY VISIBLE<BR>IF YOU SCROLL DOWN</a>
+ </div>
+ </div>
+</div>
+<div style="height: 2000px;">
+</div>
+<script>
+ window.onload = function() {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ window.scrollTo(0,300);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ });
+ };
+</script>
+</html>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrollIntoView.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrollIntoView.html
new file mode 100644
index 0000000000..6fb4723529
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrollIntoView.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Scrolling to sticky position elements uses their unshifted position</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#stickypos-scroll" />
+<meta name="assert" content="This test checks that scrolling to sticky position elements uses their initial position" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+
+h1 {
+ position: sticky;
+ background: #ddd;
+ border: 1px solid black;
+ top: 0px;
+ bottom: 0px;
+}
+
+section {
+ height: 100vh;
+}
+</style>
+
+<body>
+ <h1>Title 1</h1>
+ <section></section>
+ <h1>Title 2</h1>
+ <section></section>
+ <h1>Title 3</h1>
+ <section></section>
+
+<script>
+test(() => {
+ window.scrollTo(0, 0);
+ const element = document.querySelectorAll('h1')[2];
+ element.scrollIntoView();
+ assert_approx_equals(document.scrollingElement.scrollTop, element.offsetTop, 1);
+}, 'scrolling a sticky element into view should use its unshifted position');
+</script>
+</body>
+
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrolled-remove-sibling.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrolled-remove-sibling.html
new file mode 100644
index 0000000000..845454e1b3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-scrolled-remove-sibling.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Positioned Layout Test: element with 'position: sticky' and removing a sibling in the vertical axis and in the horizontal axis</title>
+
+ <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+
+ <meta name="flags" content="">
+
+ <style>
+ div#scrollingContainerVert
+ {
+ background-color: red;
+ height: 100px;
+ overflow: auto;
+ width: 200px;
+ }
+
+ div#scrollingContainerHoriz
+ {
+ background-color: red;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ width: 200px;
+ }
+
+ div#elemStickyVert
+ {
+ background-color: green;
+ height: 100px;
+ position: sticky;
+ top: 0px;
+ }
+
+ div#scrollingContainerHoriz > div
+ {
+ display: inline-block;
+ height: 100%;
+ }
+
+ div#elemStickyHoriz
+ {
+ background-color: green;
+ left: 0px;
+ position: sticky;
+ width: 200px;
+ }
+
+ div#tallItem
+ {
+ height: 600px;
+ }
+
+ div#wideItem
+ {
+ width: 600px;
+ }
+ </style>
+
+
+ <script src="/resources/testharness.js"></script>
+
+ <script src="/resources/testharnessreport.js"></script>
+
+ <p>Test passes if there is a filled green square and <strong>no scrollbar</strong>.
+
+ <div id="scrollingContainerVert">
+
+ <div id="elemStickyVert"></div>
+
+ <div id="tallItem"></div>
+
+ </div>
+
+
+ <div id="scrollingContainerHoriz">
+
+ <div id="elemStickyHoriz"></div><div id="wideItem"></div>
+
+ </div>
+
+
+ <script>
+ test(()=> {
+ scrollingContainerVert.scrollTop = 600;
+ tallItem.style.display = "none";
+ assert_equals(scrollingContainerVert.scrollHeight, 100);
+ }, "Sticky position and its overflow contribution in the vertical axis");
+
+ test(()=> {
+ scrollingContainerHoriz.scrollLeft = 600;
+ wideItem.style.display = "none";
+ assert_equals(scrollingContainerHoriz.scrollWidth, 200);
+ }, "Sticky position and its overflow contribution in the horizontal axis");
+ </script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-002.html
new file mode 100644
index 0000000000..1cc162880a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-002.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Positioned Layout Test: an element with 'position: sticky' creates a stacking context</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+
+ <meta name="assert" content="This test checks that an element with 'position: sticky' creates a stacking context. In this test, the first 2 divs have the same 'auto' z-index value but since div#sticky is last in document tree order, then it must overlap div#overlapped-red. The final div#overlap-bottom-half-of-sticky exists to make sure that the div#sticky is squeezed, is 'sandwiched' between the other 2.">
+
+ <style>
+ div
+ {
+ height: 100px;
+ width: 100px;
+ }
+
+ div#overlapped-red
+ {
+ background-color: red;
+ position: absolute;
+ }
+
+ div#sticky
+ {
+ background: linear-gradient(to bottom, green 51%, red 49%);
+ position: sticky;
+ }
+
+ div#overlap-bottom-half-of-sticky
+ {
+ background-color: green;
+ bottom: 50px;
+ height: 50px;
+ position: relative;
+ }
+ </style>
+
+ <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+ <div id="overlapped-red"></div>
+
+ <div id="sticky"></div>
+
+ <div id="overlap-bottom-half-of-sticky"></div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-ref.html
new file mode 100644
index 0000000000..2db17cc424
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Reference for position: sticky should create a stacking context</title>
+
+<style>
+.indicator {
+ background-color: green;
+}
+
+.box {
+ width: 200px;
+ height: 200px;
+}
+</style>
+
+<div>You should see a single green box below. No red or blue should be visible.</div>
+
+<div class="indicator box"></div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context.html
new file mode 100644
index 0000000000..4e91dfbd91
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-stacking-context.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>position: sticky should create a stacking context</title>
+<link rel="match" href="position-sticky-stacking-context-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="position:sticky elements should create a stacking context" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.indicator {
+ position: absolute;
+ background-color: green;
+ z-index: 1;
+}
+
+.sticky {
+ position: sticky;
+ z-index: 0;
+}
+
+.child {
+ position: relative;
+ background-color: red;
+ z-index: 2;
+}
+
+.box {
+ width: 200px;
+ height: 200px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+})
+</script>
+
+<div>You should see a single green box below. No red or blue should be visible.</div>
+
+<div class="indicator box"></div>
+<div class="sticky box">
+ <!-- Because sticky forms a stacking context, this child remains on bottom
+ even though it has a higher z-index than the indicator box. -->
+ <div class="child box"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts-ref.html
new file mode 100644
index 0000000000..9ba42e18b2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>Nested position:sticky table elements should render correctly</title>
+<style>
+.scroller {
+ width: 100px;
+ height: 250px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+#child, td, th {
+ height: 50px;
+ width: 50px;
+ padding: 0;
+ background: green;
+}
+
+.prepadding {
+ height: 155px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 150;
+});
+</script>
+
+<div>There should be a green square at the top of the scroll view and no red or blue visible.</div>
+
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div id="child"></div></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts.html
new file mode 100644
index 0000000000..eb32099d44
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-parts.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Nested position:sticky table elements should render correctly</title>
+<link rel="match" href="position-sticky-table-parts-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky table elements render correctly" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 250px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+.child, td, th {
+ height: 50px;
+ width: 50px;
+ padding: 0;
+}
+
+.child {
+ background: green;
+}
+
+table * {
+ position: sticky;
+ top: 5px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: red;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.body.offsetTop;
+ document.getElementById('scroller1').scrollTop = 150;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>There should be a green square at the top of the scroll view and no red or blue visible.</div>
+
+<div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 155px;"></div>
+ <table>
+ <tbody>
+ <tr><td><div class="child"></div></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom-ref.html
new file mode 100644
index 0000000000..e851315641
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;td&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 150;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom.html
new file mode 100644
index 0000000000..7cd3b8d695
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-bottom.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;td&gt; elements</title>
+<link rel="match" href="position-sticky-table-td-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;td&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 150;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></td>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></td>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 200px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></td>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left-ref.html
new file mode 100644
index 0000000000..40f80128d1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky left constraint should behave correctly for &lt;td&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left.html
new file mode 100644
index 0000000000..34d31f3eb1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-left.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<title>position:sticky left constraint should behave correctly for &lt;td&gt; elements</title>
+<link rel="match" href="position-sticky-table-td-left-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky left constraint behaves correctly for &lt;td&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 100px;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ left: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right-ref.html
new file mode 100644
index 0000000000..633c2fa50e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky right constraint should behave correctly for &lt;td&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 150;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right.html
new file mode 100644
index 0000000000..093af91a55
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-right.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<title>position:sticky right constraint should behave correctly for &lt;td&gt; elements</title>
+<link rel="match" href="position-sticky-table-td-right-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky right constraint behaves correctly for &lt;td&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 150px;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ right: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 150;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td class="sticky"><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top-ref.html
new file mode 100644
index 0000000000..2ef7c2678b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;td&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 150;
+ document.getElementById('scroller2').scrollTop = 225;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top.html
new file mode 100644
index 0000000000..46931333c6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-td-top.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;td&gt; elements</title>
+<link rel="match" href="position-sticky-table-td-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;td&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 150;
+ document.getElementById('scroller2').scrollTop = 225;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 200px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td class="sticky"><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom-ref.html
new file mode 100644
index 0000000000..a1ee8e79f1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom.html
new file mode 100644
index 0000000000..4ddd0cb78e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tfoot-bottom.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+<link rel="match" href="position-sticky-table-tfoot-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;tfoot&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom-ref.html
new file mode 100644
index 0000000000..341a75ca34
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom.html
new file mode 100644
index 0000000000..bd6a824317
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-bottom.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left-ref.html
new file mode 100644
index 0000000000..7d0e5f9e58
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky left constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left.html
new file mode 100644
index 0000000000..7361b25ce6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-left.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<title>position:sticky left constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-left-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky left constraint behaves correctly for &lt;th&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 100px;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ left: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 100px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right-ref.html
new file mode 100644
index 0000000000..760376f10c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky right constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 200;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right.html
new file mode 100644
index 0000000000..a1598f5506
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-right.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<title>position:sticky right constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-right-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky right constraint behaves correctly for &lt;th&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 150px;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ right: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 200;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 300px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top-ref.html
new file mode 100644
index 0000000000..c9058c5522
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top.html
new file mode 100644
index 0000000000..57ff489e03
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-th-top.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top-ref.html
new file mode 100644
index 0000000000..d952c13b3b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top.html
new file mode 100644
index 0000000000..13c1b313c5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-thead-top.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+<link rel="match" href="position-sticky-table-thead-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;thead&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom-ref.html
new file mode 100644
index 0000000000..2f61ee6d3a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;tr&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom.html
new file mode 100644
index 0000000000..be9f1480a3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-bottom.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;tr&gt; elements</title>
+<link rel="match" href="position-sticky-table-tr-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;tr&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<!-- .sticky element pushed as far up as possible to table edge -->
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element stuck to bottom of .scroller -->
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element unstuck -->
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top-ref.html
new file mode 100644
index 0000000000..8b1989520b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;tr&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top.html
new file mode 100644
index 0000000000..5e38ad38d9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-table-tr-top.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;tr&gt; elements</title>
+<link rel="match" href="position-sticky-table-tr-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;tr&gt; elements" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see three green boxes below. No red or blue should be visible.</div>
+
+<!-- .sticky element not yet stuck -->
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element stuck to top of .scroller -->
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element pushed as down as possible to table edge -->
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-002.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-002.html
new file mode 100644
index 0000000000..e8025d679e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-002.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with top offset specified with px unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-top-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: left bottom;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: left 100px;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left 50px;
+ }
+
+ div.vertical-spacer
+ {
+ height: 100px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ height: 100px;
+ position: sticky;
+ top: 100px;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 200; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-003.html
new file mode 100644
index 0000000000..574b1b359b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-003.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with top offset specified with percentage unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-top-002-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: left bottom;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: left 100px;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left 50px;
+ }
+
+ div.vertical-spacer
+ {
+ height: 100px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ height: 100px;
+ position: sticky;
+ top: 40%;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 200; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: before reaching the sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <!--
+ second-scrolling-container: when reaching the sticking point and beyond
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div class="content">
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
+
+ <div class="vertical-spacer"></div> <!-- 150w x 100h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom-003.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom-003.html
new file mode 100644
index 0000000000..af68bcef72
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom-003.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Position Test: sticky element with top and bottom offsets specified with percentage unit</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+ <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos">
+ <link rel="match" href="reference/position-sticky-top-and-bottom-003-ref.html">
+
+ <meta name="flags" content="">
+
+ <style>
+ div.scrolling-container
+ {
+ background-image: url("support/100x100-red.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div#first-scrolling-container
+ {
+ background-position: left 125px;
+ }
+
+ div#second-scrolling-container
+ {
+ background-position: left 50px;
+ }
+
+ div#third-scrolling-container
+ {
+ background-position: left 25px;
+ }
+
+ div.vertical-spacer
+ {
+ height: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ bottom: 10%;
+ height: 100px;
+ position: sticky;
+ top: 10%;
+ width: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 150; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 250;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <!--
+ first-scrolling-container: when reaching the first (top) sticking point
+ -->
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ </div>
+
+ <!--
+ second-scrolling-container: between both sticking points
+ -->
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ </div>
+
+ <!--
+ third-scrolling-container: when reaching the second (bottom) sticking point
+ -->
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 150w x 200h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom.html
new file mode 100644
index 0000000000..c790eaaf3e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top-and-bottom.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>position:sticky elements can be constrained by top and bottom exceeding container size</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey both top and bottom constraints" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+
+.scroller {
+ height: 200px;
+ overflow: auto;
+ position: relative;
+}
+.container {
+ height: 120px;
+}
+.padding, .sticky {
+ height: 50px;
+}
+.overflow-padding {
+ height: 200px;
+}
+.sticky {
+ position: sticky;
+ background: green;
+ top: -25px;
+ bottom: 150px;
+}
+</style>
+
+<body>
+ <div class="scroller">
+ <div class="container">
+ <div class="padding"></div>
+ <div class="sticky"></div>
+ </div>
+ <div class="overflow-padding"></div>
+ </div>
+</body>
+
+<script>
+test(() => {
+ const scroller = document.querySelector('.scroller');
+ const element = document.querySelector('.sticky');
+ scroller.scrollTop = 0;
+ assert_equals(element.offsetTop, 0);
+}, 'initially the sticky box should be pushed to the top of the container');
+
+test(() => {
+ const scroller = document.querySelector('.scroller');
+ const element = document.querySelector('.sticky');
+ scroller.scrollTop = 95;
+ assert_equals(element.offsetTop, 70);
+}, 'when we scroll past the flow position the top constraint pushes it down');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-top.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top.html
new file mode 100644
index 0000000000..9929fc734b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-top.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the top constraint</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their top anchor after scrolling" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.scroller.scrollTop = 100;
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY);
+}, 'before reaching the sticking point the sticky box should not be offset');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.scroller.scrollTop = 200;
+
+ // This math cancels to sticky.offsetTop == (scroller.scrollTop + 50), but
+ // for clarity the calculations are left explicit.
+ const nonStickyTopY = elements.container.offsetTop +
+ elements.filler.clientHeight;
+ const targetTopY = elements.scroller.scrollTop + 50;
+ const stickyOffset = targetTopY - nonStickyTopY;
+
+ assert_equals(elements.sticky.offsetTop, nonStickyTopY + stickyOffset);
+}, 'after reaching the sticking point the sticky box should be offset');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.scroller.scrollTop = 300;
+ const maxOffsetInContainer = elements.container.offsetTop +
+ elements.container.clientHeight - elements.sticky.clientHeight;
+ assert_equals(elements.sticky.offsetTop, maxOffsetInContainer);
+}, 'the sticky box should not be pushed outside its containing block');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms-translate.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms-translate.html
new file mode 100644
index 0000000000..fb2ca6b85f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms-translate.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>translations on position:sticky elements should apply after sticking</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that translations on position:sticky elements are carried out on their stuck position" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body style="margin: 0;"></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.transform = 'translateY(-100%)';
+ elements.scroller.scrollTop = 100;
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ assert_equals(elements.sticky.getBoundingClientRect().y,
+ elements.scroller.getBoundingClientRect().y);
+}, 'Translation transform can move sticky element past sticking point');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.transform = 'translateY(50%)';
+ elements.scroller.scrollTop = 200;
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ const stickyElementOffset = elements.sticky.getBoundingClientRect().y -
+ elements.scroller.getBoundingClientRect().y;
+ assert_equals(stickyElementOffset, 100);
+}, 'Stuck elements can still be moved via translations');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.container.style.transform = 'translateY(100px)';
+ elements.scroller.scrollTop = 200;
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ // Here the sticky element will originally have stuck at 50px from the top,
+ // but is then 'pulled' downwards by the 100px container transform.
+ const stickyElementOffset = elements.sticky.getBoundingClientRect().y -
+ elements.scroller.getBoundingClientRect().y;
+ assert_equals(stickyElementOffset, 150);
+}, 'The sticky element should stick before the container is offset by a ' +
+ 'translation');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms.html
new file mode 100644
index 0000000000..080f05a4eb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-transforms.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>transforms on position:sticky elements should apply after sticking</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that transforms on position:sticky elements are carried out on their stuck position" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script src="../resources/sticky-util.js"></script>
+
+<body style="margin: 0;"></body>
+
+<script>
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.transform = 'scale(2)';
+ elements.scroller.scrollTop = 200;
+
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ // Scaling the sticky element by 2 means its top-y moves (1/2 * height)
+ // upwards, in this case placing it at the top of the viewport.
+ const boundingRect = elements.sticky.getBoundingClientRect();
+ assert_equals(boundingRect.y, elements.scroller.getBoundingClientRect().y);
+}, 'Scale transforms are carried out on the stuck element position');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.transform = 'rotateX(60deg)';
+ elements.scroller.scrollTop = 200;
+
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ // Rotating around the x-axis essentially 'squashes' it (from the camera's
+ // viewpoint), in this case shifting the offset to 75 rather than 50.
+ const stickyElementOffset = elements.sticky.getBoundingClientRect().y -
+ elements.scroller.getBoundingClientRect().y;
+ assert_equals(stickyElementOffset, 75);
+}, 'Rotate transforms are carried out on the stuck element position');
+
+test(() => {
+ const elements = setupStickyTest('top', 50);
+ elements.sticky.style.transform = 'perspective(3px) translateZ(1px)';
+ elements.scroller.scrollTop = 200;
+
+ // Transforms don't affect offsetTop, so use getBoundingClientRect.
+ const stickyElementOffset = elements.sticky.getBoundingClientRect().y -
+ elements.scroller.getBoundingClientRect().y;
+ assert_equals(stickyElementOffset, 25);
+}, 'Perspective transforms are carried out on the stuck element position');
+</script>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes-ref.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes-ref.html
new file mode 100644
index 0000000000..d55c22d953
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky constraints are independent of writing mode</title>
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 130px;
+ height: 200px;
+ overflow: hidden;
+ font: 30px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+ width: 200px;
+}
+
+.indicator {
+ display: inline;
+ color: green;
+ position: relative;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller1').scrollLeft = 20;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller2').scrollLeft = -25;
+});
+</script>
+
+<div>You should see two green blocks below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller" style="writing-mode: vertical-lr;">
+ <div class="contents">
+ <div class="indicator" style="left: 40px; top: 100px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller" style="writing-mode: vertical-rl;">
+ <div class="contents">
+ <div class="indicator" style="right: 45px; top: 100px;">XXX</div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes.html b/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes.html
new file mode 100644
index 0000000000..98f50500b8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/position-sticky-writing-modes.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<title>position:sticky constraints are independent of writing mode</title>
+<link rel="match" href="position-sticky-writing-modes-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky constraints are independent of the writing mode" />
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<script src="../resources/ref-rectangle.js"></script>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 130px;
+ height: 200px;
+ overflow: hidden;
+ font: 30px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+ width: 200px;
+}
+
+.indicator {
+ position: absolute;
+ color: red;
+}
+
+.sticky {
+ display: inline;
+ color: green;
+ position: sticky;
+ top: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller1').scrollLeft = 20;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller2').scrollLeft = -25;
+ createIndicatorForStickyElements(document.querySelectorAll('.sticky'));
+});
+</script>
+
+<div>You should see two green blocks below. No red or blue should be visible.</div>
+
+<div class="group">
+ <div id="scroller1" class="scroller" style="writing-mode: vertical-lr;">
+ <div class="indicator" style="left: 40px; top: 100px;">XXX</div>
+ <div class="contents">
+ <div class="sticky" style="left: 20px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller" style="writing-mode: vertical-rl;">
+ <div class="indicator" style="left: 55px; top: 100px;">XXX</div>
+ <div class="contents">
+ <div class="sticky" style="right: 20px;">XXX</div>
+ </div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-bottom-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-bottom-002-ref.html
new file mode 100644
index 0000000000..253ca7eebe
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-bottom-002-ref.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reftest Reference</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ div.scrolling-container
+ {
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div.vertical-spacer-200
+ {
+ height: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ height: 100px;
+ position: relative;
+ width: 100px;
+ }
+
+ div#first-sticky
+ {
+ bottom: 100px;
+ }
+
+ div#second-sticky
+ {
+ bottom: 50px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-fixed-ancestor-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-fixed-ancestor-002-ref.html
new file mode 100644
index 0000000000..abdc1dbcfa
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-fixed-ancestor-002-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reference File</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ html, body, div
+ {
+ background-color: green;
+ color: white;
+ font-size: 40vh;
+ height: 100%;
+ margin: 0;
+ }
+ </style>
+
+ <div>PASS</div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-left-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-left-002-ref.html
new file mode 100644
index 0000000000..75aa9a4c1f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-left-002-ref.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reftest Reference</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ div.scrolling-container
+ {
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div.horizontal-spacer-200
+ {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ position: relative;
+ vertical-align: top;
+ width: 100px;
+ }
+
+ div#first-sticky
+ {
+ left: 0px;
+ }
+
+ div#second-sticky
+ {
+ left: 50px;
+ }
+
+ div#third-sticky
+ {
+ left: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 150; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
+
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-right-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-right-002-ref.html
new file mode 100644
index 0000000000..3230e476b5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-right-002-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reftest Reference</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ div.scrolling-container
+ {
+ height: 150px;
+ margin-bottom: 30px;
+ overflow-y: hidden;
+ position: static;
+ white-space: nowrap;
+ width: 250px;
+ }
+
+ div.horizontal-spacer-200
+ {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ display: inline-block;
+ height: 100px;
+ position: relative;
+ vertical-align: top;
+ width: 100px;
+ }
+
+ div#first-sticky
+ {
+ right: 100px;
+ }
+
+ div#second-sticky
+ {
+ right: 50px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollLeft = 25; document.getElementById(&quot;second-scrolling-container&quot;).scrollLeft = 100; document.getElementById(&quot;third-scrolling-container&quot;).scrollLeft = 200;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <div id="first-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="first-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
+
+ <div id="second-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="second-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
+
+ <div id="third-scrolling-container" class="scrolling-container">
+
+ <div class="horizontal-spacer-200"></div><div id="third-sticky" class="sticky"></div><div class="horizontal-spacer-200"></div>
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-002-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-002-ref.html
new file mode 100644
index 0000000000..4a95591a56
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-002-ref.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reftest Reference</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ div.scrolling-container
+ {
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div.vertical-spacer-200
+ {
+ height: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ height: 100px;
+ position: relative;
+ width: 100px;
+ }
+
+ div#first-sticky
+ {
+ top: 0px;
+ }
+
+ div#second-sticky
+ {
+ top: 100px;
+ }
+
+ div#third-sticky
+ {
+ top: 100px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 200; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 300;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer-200"></div> <!-- 100w x 200h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-and-bottom-003-ref.html b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-and-bottom-003-ref.html
new file mode 100644
index 0000000000..36af9a33bc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/reference/position-sticky-top-and-bottom-003-ref.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+
+ <meta charset="UTF-8">
+
+ <title>CSS Reftest Reference</title>
+
+ <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+ <style>
+ div.scrolling-container
+ {
+ display: inline-block;
+ height: 250px;
+ margin-right: 30px;
+ overflow: auto;
+ position: static;
+ width: 150px;
+ }
+
+ div.vertical-spacer
+ {
+ height: 200px;
+ }
+
+ div.sticky
+ {
+ background-color: green;
+ height: 100px;
+ position: relative;
+ width: 100px;
+ }
+
+ div#first-sticky
+ {
+ bottom: 25px;
+ }
+
+ div#second-sticky
+ {
+ top: 0px;
+ }
+
+ div#third-sticky
+ {
+ top: 75px;
+ }
+ </style>
+
+ <body onload="document.getElementById(&quot;first-scrolling-container&quot;).scrollTop = 50; document.getElementById(&quot;second-scrolling-container&quot;).scrollTop = 150; document.getElementById(&quot;third-scrolling-container&quot;).scrollTop = 250;">
+
+ <p>Test passes if there are 3 filled green squares and <strong>no red</strong>.
+
+ <div id="first-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ <div id="first-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="second-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ <div id="second-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ </div>
+
+
+ <div id="third-scrolling-container" class="scrolling-container"> <!-- 150w x 250h viewport -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ <div id="third-sticky" class="sticky"></div> <!-- 100w x 100h -->
+
+ <div class="vertical-spacer"></div> <!-- 100w x 200h -->
+
+ </div>
diff --git a/testing/web-platform/tests/css/css-position/sticky/sticky-after-input.html b/testing/web-platform/tests/css/css-position/sticky/sticky-after-input.html
new file mode 100644
index 0000000000..9104aa3c5e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/sticky-after-input.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>Sticky positioned element should reset the scroll position to unshifted position</title>
+<link rel="author" title="Seokho Song" href="mailto:0xdevssh@gmail.com">
+<link rel="help" href="https://crbug.com/664246">
+<link rel="help" href="https://crbug.com/1178622">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<div id="scrollEl" style="height:100px; overflow-y: auto; scroll-padding:20px 20px 20px 20px;">
+ <input id="stickyEl" type="text" style="position:sticky;top:0" />
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+ <div>10</div>
+ <div>11</div>
+ <div>12</div>
+ <div>13</div>
+ <div>14</div>
+ <div>15</div>
+ <div>16</div>
+ <div>17</div>
+</div>
+</div>
+<script>
+
+async_test(t => {
+ var scrollEl = document.getElementById("scrollEl");
+ var stickyEl = document.getElementById("stickyEl");
+ stickyEl.focus()
+ scrollEl.scrollTo(0, scrollEl.scrollHeight);
+ scrollEl.addEventListener('input', ()=> {
+ requestAnimationFrame(t.step_func(()=>{
+ assert_equals(scrollEl.scrollTop, 0,
+ "should reset the scroll to unshifted sticky position");
+ t.done()
+ }))
+ })
+ test_driver.send_keys(stickyEl, "A")
+})
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/css-position/sticky/support/100x100-red.png b/testing/web-platform/tests/css/css-position/sticky/support/100x100-red.png
new file mode 100644
index 0000000000..57bf3ddc52
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/sticky/support/100x100-red.png
Binary files differ