| Index: third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp
|
| diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp
|
| index 9fbdbbdc0334a6846b9c92221a2d739b6697cc32..b099587c37d2ca64e487ac25bc423515b533255e 100644
|
| --- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp
|
| +++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp
|
| @@ -249,4 +249,559 @@ TEST_F(LayoutBoxModelObjectTest, StickyPositionAnonymousContainer) {
|
| IntRect(15, 165, 100, 100),
|
| enclosingIntRect(getScrollContainerRelativeStickyBoxRect(constraints)));
|
| }
|
| +
|
| +// Verifies that the correct "to containing block" sticky ancestor is found when
|
| +// computing the sticky constraints.
|
| +//
|
| +// In most cases, this pointer should be null since your parent() is normally
|
| +// your containingBlock. However there are a few cases where this is not true,
|
| +// most notably in tables where the root <table> is the containingBlock for many
|
| +// of the descendant elements.
|
| +TEST_F(LayoutBoxModelObjectTest,
|
| + StickyPositionFindsCorrectToContainingBlockStickyAncestor) {
|
| + setBodyInnerHTML(
|
| + "<style>#stickyOuterDiv { position: sticky; }"
|
| + "#stickyInnerDiv { position: sticky; }"
|
| + "#stickyThead { position: sticky; }"
|
| + "#stickyTr { position: sticky; }"
|
| + "#stickyTh { position: sticky; }</style>"
|
| + "<div id='stickyOuterDiv'><div id='stickyInnerDiv'></div><table>"
|
| + "<thead id='stickyThead'><tr id='stickyTr'><th id='stickyTh'></th></tr>"
|
| + "</thead></table></div>");
|
| +
|
| + // Force the constraints to be calculated.
|
| + LayoutBoxModelObject* stickyOuterDiv =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterDiv"));
|
| + stickyOuterDiv->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyInnerDiv =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyInnerDiv"));
|
| + stickyInnerDiv->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyThead =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
|
| + stickyThead->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTr =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
|
| + stickyTr->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTh =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
|
| + stickyTh->updateStickyPositionConstraints();
|
| +
|
| + // There's only one scrollableArea, the main page.
|
| + PaintLayerScrollableArea* scrollableArea =
|
| + stickyOuterDiv->layer()->ancestorOverflowLayer()->getScrollableArea();
|
| + ASSERT_TRUE(scrollableArea);
|
| + StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
|
| +
|
| + ASSERT_TRUE(constraintsMap.contains(stickyOuterDiv->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyInnerDiv->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyThead->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyTr->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyTh->layer()));
|
| +
|
| + // The outer <div> trivially has no ancestor.
|
| + EXPECT_FALSE(constraintsMap.get(stickyOuterDiv->layer())
|
| + .nearestStickyElementToContainingBlock());
|
| +
|
| + // Neither does the inner <div>, as the "to containing block" range is
|
| + // exclusive of the containing block.
|
| + EXPECT_FALSE(constraintsMap.get(stickyInnerDiv->layer())
|
| + .nearestStickyElementToContainingBlock());
|
| +
|
| + // All of the table elements containing block is the <table> itself, so the
|
| + // <tr> and <th> both have ancestors.
|
| + EXPECT_FALSE(constraintsMap.get(stickyThead->layer())
|
| + .nearestStickyElementToContainingBlock());
|
| + EXPECT_EQ(stickyThead, constraintsMap.get(stickyTr->layer())
|
| + .nearestStickyElementToContainingBlock());
|
| + EXPECT_EQ(stickyTr, constraintsMap.get(stickyTh->layer())
|
| + .nearestStickyElementToContainingBlock());
|
| +}
|
| +
|
| +// Verifies that the correct "to scroll container" sticky ancestor is found when
|
| +// computing the sticky constraints.
|
| +//
|
| +// The "to scroll container" ancestor is the nearest sticky ancestor between our
|
| +// containing block (inclusive) and our scroll container (exclusive).
|
| +TEST_F(LayoutBoxModelObjectTest,
|
| + StickyPositionFindsCorrectToScrollContainerStickyAncestor) {
|
| + // We make the scroller itself sticky in order to check that elements do not
|
| + // detect it as their sticky ancestor.
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { overflow-y: scroll; position: sticky; }"
|
| + "#stickyParent { position: sticky; }"
|
| + "#stickyChild { position: sticky; }"
|
| + "#stickyNestedChild { position: sticky; }</style>"
|
| + "<div id='scroller'><div id='stickyParent'><div id='stickyChild'></div>"
|
| + "<div><div id='stickyNestedChild'></div></div></div></div>");
|
| +
|
| + // Force the constraints to be calculated.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + scroller->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyParent =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
|
| + stickyParent->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
|
| + stickyChild->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyNestedChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyNestedChild"));
|
| + stickyNestedChild->updateStickyPositionConstraints();
|
| +
|
| + // There are two scrollable areas, but we only care about the scroller itself.
|
| + PaintLayerScrollableArea* scrollableArea =
|
| + scroller->layer()->getScrollableArea();
|
| + ASSERT_TRUE(scrollableArea);
|
| + StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
|
| +
|
| + ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyParent->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyChild->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyNestedChild->layer()));
|
| +
|
| + // The outer <div> should not count the scroller as its ancestor.
|
| + EXPECT_FALSE(constraintsMap.get(stickyParent->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| +
|
| + // Both inner children should count the parent <div> as their ancestor.
|
| + EXPECT_EQ(stickyParent,
|
| + constraintsMap.get(stickyChild->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| + EXPECT_EQ(stickyParent,
|
| + constraintsMap.get(stickyNestedChild->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| +}
|
| +
|
| +// Verifies that the correct "to scroll container" sticky ancestor is found when
|
| +// computing the sticky constraints in the case of tables.
|
| +//
|
| +// Tables are unusual because the containingBlock for all table elements is the
|
| +// <table> itself, so we have to skip over elements to find the "to scroll
|
| +// container" stick ancestor.
|
| +TEST_F(LayoutBoxModelObjectTest,
|
| + StickyPositionFindsCorrectToScrollContainerStickyAncestorTable) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { overflow-y: scroll; }"
|
| + "#stickyOuter { position: sticky; }"
|
| + "#stickyThead { position: sticky; }"
|
| + "#stickyTr { position: sticky; }"
|
| + "#stickyTh { position: sticky; }</style>"
|
| + "<div id='scroller'><div id='stickyOuter'><table><thead id='stickyThead'>"
|
| + "<tr id='stickyTr'><th id='stickyTh'></th></tr></thead></table></div>"
|
| + "</div>");
|
| +
|
| + // Force the constraints to be calculated.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + scroller->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyOuter =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuter"));
|
| + stickyOuter->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyThead =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
|
| + stickyThead->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTr =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
|
| + stickyTr->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTh =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
|
| + stickyTh->updateStickyPositionConstraints();
|
| +
|
| + // There are two scrollable areas, but we only care about the scroller itself.
|
| + PaintLayerScrollableArea* scrollableArea =
|
| + scroller->layer()->getScrollableArea();
|
| + ASSERT_TRUE(scrollableArea);
|
| + StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
|
| +
|
| + ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyOuter->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyThead->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyTr->layer()));
|
| + ASSERT_TRUE(constraintsMap.contains(stickyTh->layer()));
|
| +
|
| + // All of the table elements should point to the outer <div> as their
|
| + // "to scroll container" sticky ancestor.
|
| + EXPECT_EQ(stickyOuter,
|
| + constraintsMap.get(stickyThead->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| + EXPECT_EQ(stickyOuter,
|
| + constraintsMap.get(stickyTr->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| + EXPECT_EQ(stickyOuter,
|
| + constraintsMap.get(stickyTh->layer())
|
| + .nearestStickyElementFromContainingBlockToScrollContainer());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct when we have
|
| +// a simple case of nested sticky elements.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionNested) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px }"
|
| + "#stickyParent { position: sticky; top: 0; height: 50px; }"
|
| + "#stickyChild { position: sticky; top: 0; height: 25px; }"
|
| + "#postPadding { height: 200px }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
|
| + "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* stickyParent =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
|
| + stickyParent->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
|
| + stickyChild->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 100));
|
| + ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // Both the parent and child sticky divs are attempting to place themselves at
|
| + // the top of the scrollable area. To achieve this the parent must offset on
|
| + // the y-axis against its starting position. The child is offset relative to
|
| + // its parent so should not move at all.
|
| + EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct when the
|
| +// child has a larger edge constraint value than the parent.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionChildHasLargerTop) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px }"
|
| + "#stickyParent { position: sticky; top: 0; height: 50px; }"
|
| + "#stickyChild { position: sticky; top: 25px; height: 25px; }"
|
| + "#postPadding { height: 200px }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
|
| + "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* stickyParent =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
|
| + stickyParent->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
|
| + stickyChild->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 100));
|
| + ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // The parent is attempting to place itself at the top of the scrollable area,
|
| + // whilst the child is attempting to be 25 pixels from the top. To achieve
|
| + // this both must offset on the y-axis against their starting positions, but
|
| + // note the child is offset relative to the parent.
|
| + EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct when the
|
| +// child has a smaller edge constraint value than the parent.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionParentHasLargerTop) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px }"
|
| + "#stickyParent { position: sticky; top: 25px; height: 50px; }"
|
| + "#stickyChild { position: sticky; top: 0; height: 25px; }"
|
| + "#postPadding { height: 200px }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
|
| + "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* stickyParent =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
|
| + stickyParent->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
|
| + stickyChild->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 100));
|
| + ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // The parent is attempting to place itself 25 pixels from the top of the
|
| + // scrollable area, whilst the child is attempting to be at the top. However,
|
| + // the child must stay contained within the parent, so it should be pushed
|
| + // down to the same height. As always, the child offset is relative.
|
| + EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct when the
|
| +// child has a large enough edge constraint value to push outside of its parent.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionChildPushingOutsideParent) {
|
| + setBodyInnerHTML(
|
| + "<style> #scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px; }"
|
| + "#stickyParent { position: sticky; top: 0; height: 50px; }"
|
| + "#stickyChild { position: sticky; top: 50px; height: 25px; }"
|
| + "#postPadding { height: 200px }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
|
| + "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* stickyParent =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
|
| + stickyParent->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyChild =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
|
| + stickyChild->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 100));
|
| + ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // The parent is attempting to place itself at the top of the scrollable area,
|
| + // whilst the child is attempting to be 50 pixels from the top. However, there
|
| + // is only 25 pixels of space for the child to move into, so it should be
|
| + // capped by that offset. As always, the child offset is relative.
|
| + EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct in the case
|
| +// of triple nesting. Triple (or more) nesting must be tested as the grandchild
|
| +// sticky must be corrected for both the containing block and the viewport.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionTripleNestedDiv) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 200px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px; }"
|
| + "#outmostSticky { position: sticky; top: 0; height: 100px; }"
|
| + "#middleSticky { position: sticky; top: 0; height: 75px; }"
|
| + "#innerSticky { position: sticky; top: 25px; height: 25px; }"
|
| + "#postPadding { height: 400px }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='outmostSticky'>"
|
| + "<div id='middleSticky'><div id='innerSticky'></div></div></div>"
|
| + "<div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* outmostSticky =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("outmostSticky"));
|
| + outmostSticky->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* middleSticky =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("middleSticky"));
|
| + middleSticky->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* innerSticky =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("innerSticky"));
|
| + innerSticky->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 100));
|
| + ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // The grandparent and parent divs are attempting to place themselves at the
|
| + // top of the scrollable area. The child div is attempting to place itself at
|
| + // an offset of 25 pixels to the top of the scrollable area. The result of
|
| + // this sticky offset calculation is quite simple, but internally the child
|
| + // offset has to offset both against its containing block and against its
|
| + // viewport ancestor (the grandparent).
|
| + EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), middleSticky->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct in the case
|
| +// of tables. Tables are special as the containing block for all child elements
|
| +// is always the root level <table>.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedStickyInTable) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px; }"
|
| + "#stickyDiv { position: sticky; top: 0; height: 200px; }"
|
| + "#table { border-collapse: collapse; }"
|
| + "#stickyThead { position: sticky; top: 0; }"
|
| + "#stickyTr { position: sticky; top: 0; }"
|
| + "#stickyTh { position: sticky; top: 0; }"
|
| + "#postPadding { height: 200px; }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div><div id='stickyDiv'>"
|
| + "<table id='table'><thead id='stickyThead'><tr id='stickyTr'>"
|
| + "<th id='stickyTh'>Header</th></tr></thread><tbody><tr><td>0</td></tr>"
|
| + "<tr><td>1</td></tr><tr><td>2</td></tr><tr><td>3</td></tr></tbody>"
|
| + "</table></div><div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* stickyDiv =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyDiv"));
|
| + stickyDiv->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyThead =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
|
| + stickyThead->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTr =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
|
| + stickyTr->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* stickyTh =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
|
| + stickyTh->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 150));
|
| + ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // All sticky elements are attempting to stick to the top of the scrollable
|
| + // area. For the root sticky div, this requires an offset. All the other
|
| + // descendant sticky elements are positioned relatively so don't need offset.
|
| + EXPECT_EQ(LayoutSize(0, 100), stickyDiv->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyThead->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
|
| +
|
| + // If we now scroll to the point where the overall sticky div starts to move,
|
| + // the table headers should continue to stick to the top of the scrollable
|
| + // area until they run out of <table> space to move in.
|
| +
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 275));
|
| + ASSERT_EQ(275.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // TODO(smcgruer): I'm not sure that 12 is correct. My hankerchief math gets
|
| + // me something like (275 - 50 - 200) = 25? But it renders correctly.
|
| + EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 12), stickyThead->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
|
| +
|
| + // Finally, if we scroll so that the table is off the top of the page, the
|
| + // sticky header should go off the top with it.
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 350));
|
| + ASSERT_EQ(350.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // TODO(smcgruer): Again I have no idea why this is correct. But it does
|
| + // render correctly.
|
| + EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 12), stickyThead->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
|
| +}
|
| +
|
| +// Verifies that the calculated position:sticky offsets are correct in the case
|
| +// where a particular position:sticky element is used both as a "to containing
|
| +// block" ancestor as well as a "to scroll container" ancestor.
|
| +//
|
| +// This is a rare case that can be replicated by nesting tables so that a sticky
|
| +// cell contains another table that has sticky elements. See the HTML below.
|
| +TEST_F(LayoutBoxModelObjectTest, StickyPositionComplexTableNesting) {
|
| + setBodyInnerHTML(
|
| + "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
|
| + "#prePadding { height: 50px; }"
|
| + "#outerStickyThead { position: sticky; top: 0; }"
|
| + "#outerStickyTr { position: sticky; top: 0; }"
|
| + "#outerStickyTh { position: sticky; top: 0; }"
|
| + "#innerStickyThead { position: sticky; top: 25px; }"
|
| + "#innerStickyTr { position: sticky; top: 25px; }"
|
| + "#innerStickyTh { position: sticky; top: 25px; }"
|
| + "#postPadding { height: 200px; }"
|
| + "table { border-collapse: collapse; }</style>"
|
| + "<div id='scroller'><div id='prePadding'></div>"
|
| + "<table><thead id='outerStickyThead'><tr id='outerStickyTr'>"
|
| + "<th id='outerStickyTh'><table><thead id='innerStickyThead'>"
|
| + "<tr id='innerStickyTr'><th id='innerStickyTh'>Nested</th></tr></thead>"
|
| + "</table></th></tr></thread><tbody><tr><td>0</td></tr><tr><td>1</td></tr>"
|
| + "<tr><td>2</td></tr><tr><td>3</td></tr></tbody></table>"
|
| + "<div id='postPadding'></div></div>");
|
| +
|
| + // Make sure that the constraints are cached before scrolling or they may be
|
| + // calculated post-scroll and thus not require offset correction.
|
| +
|
| + LayoutBoxModelObject* outerStickyThead =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyThead"));
|
| + outerStickyThead->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* outerStickyTr =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyTr"));
|
| + outerStickyTr->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* outerStickyTh =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyTh"));
|
| + outerStickyTh->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* innerStickyThead =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyThead"));
|
| + innerStickyThead->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* innerStickyTr =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyTr"));
|
| + innerStickyTr->updateStickyPositionConstraints();
|
| +
|
| + LayoutBoxModelObject* innerStickyTh =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyTh"));
|
| + innerStickyTh->updateStickyPositionConstraints();
|
| +
|
| + // Scroll the page down.
|
| + LayoutBoxModelObject* scroller =
|
| + toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
|
| + PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
|
| + scrollableArea->scrollToAbsolutePosition(
|
| + FloatPoint(scrollableArea->scrollPosition().x(), 150));
|
| + ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
|
| +
|
| + // TODO(smcgruer): I have no idea again where these numbers come from, but it
|
| + // renders correctly!
|
| + EXPECT_EQ(LayoutSize(0, 12), outerStickyThead->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), outerStickyTr->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), outerStickyTh->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), innerStickyThead->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), innerStickyTr->stickyPositionOffset());
|
| + EXPECT_EQ(LayoutSize(0, 0), innerStickyTh->stickyPositionOffset());
|
| +
|
| + // TODO(smcgruer): More scrolling.
|
| +}
|
| +
|
| } // namespace blink
|
|
|