Implement sticky positioning
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Aug 2012 17:39:45 +0000 (17:39 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Aug 2012 17:39:45 +0000 (17:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=90046

Reviewed by Ojan Vafai.

Source/WebCore:

Initial implementation of position: -webkit-sticky, which
constrains an element to be positioned inside the intersection
of its container box, and the viewport. Sticky elements create
stacking context.

A stickily positioned element behaves like position:relative
(space is reserved for it in-flow), but with an offset that is
determined by the sticky position. Changed isInFlowPositioned()
to cover relative and sticky.

Added a convenience isPositioned() to RenderObject(), which
is true for an object with any non-static position value.

Tests: fast/css/sticky/inflow-sticky.html
       fast/css/sticky/inline-sticky-abspos-child.html
       fast/css/sticky/inline-sticky.html
       fast/css/sticky/replaced-sticky.html
       fast/css/sticky/sticky-as-positioning-container.html
       fast/css/sticky/sticky-left-percentage.html
       fast/css/sticky/sticky-left.html
       fast/css/sticky/sticky-margins.html
       fast/css/sticky/sticky-side-margins.html
       fast/css/sticky/sticky-stacking-context.html
       fast/css/sticky/sticky-top-margins.html
       fast/css/sticky/sticky-top.html
       fast/css/sticky/sticky-writing-mode-horizontal-bt.html
       fast/css/sticky/sticky-writing-mode-vertical-lr.html
       fast/css/sticky/sticky-writing-mode-vertical-rl.html

* css/StyleResolver.cpp:
(WebCore::StyleResolver::adjustRenderStyle): Have position:sticky
create stacking context from the get-go, to make scrolling optimizations easier later.
* page/FrameView.cpp:
(WebCore::FrameView::scrollContentsFastPath): Use hasViewportConstrainedPosition().
* page/FrameView.h: FrameView's "fixed" objects contains both fixed and sticky objects now.
* rendering/RenderBlock.cpp: Use isPositioned().
(WebCore::RenderBlock::isSelectionRoot):
(WebCore::RenderBlock::renderName):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::styleWillChange): Need to look for both stick and fixed positioning to
determine whether to add something to FrameView's fixed object set.
(WebCore::RenderBox::computeRectForRepaint): Need to take the sticky offset into account
when computing repaint rects.
* rendering/RenderBox.h: Implement frameRectForStickyPositioning() for boxes.
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::updateBoxModelInfoFromStyle):
(WebCore::RenderBoxModelObject::adjustedPositionRelativeToOffsetParent):
(WebCore::RenderBoxModelObject::stickyPositionOffset): Compute the sticky position
offset by taking into account the viewport rect, and the conteriner's contentRect
inset by its margins.
(WebCore::RenderBoxModelObject::offsetForInFlowPosition): Convenience wrapper
for getting relative or sticky offset.
* rendering/RenderBoxModelObject.h: Have requiresLayer() use isPositioned().
(WebCore::RenderBoxModelObject::stickyPositionLogicalOffset):
* rendering/RenderInline.cpp:
(WebCore::RenderInline::styleWillChange): Need to implement this to
add/remove objects from FrameView's fixed object list, since, prior to sticky,
only boxes could be fixed.
(WebCore::RenderInline::renderName):
(WebCore::RenderInline::positionForPoint):
(WebCore::RenderInline::computeRectForRepaint):
* rendering/RenderInline.h:
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::updateLayerPositionsAfterScroll): Have to look for fixed or sticky.
(WebCore::RenderLayer::calculateClipRects): Use isPositioned().
(WebCore::RenderLayer::shouldBeNormalFlowOnly): Ditto.
* rendering/RenderLayer.h:
* rendering/RenderObject.cpp:
(WebCore::RenderObject::styleWillChange):
(WebCore::RenderObject::propagateStyleToAnonymousChildren): Should use isInFlowPositioned(),
not just isRelPositioned().
(WebCore::RenderObject::offsetParent): Use isPositioned().
* rendering/RenderObject.h:
(WebCore::RenderObject::isInFlowPositioned):
(WebCore::RenderObject::isStickyPositioned):
(WebCore::RenderObject::setStickyPositioned):
(WebCore::RenderObject::RenderObjectBitfields::RenderObjectBitfields):
(RenderObjectBitfields):
* rendering/RenderStyle.h: add hasViewportConstrainedPosition() for fixed or sticky position.

LayoutTests:

Various ref tests for sticky positioning.

* fast/css/sticky/inflow-sticky-expected.html: Added.
* fast/css/sticky/inflow-sticky.html: Added.
* fast/css/sticky/inline-sticky-abspos-child-expected.html: Added.
* fast/css/sticky/inline-sticky-abspos-child.html: Added.
* fast/css/sticky/inline-sticky-expected.html: Added.
* fast/css/sticky/inline-sticky.html: Added.
* fast/css/sticky/replaced-sticky-expected.html: Added.
* fast/css/sticky/replaced-sticky.html: Added.
* fast/css/sticky/sticky-as-positioning-container-expected.html: Added.
* fast/css/sticky/sticky-as-positioning-container.html: Added.
* fast/css/sticky/sticky-left-expected.html: Added.
* fast/css/sticky/sticky-left-percentage-expected.html: Added.
* fast/css/sticky/sticky-left-percentage.html: Added.
* fast/css/sticky/sticky-left.html: Added.
* fast/css/sticky/sticky-margins-expected.html: Added.
* fast/css/sticky/sticky-margins.html: Added.
* fast/css/sticky/sticky-side-margins-expected.html: Added.
* fast/css/sticky/sticky-side-margins.html: Added.
* fast/css/sticky/sticky-stacking-context-expected.html: Added.
* fast/css/sticky/sticky-stacking-context.html: Added.
* fast/css/sticky/sticky-top-expected.html: Added.
* fast/css/sticky/sticky-top-margins-expected.html: Added.
* fast/css/sticky/sticky-top-margins.html: Added.
* fast/css/sticky/sticky-top.html: Added.
* fast/css/sticky/sticky-writing-mode-horizontal-bt-expected.html: Added.
* fast/css/sticky/sticky-writing-mode-horizontal-bt.html: Added.
* fast/css/sticky/sticky-writing-mode-vertical-lr-expected.html: Added.
* fast/css/sticky/sticky-writing-mode-vertical-lr.html: Added.
* fast/css/sticky/sticky-writing-mode-vertical-rl-expected.html: Added.
* fast/css/sticky/sticky-writing-mode-vertical-rl.html: Added.

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

47 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/sticky/inflow-sticky-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/inflow-sticky.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/inline-sticky-abspos-child-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/inline-sticky-abspos-child.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/inline-sticky-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/inline-sticky.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/replaced-sticky-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/replaced-sticky.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-as-positioning-container-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-as-positioning-container.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-left-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-left-percentage-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-left-percentage.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-left.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-margins-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-margins.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-side-margins-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-side-margins.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-stacking-context-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-stacking-context.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-top-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-top-margins-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-top-margins.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-top.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl-expected.html [new file with mode: 0644]
LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/RenderBoxModelObject.cpp
Source/WebCore/rendering/RenderBoxModelObject.h
Source/WebCore/rendering/RenderInline.cpp
Source/WebCore/rendering/RenderInline.h
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayer.h
Source/WebCore/rendering/RenderObject.cpp
Source/WebCore/rendering/RenderObject.h
Source/WebCore/rendering/style/RenderStyle.h

index 5310bcd..227407c 100644 (file)
@@ -1,3 +1,43 @@
+2012-08-24  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement sticky positioning
+        https://bugs.webkit.org/show_bug.cgi?id=90046
+
+        Reviewed by Ojan Vafai.
+
+        Various ref tests for sticky positioning.
+
+        * fast/css/sticky/inflow-sticky-expected.html: Added.
+        * fast/css/sticky/inflow-sticky.html: Added.
+        * fast/css/sticky/inline-sticky-abspos-child-expected.html: Added.
+        * fast/css/sticky/inline-sticky-abspos-child.html: Added.
+        * fast/css/sticky/inline-sticky-expected.html: Added.
+        * fast/css/sticky/inline-sticky.html: Added.
+        * fast/css/sticky/replaced-sticky-expected.html: Added.
+        * fast/css/sticky/replaced-sticky.html: Added.
+        * fast/css/sticky/sticky-as-positioning-container-expected.html: Added.
+        * fast/css/sticky/sticky-as-positioning-container.html: Added.
+        * fast/css/sticky/sticky-left-expected.html: Added.
+        * fast/css/sticky/sticky-left-percentage-expected.html: Added.
+        * fast/css/sticky/sticky-left-percentage.html: Added.
+        * fast/css/sticky/sticky-left.html: Added.
+        * fast/css/sticky/sticky-margins-expected.html: Added.
+        * fast/css/sticky/sticky-margins.html: Added.
+        * fast/css/sticky/sticky-side-margins-expected.html: Added.
+        * fast/css/sticky/sticky-side-margins.html: Added.
+        * fast/css/sticky/sticky-stacking-context-expected.html: Added.
+        * fast/css/sticky/sticky-stacking-context.html: Added.
+        * fast/css/sticky/sticky-top-expected.html: Added.
+        * fast/css/sticky/sticky-top-margins-expected.html: Added.
+        * fast/css/sticky/sticky-top-margins.html: Added.
+        * fast/css/sticky/sticky-top.html: Added.
+        * fast/css/sticky/sticky-writing-mode-horizontal-bt-expected.html: Added.
+        * fast/css/sticky/sticky-writing-mode-horizontal-bt.html: Added.
+        * fast/css/sticky/sticky-writing-mode-vertical-lr-expected.html: Added.
+        * fast/css/sticky/sticky-writing-mode-vertical-lr.html: Added.
+        * fast/css/sticky/sticky-writing-mode-vertical-rl-expected.html: Added.
+        * fast/css/sticky/sticky-writing-mode-vertical-rl.html: Added.
+
 2012-08-27  Vsevolod Vlasov  <vsevik@chromium.org>
 
         Web Inspector: Debugger should break on failed assertions in Break on exceptions mode.
diff --git a/LayoutTests/fast/css/sticky/inflow-sticky-expected.html b/LayoutTests/fast/css/sticky/inflow-sticky-expected.html
new file mode 100644 (file)
index 0000000..f2fe60d
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        position: relative;
+        width: 250px;
+        height: 450px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 600px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .before {
+        background-color: orange;
+        height: 50px;
+    }
+
+    .after {
+        background-color: blue;
+        height: 50px;
+    }
+    .sticky {
+        background-color: green;
+        position: relative;
+        top: 250px;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: 0">
+        <div class="container">
+            <div class="before box"></div>
+            <div class="sticky box"></div>
+            <div class="after box"></div>
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/inflow-sticky.html b/LayoutTests/fast/css/sticky/inflow-sticky.html
new file mode 100644 (file)
index 0000000..3461efd
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 250px;
+        height: 450px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 600px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .before {
+        background-color: orange;
+        height: 50px;
+    }
+
+    .after {
+        background-color: blue;
+        height: 50px;
+    }
+    .sticky {
+        background-color: green;
+        position: -webkit-sticky;
+        top: 300px;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group" style="top: 100px">
+        <div class="container">
+            <div class="before box"></div>
+            <div class="sticky box"></div>
+            <div class="after box"></div>
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/inline-sticky-abspos-child-expected.html b/LayoutTests/fast/css/sticky/inline-sticky-abspos-child-expected.html
new file mode 100644 (file)
index 0000000..b1e7233
--- /dev/null
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+        font-family: 'Ahem';
+        font-size: 24px;
+        line-height: 2;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 220px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        width: 200px;
+        height: 200px;
+        color: blue;
+        position: relative;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        top: 50px;
+        left: 50px;
+        height: 50px;
+        width: 50px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 172px;">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 0">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 88px;">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 0;">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/inline-sticky-abspos-child.html b/LayoutTests/fast/css/sticky/inline-sticky-abspos-child.html
new file mode 100644 (file)
index 0000000..67dadf6
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+        font-family: 'Ahem';
+        font-size: 24px;
+        line-height: 2;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 220px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        width: 200px;
+        height: 200px;
+        color: blue;
+        position: -webkit-sticky;
+        top: 100px;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        top: 50px;
+        left: 50px;
+        height: 50px;
+        width: 50px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 200px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>
+            <div class="child box"></div>
+            XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/inline-sticky-expected.html b/LayoutTests/fast/css/sticky/inline-sticky-expected.html
new file mode 100644 (file)
index 0000000..9191dc2
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        font-family: 'Ahem';
+        font-size: 24px;
+        line-height: 2;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 220px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        width: 200px;
+        height: 200px;
+        color: blue;
+        position: relative;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        opacity: 0.8;
+        top: 50px;
+        left: 50px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 172px;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 0">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 88px;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky" style="top: 0;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/inline-sticky.html b/LayoutTests/fast/css/sticky/inline-sticky.html
new file mode 100644 (file)
index 0000000..32c525f
--- /dev/null
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+        font-family: 'Ahem';
+        font-size: 24px;
+        line-height: 2;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 220px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        width: 200px;
+        height: 200px;
+        color: blue;
+        position: -webkit-sticky;
+        top: 100px;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        opacity: 0.8;
+        top: 50px;
+        left: 50px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+    <div class="group" style="top: 200px">
+        <div class="indicator box"></div>
+        <div class="container">
+            XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/replaced-sticky-expected.html b/LayoutTests/fast/css/sticky/replaced-sticky-expected.html
new file mode 100644 (file)
index 0000000..a51843a
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+    
+    .sticky {
+        background-color: silver;
+        position: relative;
+        top: 100px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            <img src="../resources/greenbox.png" class="sticky box" style="top: 200px;">
+        </div>
+    </div>
+
+    <div class="group" style="top: 0">
+        <div class="indicator box"></div>
+        <div class="container">
+            <img src="../resources/greenbox.png" class="sticky box" style="top: 100px;">
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/replaced-sticky.html b/LayoutTests/fast/css/sticky/replaced-sticky.html
new file mode 100644 (file)
index 0000000..7951e93
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+    
+    .sticky {
+        background-color: silver;
+        position: -webkit-sticky;
+        top: 100px;
+    }
+    
+    .indicator {
+        display: none;
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box"></div>
+        <div class="container">
+            <img src="../resources/greenbox.png" class="sticky box">
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="indicator box"></div>
+        <div class="container">
+            <img src="../resources/greenbox.png" class="sticky box">
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-as-positioning-container-expected.html b/LayoutTests/fast/css/sticky/sticky-as-positioning-container-expected.html
new file mode 100644 (file)
index 0000000..823cb4b
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: relative;
+        top: 200px;
+        background-color: silver;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        top: 50px;
+        left: 50px;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px;">
+        <div class="indicator box"></div>
+        <div class="container">
+            <div class="sticky box">
+                <div class="child box"></div>
+            </div>
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-as-positioning-container.html b/LayoutTests/fast/css/sticky/sticky-as-positioning-container.html
new file mode 100644 (file)
index 0000000..972b82b
--- /dev/null
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        top: 100px;
+        background-color: silver;
+    }
+    
+    .child {
+        position: absolute;
+        background-color: green;
+        top: 50px;
+        left: 50px;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 250px;
+        left: 50px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box"></div>
+        <div class="container">
+            <div class="sticky box">
+                <div class="child box"></div>
+            </div>
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-left-expected.html b/LayoutTests/fast/css/sticky/sticky-left-expected.html
new file mode 100644 (file)
index 0000000..35f1ee4
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        background-color: green;
+        position: absolute;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="left: -100px;">
+        <div class="indicator box" style="left: 200px;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 200px"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 0">
+        <div class="indicator box" style="left: 100px;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 100px"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 100px">
+        <div class="indicator box" style="left: 0;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 0"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-left-percentage-expected.html b/LayoutTests/fast/css/sticky/sticky-left-percentage-expected.html
new file mode 100644 (file)
index 0000000..2a1ab02
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: absolute;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="left: -100px;">
+        <div class="indicator box" style="left: 200px;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 200px"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 0">
+        <div class="indicator box" style="left: 100px;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 12.5vw"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 100px">
+        <div class="indicator box" style="left: 0;"></div>
+        <div class="container">
+            <div class="sticky box" style="left: 0"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-left-percentage.html b/LayoutTests/fast/css/sticky/sticky-left-percentage.html
new file mode 100644 (file)
index 0000000..b03c493
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        /* DRT window is 800px wide */
+        left: 12.5%;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(100, 0);
+        window.resizeTo(800, 700)
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box" style="left: 200px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 100px">
+        <div class="indicator box" style="left: 100px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 200px">
+        <div class="indicator box" style="left: 0;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-left.html b/LayoutTests/fast/css/sticky/sticky-left.html
new file mode 100644 (file)
index 0000000..fdb4d84
--- /dev/null
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        left: 100px;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(100, 0);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box" style="left: 200px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 100px">
+        <div class="indicator box" style="left: 100px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 200px">
+        <div class="indicator box" style="left: 0;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-margins-expected.html b/LayoutTests/fast/css/sticky/sticky-margins-expected.html
new file mode 100644 (file)
index 0000000..fddb519
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 200px;
+        height: 500px;
+    }
+    
+    .container {
+        position: relative;
+        width: 150px;
+        height: 400px;
+        border: 2px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 120px;
+        height: 120px;
+    }
+
+    .sticky {
+        position: relative;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 37px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <!-- auto margins -->
+    <div class="group" style="top: -300px">
+        <div class="indicator box" style="top: 110px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: auto; top: 280px"></div>
+        </div>
+    </div>
+
+    <!-- px margins -->
+    <div class="group" style="top: -300px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: 20px; top: 240px"></div>
+        </div>
+    </div>
+
+    <!-- % margins -->
+    <div class="group" style="top: -300px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: 20%; top: 220px"></div>
+        </div>
+    </div>
+
+    <!-- % margins -->
+    <div class="group" style="top: -300px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container" style="width: 200px;">
+            <div class="sticky box" style="margin: 20%; top: 200px"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-margins.html b/LayoutTests/fast/css/sticky/sticky-margins.html
new file mode 100644 (file)
index 0000000..9966049
--- /dev/null
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 200px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 150px;
+        height: 400px;
+        border: 2px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 120px;
+        height: 120px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        top: 10px;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 37px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 300);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <!-- auto margins -->
+    <div class="group">
+        <div class="indicator box" style="top: 110px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: auto;"></div>
+        </div>
+    </div>
+
+    <!-- px margins -->
+    <div class="group">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: 20px;"></div>
+        </div>
+    </div>
+
+    <!-- % margins -->
+    <div class="group">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box" style="margin: 20%;"></div>
+        </div>
+    </div>
+
+    <!-- % margins -->
+    <div class="group">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container" style="width: 200px;">
+            <div class="sticky box" style="margin: 20%;"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-side-margins-expected.html b/LayoutTests/fast/css/sticky/sticky-side-margins-expected.html
new file mode 100644 (file)
index 0000000..25e70f2
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        position: relative;
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: absolute;
+        background-color: green;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="left: -100px">
+        <div class="container">
+            <div class="sticky box" style="right: 0;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: -100px">
+        <div class="container">
+            <div class="sticky box" style="right: 20px;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: -100px">
+        <div class="container">
+            <div class="sticky box" style="right: 80px"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-side-margins.html b/LayoutTests/fast/css/sticky/sticky-side-margins.html
new file mode 100644 (file)
index 0000000..731b18b
--- /dev/null
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 200px;
+    }
+    
+    .container {
+        width: 400px;
+        height: 180px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        left: 100px;
+        background-color: green;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(300, 0);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group" style="left: 200px">
+        <div class="container">
+            <div class="sticky box" style="margin: 0 auto;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 200px">
+        <div class="container">
+            <div class="sticky box" style="margin: 0 20px;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="left: 200px">
+        <div class="container">
+            <div class="sticky box" style="margin: 0 20%;"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-stacking-context-expected.html b/LayoutTests/fast/css/sticky/sticky-stacking-context-expected.html
new file mode 100644 (file)
index 0000000..9c1cc98
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+    }
+    
+    .child {
+        position: relative;
+        background-color: green;
+        z-index: 2;
+    }
+</style>
+</head>
+<body>
+    <div class="group">
+        <div class="container">
+            <div class="sticky box">
+                <div class="child box"></div>
+            </div>
+        </div>
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-stacking-context.html b/LayoutTests/fast/css/sticky/sticky-stacking-context.html
new file mode 100644 (file)
index 0000000..d0ce938
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+    }
+    
+    .intermediate {
+        position: absolute;
+        z-index: 1;
+        background-color: green
+    }
+    
+    .child {
+        position: relative;
+        background-color: red;
+        z-index: 2;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: blue;
+        z-index: 0;
+    }
+</style>
+</head>
+<body>
+    <div class="indicator box"></div>
+    <div class="container">
+        <div class="intermediate box"></div>
+        <div class="sticky box">
+            <div class="child box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-top-expected.html b/LayoutTests/fast/css/sticky/sticky-top-expected.html
new file mode 100644 (file)
index 0000000..eca9bcf
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: absolute;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px;">
+        <div class="indicator box" style="top: 200px;"></div>
+        <div class="container">
+            <div class="sticky box" style="top: 200px;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 0">
+        <div class="indicator box" style="top: 100px;"></div>
+        <div class="container">
+            <div class="sticky box" style="top: 100px;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="indicator box" style="top: 0;"></div>
+        <div class="container">
+            <div class="sticky box" style="top: 0;"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-top-margins-expected.html b/LayoutTests/fast/css/sticky/sticky-top-margins-expected.html
new file mode 100644 (file)
index 0000000..b46bf7a
--- /dev/null
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        border: 12px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 160px;
+        height: 160px;
+    }
+
+    .sticky {
+        position: relative;
+        margin: 20px;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 37px;
+        background-color: red;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px;">
+        <div class="indicator box" style="top: 110px;"></div>
+        <div class="container">
+            <div class="sticky box" style="top: 73px"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 0">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-top-margins.html b/LayoutTests/fast/css/sticky/sticky-top-margins.html
new file mode 100644 (file)
index 0000000..76da84e
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        border: 12px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 160px;
+        height: 160px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        top: 10px;
+        margin: 20px;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 37px;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box" style="top: 110px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 200px">
+        <div class="indicator box" style="top: 37px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-top.html b/LayoutTests/fast/css/sticky/sticky-top.html
new file mode 100644 (file)
index 0000000..76c91b8
--- /dev/null
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        width: 200px;
+        height: 400px;
+        outline: 2px solid black;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        top: 100px;
+        background-color: green;
+    }
+    
+    .indicator {
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: red;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="indicator box" style="top: 200px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="indicator box" style="top: 100px;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 200px">
+        <div class="indicator box" style="top: 0;"></div>
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt-expected.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt-expected.html
new file mode 100644 (file)
index 0000000..68b9ad2
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        -webkit-writing-mode: horizontal-bt;
+        width: 200px;
+        height: 400px;
+        border: 10px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: relative;
+        background-color: green;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="top: -100px">
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 100px">
+        <div class="container">
+            <div class="sticky box" style="top: -15px;"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 300px">
+        <div class="container">
+            <div class="sticky box" style="top: -200px;"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-horizontal-bt.html
new file mode 100644 (file)
index 0000000..a18ad14
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        height: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        display: inline-block;
+        position: relative;
+        width: 250px;
+        height: 500px;
+    }
+    
+    .container {
+        -webkit-writing-mode: horizontal-bt;
+        width: 200px;
+        height: 400px;
+        border: 10px solid black;
+        padding: 5px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 200px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        top: 100px;
+        bottom: 100px;
+        background-color: green;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(0, 100);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group">
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 200px">
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+
+    <div class="group" style="top: 400px">
+        <div class="container">
+            <div class="sticky box"></div>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr-expected.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr-expected.html
new file mode 100644 (file)
index 0000000..b875858
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 160px;
+    }
+    
+    .container {
+        -webkit-writing-mode: vertical-lr;
+        width: 300px;
+        height: 160px;
+        border: 2px solid black;
+        margin: 10px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: relative;
+        background-color: green;
+        width: 100px;
+        height: 100px;
+        margin: 5px;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="left: -200px">
+        <div class="container">Before
+            <div class="sticky box" style="margin: auto; left: 172px;"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 300px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 700px">
+        <div class="container">Before
+            <div class="sticky box" style="left: -18px;"></div>After
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-lr.html
new file mode 100644 (file)
index 0000000..79042fe
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 160px;
+    }
+    
+    .container {
+        -webkit-writing-mode: vertical-lr;
+        width: 300px;
+        height: 160px;
+        border: 2px solid black;
+        margin: 10px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        background-color: green;
+        width: 100px;
+        height: 100px;
+        left: 20px;
+        right: 20px;
+        margin: 5px;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(100, 0);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group" style="left: -100px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 400px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 800px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl-expected.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl-expected.html
new file mode 100644 (file)
index 0000000..d099449
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 160px;
+    }
+    
+    .container {
+        -webkit-writing-mode: vertical-rl;
+        width: 300px;
+        height: 160px;
+        border: 2px solid black;
+        margin: 10px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: relative;
+        background-color: green;
+        width: 100px;
+        height: 100px;
+        margin: 5px;
+    }
+</style>
+</head>
+<body>
+    <div class="group" style="left: -200px">
+        <div class="container">Before
+            <div class="sticky box" style="left: 18px;"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 300px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 700px">
+        <div class="container">Before
+            <div class="sticky box" style="left: -172px;"></div>After
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl.html b/LayoutTests/fast/css/sticky/sticky-writing-mode-vertical-rl.html
new file mode 100644 (file)
index 0000000..5b041ab
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<style>
+    body {
+        margin: 0;
+        width: 2000px;
+        overflow: hidden; /* hide scrollbars */
+    }
+    
+    .group {
+        position: relative;
+        width: 500px;
+        height: 160px;
+    }
+    
+    .container {
+        -webkit-writing-mode: vertical-rl;
+        width: 300px;
+        height: 160px;
+        border: 2px solid black;
+        margin: 10px;
+    }
+    
+    .box {
+        width: 200px;
+        height: 180px;
+    }
+
+    .sticky {
+        position: -webkit-sticky;
+        background-color: green;
+        width: 100px;
+        height: 100px;
+        left: 20px;
+        right: 20px;
+        margin: 5px;
+    }
+</style>
+<script>
+    function doTest()
+    {
+        window.scrollTo(100, 0);
+    }
+    window.addEventListener('load', doTest, false);
+</script>
+</head>
+<body>
+    <div class="group" style="left: -100px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 400px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+
+    <div class="group" style="left: 800px">
+        <div class="container">Before
+            <div class="sticky box"></div>After
+        </div>
+    </div>
+</body>
+</html>
index 711971c..cb55596 100644 (file)
@@ -1,3 +1,90 @@
+2012-08-24  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement sticky positioning
+        https://bugs.webkit.org/show_bug.cgi?id=90046
+
+        Reviewed by Ojan Vafai.
+
+        Initial implementation of position: -webkit-sticky, which 
+        constrains an element to be positioned inside the intersection
+        of its container box, and the viewport. Sticky elements create
+        stacking context.
+        
+        A stickily positioned element behaves like position:relative
+        (space is reserved for it in-flow), but with an offset that is
+        determined by the sticky position. Changed isInFlowPositioned()
+        to cover relative and sticky.
+        
+        Added a convenience isPositioned() to RenderObject(), which
+        is true for an object with any non-static position value.
+        
+        Tests: fast/css/sticky/inflow-sticky.html
+               fast/css/sticky/inline-sticky-abspos-child.html
+               fast/css/sticky/inline-sticky.html
+               fast/css/sticky/replaced-sticky.html
+               fast/css/sticky/sticky-as-positioning-container.html
+               fast/css/sticky/sticky-left-percentage.html
+               fast/css/sticky/sticky-left.html
+               fast/css/sticky/sticky-margins.html
+               fast/css/sticky/sticky-side-margins.html
+               fast/css/sticky/sticky-stacking-context.html
+               fast/css/sticky/sticky-top-margins.html
+               fast/css/sticky/sticky-top.html
+               fast/css/sticky/sticky-writing-mode-horizontal-bt.html
+               fast/css/sticky/sticky-writing-mode-vertical-lr.html
+               fast/css/sticky/sticky-writing-mode-vertical-rl.html
+
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::adjustRenderStyle): Have position:sticky
+        create stacking context from the get-go, to make scrolling optimizations easier later.
+        * page/FrameView.cpp:
+        (WebCore::FrameView::scrollContentsFastPath): Use hasViewportConstrainedPosition().
+        * page/FrameView.h: FrameView's "fixed" objects contains both fixed and sticky objects now.
+        * rendering/RenderBlock.cpp: Use isPositioned().
+        (WebCore::RenderBlock::isSelectionRoot):
+        (WebCore::RenderBlock::renderName):
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::styleWillChange): Need to look for both stick and fixed positioning to
+        determine whether to add something to FrameView's fixed object set.
+        (WebCore::RenderBox::computeRectForRepaint): Need to take the sticky offset into account
+        when computing repaint rects.
+        * rendering/RenderBox.h: Implement frameRectForStickyPositioning() for boxes.
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::RenderBoxModelObject::updateBoxModelInfoFromStyle):
+        (WebCore::RenderBoxModelObject::adjustedPositionRelativeToOffsetParent):
+        (WebCore::RenderBoxModelObject::stickyPositionOffset): Compute the sticky position
+        offset by taking into account the viewport rect, and the conteriner's contentRect
+        inset by its margins.
+        (WebCore::RenderBoxModelObject::offsetForInFlowPosition): Convenience wrapper
+        for getting relative or sticky offset.
+        * rendering/RenderBoxModelObject.h: Have requiresLayer() use isPositioned().
+        (WebCore::RenderBoxModelObject::stickyPositionLogicalOffset):
+        * rendering/RenderInline.cpp:
+        (WebCore::RenderInline::styleWillChange): Need to implement this to 
+        add/remove objects from FrameView's fixed object list, since, prior to sticky,
+        only boxes could be fixed.
+        (WebCore::RenderInline::renderName):
+        (WebCore::RenderInline::positionForPoint):
+        (WebCore::RenderInline::computeRectForRepaint):
+        * rendering/RenderInline.h:
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::updateLayerPositionsAfterScroll): Have to look for fixed or sticky.
+        (WebCore::RenderLayer::calculateClipRects): Use isPositioned().
+        (WebCore::RenderLayer::shouldBeNormalFlowOnly): Ditto.
+        * rendering/RenderLayer.h:
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::styleWillChange):
+        (WebCore::RenderObject::propagateStyleToAnonymousChildren): Should use isInFlowPositioned(),
+        not just isRelPositioned().
+        (WebCore::RenderObject::offsetParent): Use isPositioned().
+        * rendering/RenderObject.h:
+        (WebCore::RenderObject::isInFlowPositioned):
+        (WebCore::RenderObject::isStickyPositioned):
+        (WebCore::RenderObject::setStickyPositioned):
+        (WebCore::RenderObject::RenderObjectBitfields::RenderObjectBitfields):
+        (RenderObjectBitfields):
+        * rendering/RenderStyle.h: add hasViewportConstrainedPosition() for fixed or sticky position.
+
 2012-08-27  Julien Chaffraix  <jchaffraix@webkit.org>
 
         Unreviewed Mac Chromium build fix after r126763.
index 08ee28c..15de3a7 100644 (file)
@@ -2196,6 +2196,7 @@ void StyleResolver::adjustRenderStyle(RenderStyle* style, RenderStyle* parentSty
         || style->hasMask()
         || style->boxReflect()
         || style->hasFilter()
+        || style->position() == StickyPosition
 #ifdef FIXED_POSITION_CREATES_STACKING_CONTEXT
         || style->position() == FixedPosition
 #else
index bdb3e85..07ad862 100644 (file)
@@ -1502,7 +1502,7 @@ bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect
     FixedObjectSet::const_iterator end = m_fixedObjects->end();
     for (FixedObjectSet::const_iterator it = m_fixedObjects->begin(); it != end; ++it) {
         RenderObject* renderer = *it;
-        if (renderer->style()->position() != FixedPosition)
+        if (!renderer->style()->hasViewportConstrainedPosition())
             continue;
 #if USE(ACCELERATED_COMPOSITING)
         if (renderer->isComposited())
index 7e73150..8da177e 100644 (file)
@@ -197,6 +197,7 @@ public:
     void removeSlowRepaintObject();
     bool hasSlowRepaintObjects() const { return m_slowRepaintObjectCount; }
 
+    // This includes position:fixed and sticky objects.
     typedef HashSet<RenderObject*> FixedObjectSet;
     void addFixedObject(RenderObject*);
     void removeFixedObject(RenderObject*);
index 31b8fd5..8277077 100755 (executable)
@@ -3210,7 +3210,7 @@ bool RenderBlock::isSelectionRoot() const
         return false;
         
     if (isBody() || isRoot() || hasOverflowClip()
-        || isInFlowPositioned() || isFloatingOrOutOfFlowPositioned()
+        || isPositioned() || isFloating()
         || isTableCell() || isInlineBlockOrInlineTable()
         || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot())
         return true;
@@ -7362,6 +7362,8 @@ const char* RenderBlock::renderName() const
         return "RenderBlock (generated)";
     if (isRelPositioned())
         return "RenderBlock (relative positioned)";
+    if (isStickyPositioned())
+        return "RenderBlock (sticky positioned)";
     if (isRunIn())
         return "RenderBlock (run-in)";
     return "RenderBlock";
index f16457b..1a1714f 100644 (file)
@@ -206,10 +206,10 @@ void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyl
         view()->repaint();
 
     if (FrameView *frameView = view()->frameView()) {
-        bool newStyleIsFixed = newStyle && newStyle->position() == FixedPosition;
-        bool oldStyleIsFixed = oldStyle && oldStyle->position() == FixedPosition;
-        if (newStyleIsFixed != oldStyleIsFixed) {
-            if (newStyleIsFixed)
+        bool newStyleIsViewportConstained = newStyle && newStyle->hasViewportConstrainedPosition();
+        bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition();
+        if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) {
+            if (newStyleIsViewportConstained)
                 frameView->addFixedObject(this);
             else
                 frameView->removeFixedObject(this);
@@ -1577,7 +1577,7 @@ void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, La
 
     if (position == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline())
         topLeft += toRenderInline(o)->offsetForInFlowPositionedInline(this);
-    else if ((position == RelativePosition) && layer()) {
+    else if ((position == RelativePosition || position == StickyPosition) && layer()) {
         // Apply the relative position offset when invalidating a rectangle.  The layer
         // is translated, but the render box isn't, so we need to do this to get the
         // right dirty rect.  Since this is called from RenderObject::setStyle, the relative position
index f011714..f3c6d5a 100644 (file)
@@ -44,7 +44,7 @@ public:
 
     // hasAutoZIndex only returns true if the element is positioned or a flex-item since
     // position:static elements that are not flex-items get their z-index coerced to auto.
-    virtual bool requiresLayer() const OVERRIDE { return isRoot() || isOutOfFlowPositioned() || isInFlowPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns() || !style()->hasAutoZIndex(); }
+    virtual bool requiresLayer() const OVERRIDE { return isRoot() || isPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns() || !style()->hasAutoZIndex(); }
 
     // Use this with caution! No type checking is done!
     RenderBox* firstChildBox() const;
@@ -579,6 +579,8 @@ private:
     // These include tables, positioned objects, floats and flexible boxes.
     virtual void computePreferredLogicalWidths() { setPreferredLogicalWidthsDirty(false); }
 
+    virtual LayoutRect frameRectForStickyPositioning() const OVERRIDE { return frameRect(); }
+
 private:
     // The width/height of the contents + borders + padding.  The x/y location is relative to our container (which is not always our parent).
     LayoutRect m_frameRect;
index 7dcd241..5053d87 100644 (file)
@@ -456,6 +456,7 @@ void RenderBoxModelObject::updateBoxModelInfoFromStyle()
     setHasBoxDecorations(hasBackground() || styleToUse->hasBorder() || styleToUse->hasAppearance() || styleToUse->boxShadow());
     setInline(styleToUse->isDisplayInlineType());
     setRelPositioned(styleToUse->position() == RelativePosition);
+    setStickyPositioned(styleToUse->position() == StickyPosition);
     setHorizontalWritingMode(styleToUse->isHorizontalWritingMode());
 }
 
@@ -534,6 +535,8 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
         if (!isOutOfFlowPositioned()) {
             if (isRelPositioned())
                 referencePoint.move(relativePositionOffset());
+            else if (isStickyPositioned())
+                referencePoint.move(stickyPositionOffset());
             const RenderObject* curr = parent();
             while (curr != offsetParent) {
                 // FIXME: What are we supposed to do inside SVG content?
@@ -542,7 +545,7 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
                 referencePoint.move(curr->parent()->offsetForColumns(referencePoint));
                 curr = curr->parent();
             }
-            if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isRelPositioned() && !offsetParent->isOutOfFlowPositioned())
+            if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned())
                 referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation());
         }
     }
@@ -550,11 +553,87 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
     return referencePoint;
 }
 
+LayoutSize RenderBoxModelObject::stickyPositionOffset() const
+{
+    RenderBlock* containingBlock = this->containingBlock();
+
+    LayoutRect viewportRect = view()->frameView()->visibleContentRect();
+    LayoutRect containerContentRect = containingBlock->contentBoxRect();
+
+    LayoutUnit minLeftMargin = minimumValueForLength(style()->marginLeft(), containingBlock->availableLogicalWidth(), view());
+    LayoutUnit minTopMargin = minimumValueForLength(style()->marginTop(), containingBlock->availableLogicalWidth(), view());
+    LayoutUnit minRightMargin = minimumValueForLength(style()->marginRight(), containingBlock->availableLogicalWidth(), view());
+    LayoutUnit minBottomMargin = minimumValueForLength(style()->marginBottom(), containingBlock->availableLogicalWidth(), view());
+
+    // Compute the container-relative area within which the sticky element is allowed to move.
+    containerContentRect.move(minLeftMargin, minTopMargin);
+    containerContentRect.contract(minLeftMargin + minRightMargin, minTopMargin + minBottomMargin);
+    FloatRect absContainerContentRect = containingBlock->localToAbsoluteQuad(FloatRect(containerContentRect)).boundingBox();
+
+    LayoutRect stickyBoxRect = frameRectForStickyPositioning();
+    LayoutRect flippedStickyBoxRect = stickyBoxRect;
+    containingBlock->flipForWritingMode(flippedStickyBoxRect);
+    LayoutPoint stickyLocation = flippedStickyBoxRect.location();
+
+    // FIXME: sucks to call localToAbsolute again, but we can't just offset from the previously computed rect if there are transforms.
+    FloatRect absContainerFrame = containingBlock->localToAbsoluteQuad(FloatRect(FloatPoint(), containingBlock->size())).boundingBox();
+    // We can't call localToAbsolute on |this| because that will recur. FIXME: For now, assume that |this| is not transformed.
+    FloatRect absoluteStickyBoxRect(absContainerFrame.location() + stickyLocation, flippedStickyBoxRect.size());
+
+    FloatPoint originalLocation = absoluteStickyBoxRect.location();
+
+    // Horizontal position.
+    // FIXME: if left and right are specified, allow right to override left.
+    if (!style()->left().isAuto()) {
+        LayoutUnit leftLimit = viewportRect.x() + valueForLength(style()->left(), viewportRect.width(), view());
+        if (absoluteStickyBoxRect.x() < leftLimit)
+            absoluteStickyBoxRect.setX(leftLimit);
+
+        if (absoluteStickyBoxRect.maxX() > absContainerContentRect.maxX())
+            absoluteStickyBoxRect.setX(absContainerContentRect.maxX() - absoluteStickyBoxRect.width());
+    }
+    
+    if (!style()->right().isAuto()) {
+        LayoutUnit rightLimit = viewportRect.maxX() - valueForLength(style()->right(), viewportRect.width(), view());
+        if (absoluteStickyBoxRect.maxX() > rightLimit)
+            absoluteStickyBoxRect.setX(rightLimit - absoluteStickyBoxRect.width());
+            
+        if (absoluteStickyBoxRect.x() < absContainerContentRect.x())
+            absoluteStickyBoxRect.setX(absContainerContentRect.x());
+    }
+
+    // Vertical position.
+    // FIXME: if top and bottom are specified, allow bottom to override top.
+    if (!style()->top().isAuto()) {
+        LayoutUnit topLimit = viewportRect.y() + valueForLength(style()->top(), viewportRect.height(), view());
+        if (absoluteStickyBoxRect.y() < topLimit)
+            absoluteStickyBoxRect.setY(topLimit);
+
+        if (absoluteStickyBoxRect.maxY() > absContainerContentRect.maxY())
+            absoluteStickyBoxRect.setY(absContainerContentRect.maxY() - absoluteStickyBoxRect.height());
+    }
+    
+    if (!style()->bottom().isAuto()) {
+        LayoutUnit bottomLimit = viewportRect.maxY() - valueForLength(style()->bottom(), viewportRect.height(), view());
+        if (absoluteStickyBoxRect.maxY() > bottomLimit)
+            absoluteStickyBoxRect.setY(bottomLimit - absoluteStickyBoxRect.height());
+            
+        if (absoluteStickyBoxRect.y() < absContainerContentRect.y())
+            absoluteStickyBoxRect.setY(absContainerContentRect.y());
+    }
+
+    // The sticky offset is physical, so we can just return the delta computed in absolute coords (though it may be wrong with transforms).
+    return roundedLayoutSize(absoluteStickyBoxRect.location() - originalLocation);
+}
+
 LayoutSize RenderBoxModelObject::offsetForInFlowPosition() const
 {
     if (isRelPositioned())
         return relativePositionOffset();
 
+    if (isStickyPositioned())
+        return stickyPositionOffset();
+
     return LayoutSize();
 }
 
index 8d5e83e..3304e97 100644 (file)
@@ -62,6 +62,9 @@ public:
     LayoutSize relativePositionOffset() const;
     LayoutSize relativePositionLogicalOffset() const { return style()->isHorizontalWritingMode() ? relativePositionOffset() : relativePositionOffset().transposedSize(); }
 
+    LayoutSize stickyPositionOffset() const;
+    LayoutSize stickyPositionLogicalOffset() const { return style()->isHorizontalWritingMode() ? stickyPositionOffset() : stickyPositionOffset().transposedSize(); }
+
     LayoutSize offsetForInFlowPosition() const;
 
     // IE extensions. Used to calculate offsetWidth/Height.  Overridden by inlines (RenderFlow)
@@ -82,7 +85,7 @@ public:
 
     bool hasSelfPaintingLayer() const;
     RenderLayer* layer() const { return m_layer; }
-    virtual bool requiresLayer() const { return isRoot() || isOutOfFlowPositioned() || isInFlowPositioned() || isTransparent() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
+    virtual bool requiresLayer() const { return isRoot() || isPositioned() || isTransparent() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
 
     // This will work on inlines to return the bounding box of all of the lines' border boxes.
     virtual IntRect borderBoundingBox() const = 0;
@@ -275,6 +278,8 @@ public:
 
 private:
     virtual bool isBoxModelObject() const { return true; }
+    
+    virtual LayoutRect frameRectForStickyPositioning() const = 0;
 
     IntSize calculateFillTileSize(const FillLayer*, const IntSize& scaledPositioningAreaSize) const;
 
index b139bcb..ebb9500 100644 (file)
@@ -155,6 +155,23 @@ static void updateStyleOfAnonymousBlockContinuations(RenderObject* block, const
     }
 }
 
+void RenderInline::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+    if (FrameView *frameView = view()->frameView()) {
+        RenderStyle* oldStyle = style();
+        bool newStyleIsSticky = newStyle && newStyle->position() == StickyPosition;
+        bool oldStyleIsSticky = oldStyle && oldStyle->position() == StickyPosition;
+        if (newStyleIsSticky != oldStyleIsSticky) {
+            if (newStyleIsSticky)
+                frameView->addFixedObject(this);
+            else
+                frameView->removeFixedObject(this);
+        }
+    }
+
+    RenderBoxModelObject::styleWillChange(diff, newStyle);
+}
+
 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 {
     RenderBoxModelObject::styleDidChange(diff, oldStyle);
@@ -750,6 +767,8 @@ const char* RenderInline::renderName() const
 {
     if (isRelPositioned())
         return "RenderInline (relative positioned)";
+    if (isStickyPositioned())
+        return "RenderInline (sticky positioned)";
     if (isAnonymous())
         return "RenderInline (generated)";
     if (isRunIn())
@@ -765,7 +784,7 @@ bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& res
 
 VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point)
 {
-    // FIXME: Does not deal with relative positioned inlines (should it?)
+    // FIXME: Does not deal with relative or sticky positioned inlines (should it?)
     RenderBlock* cb = containingBlock();
     if (firstLineBox()) {
         // This inline actually has a line box.  We must have clicked in the border/padding of one of these boxes.  We
@@ -1064,7 +1083,7 @@ void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer,
     if (style()->hasInFlowPosition() && layer()) {
         // Apply the in-flow position offset when invalidating a rectangle. The layer
         // is translated, but the render box isn't, so we need to do this to get the
-        // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
+        // right dirty rect. Since this is called from RenderObject::setStyle, the relative or sticky position
         // flag on the RenderObject has been cleared, so use the one on the style().
         topLeft += layer()->offsetForInFlowPosition();
     }
index 9adec42..67a6429 100644 (file)
@@ -89,7 +89,8 @@ public:
 protected:
     virtual void willBeDestroyed();
 
-    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+    virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle) OVERRIDE;
+    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
 
 private:
     virtual RenderObjectChildList* virtualChildren() { return children(); }
@@ -140,6 +141,8 @@ private:
 
     virtual VisiblePosition positionForPoint(const LayoutPoint&);
 
+    virtual LayoutRect frameRectForStickyPositioning() const OVERRIDE { return linesBoundingBox(); }
+
     virtual IntRect borderBoundingBox() const
     {
         IntRect boundingBox = linesBoundingBox();
index 0350ac9..2d91fd8 100644 (file)
@@ -519,10 +519,10 @@ void RenderLayer::updateLayerPositionsAfterScroll(UpdateLayerPositionsAfterScrol
 
     updateLayerPosition();
 
-    if ((flags & HasSeenFixedPositionedAncestor) || renderer()->style()->position() == FixedPosition) {
+    if ((flags & HasSeenViewportConstrainedAncestor) || renderer()->style()->hasViewportConstrainedPosition()) {
         // FIXME: Is it worth passing the offsetFromRoot around like in updateLayerPositions?
         computeRepaintRects();
-        flags |= HasSeenFixedPositionedAncestor;
+        flags |= HasSeenViewportConstrainedAncestor;
     } else if ((flags & HasSeenAncestorWithOverflowClip) && !m_canSkipRepaintRectsUpdateOnScroll) {
         // If we have seen an overflow clip, we should update our repaint rects as clippedOverflowRectForRepaint
         // intersects it with our ancestor overflow clip that may have moved.
@@ -933,7 +933,7 @@ RenderLayer* RenderLayer::stackingContext() const
 static inline bool isPositionedContainer(RenderLayer* layer)
 {
     RenderBoxModelObject* layerRenderer = layer->renderer();
-    return layer->isRootLayer() || layerRenderer->isOutOfFlowPositioned() || layerRenderer->isInFlowPositioned() || layer->hasTransform();
+    return layer->isRootLayer() || layerRenderer->isPositioned() || layer->hasTransform();
 }
 
 static inline bool isFixedPositionedContainer(RenderLayer* layer)
@@ -3996,7 +3996,7 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, RenderRegion*
             if (renderer()->style()->hasBorderRadius())
                 newOverflowClip.setHasRadius(true);
             clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
-            if (renderer()->isOutOfFlowPositioned() || renderer()->isInFlowPositioned())
+            if (renderer()->isPositioned())
                 clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
         }
         if (renderer()->hasClip()) {
@@ -4801,8 +4801,7 @@ bool RenderLayer::shouldBeNormalFlowOnly() const
                 || renderer()->isEmbeddedObject()
                 || renderer()->isRenderIFrame()
                 || (renderer()->style()->specifiesColumns() && !isRootLayer()))
-            && !renderer()->isOutOfFlowPositioned()
-            && !renderer()->isInFlowPositioned()
+            && !renderer()->isPositioned()
             && !renderer()->hasTransform()
 #if ENABLE(CSS_FILTERS)
             && !renderer()->hasFilter()
index c96d057..bcdd801 100644 (file)
@@ -575,7 +575,7 @@ public:
 
     enum UpdateLayerPositionsAfterScrollFlag {
         NoFlag = 0,
-        HasSeenFixedPositionedAncestor = 1 << 0,
+        HasSeenViewportConstrainedAncestor = 1 << 0,
         HasSeenAncestorWithOverflowClip = 1 << 1
     };
 
index 933a559..84a7c08 100755 (executable)
@@ -1853,7 +1853,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS
             toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists();
 
         s_affectsParentBlock = isFloatingOrOutOfFlowPositioned()
-            && (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)
+            && (!newStyle->isFloating() && !newStyle->hasOutOfFlowPosition())
             && parent() && (parent()->isBlockFlow() || parent()->isRenderInline());
 
         // reset style flags
@@ -1861,6 +1861,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS
             setFloating(false);
             setPositioned(false);
             setRelPositioned(false);
+            setStickyPositioned(false);
         }
         setHorizontalWritingMode(true);
         setPaintBackground(false);
@@ -1966,9 +1967,9 @@ void RenderObject::propagateStyleToAnonymousChildren(bool blockChildrenOnly)
                 newStyle->setColumnSpan(ColumnSpanAll);
         }
 
-        // Preserve the position style of anonymous block continuations as they can have relative position when
-        // they contain block descendants of relative positioned inlines.
-        if (child->isRelPositioned() && toRenderBlock(child)->isAnonymousBlockContinuation())
+        // Preserve the position style of anonymous block continuations as they can have relative or sticky position when
+        // they contain block descendants of relative or sticky positioned inlines.
+        if (child->isInFlowPositioned() && toRenderBlock(child)->isAnonymousBlockContinuation())
             newStyle->setPosition(child->style()->position());
 
         child->setStyle(newStyle.release());
@@ -2898,10 +2899,10 @@ RenderBoxModelObject* RenderObject::offsetParent() const
     //       is one of the following HTML elements: td, th, or table.
     //     * Our own extension: if there is a difference in the effective zoom
 
-    bool skipTables = isOutOfFlowPositioned() || isRelPositioned();
+    bool skipTables = isPositioned();
     float currZoom = style()->effectiveZoom();
     RenderObject* curr = parent();
-    while (curr && (!curr->node() || (!curr->isOutOfFlowPositioned() && !curr->isRelPositioned() && !curr->isBody()))) {
+    while (curr && (!curr->node() || (!curr->isPositioned() && !curr->isBody()))) {
         Node* element = curr->node();
         if (!skipTables && element && (element->hasTagName(tableTag) || element->hasTagName(tdTag) || element->hasTagName(thTag)))
             break;
index 05ccea4..023d25e 100644 (file)
@@ -513,8 +513,10 @@ public:
     bool isFloating() const { return m_bitfields.floating(); }
 
     bool isOutOfFlowPositioned() const { return m_bitfields.positioned(); } // absolute or fixed positioning
-    bool isInFlowPositioned() const { return m_bitfields.relPositioned(); } // relative positioning
+    bool isInFlowPositioned() const { return m_bitfields.relPositioned() || m_bitfields.stickyPositioned(); } // relative or sticky positioning
     bool isRelPositioned() const { return m_bitfields.relPositioned(); } // relative positioning
+    bool isStickyPositioned() const { return m_bitfields.stickyPositioned(); }
+    bool isPositioned() const { return m_bitfields.positioned() || m_bitfields.relPositioned() || m_bitfields.stickyPositioned(); }
 
     bool isText() const  { return m_bitfields.isText(); }
     bool isBox() const { return m_bitfields.isBox(); }
@@ -621,6 +623,7 @@ public:
 
     void setPositioned(bool b = true)  { m_bitfields.setPositioned(b);  }
     void setRelPositioned(bool b = true) { m_bitfields.setRelPositioned(b); }
+    void setStickyPositioned(bool b = true) { m_bitfields.setStickyPositioned(b); }
     void setFloating(bool b = true) { m_bitfields.setFloating(b); }
     void setInline(bool b = true) { m_bitfields.setIsInline(b); }
     void setHasBoxDecorations(bool b = true) { m_bitfields.setPaintBackground(b); }
@@ -990,6 +993,7 @@ private:
             , m_floating(false)
             , m_positioned(false)
             , m_relPositioned(false)
+            , m_stickyPositioned(false)
             , m_paintBackground(false)
             , m_isAnonymous(node == node->document())
             , m_isText(false)
@@ -1024,6 +1028,7 @@ private:
 
         ADD_BOOLEAN_BITFIELD(positioned, Positioned);
         ADD_BOOLEAN_BITFIELD(relPositioned, RelPositioned);
+        ADD_BOOLEAN_BITFIELD(stickyPositioned, StickyPositioned);
         ADD_BOOLEAN_BITFIELD(paintBackground, PaintBackground); // if the box has something to paint in the
         // background painting phase (background, border, etc)
 
index 2378c7a..accfe2d 100644 (file)
@@ -511,6 +511,7 @@ public:
     EPosition position() const { return static_cast<EPosition>(noninherited_flags._position); }
     bool hasOutOfFlowPosition() const { return position() == AbsolutePosition || position() == FixedPosition; }
     bool hasInFlowPosition() const { return position() == RelativePosition || position() == StickyPosition; }
+    bool hasViewportConstrainedPosition() const { return position() == FixedPosition || position() == StickyPosition; }
     EFloat floating() const { return static_cast<EFloat>(noninherited_flags._floating); }
 
     Length width() const { return m_box->width(); }