Chromium Code Reviews| Index: third_party/WebKit/Source/core/dom/ElementTest.cpp |
| diff --git a/third_party/WebKit/Source/core/dom/ElementTest.cpp b/third_party/WebKit/Source/core/dom/ElementTest.cpp |
| index 079936b6cbbc31ea3a4823a28bcda2c3799e11ca..5650c54996a5a7968edebdb4e1527685f8667d71 100644 |
| --- a/third_party/WebKit/Source/core/dom/ElementTest.cpp |
| +++ b/third_party/WebKit/Source/core/dom/ElementTest.cpp |
| @@ -4,18 +4,23 @@ |
| #include "core/dom/Element.h" |
| +#include <memory> |
| +#include "core/dom/ClientRect.h" |
| #include "core/dom/Document.h" |
| +#include "core/editing/EditingTestBase.h" |
| #include "core/frame/FrameView.h" |
| #include "core/html/HTMLHtmlElement.h" |
| +#include "core/layout/LayoutBoxModelObject.h" |
| +#include "core/paint/PaintLayer.h" |
| #include "core/testing/DummyPageHolder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| -#include <memory> |
| namespace blink { |
| -TEST(ElementTest, SupportsFocus) { |
| - std::unique_ptr<DummyPageHolder> page_holder = DummyPageHolder::Create(); |
| - Document& document = page_holder->GetDocument(); |
| +class ElementTest : public EditingTestBase {}; |
| + |
| +TEST_F(ElementTest, SupportsFocus) { |
| + Document& document = GetDocument(); |
| DCHECK(isHTMLHtmlElement(document.documentElement())); |
| document.setDesignMode("on"); |
| document.View()->UpdateAllLifecyclePhases(); |
| @@ -23,4 +28,201 @@ TEST(ElementTest, SupportsFocus) { |
| << "<html> with designMode=on should be focusable."; |
| } |
| +TEST_F(ElementTest, |
| + GetBoundingClientRectCorrectForStickyElementsAfterInsertion) { |
| + Document& document = GetDocument(); |
| + SetBodyContent( |
| + "<style>body { margin: 0 }" |
| + "#scroller { overflow: scroll; height: 100px; width: 100px; }" |
| + "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }" |
| + "#padding { height: 500px; width: 300px; }</style>" |
| + "<div id='scroller'><div id='writer'></div><div id='sticky'></div>" |
| + "<div id='padding'></div></div>"); |
| + |
| + Element* scroller = document.getElementById("scroller"); |
| + Element* writer = document.getElementById("writer"); |
| + Element* sticky = document.getElementById("sticky"); |
| + |
| + ASSERT_TRUE(scroller); |
| + ASSERT_TRUE(writer); |
| + ASSERT_TRUE(sticky); |
| + |
| + scroller->scrollTo(50.0, 200.0); |
| + |
| + // The sticky element should remain at (0, 25) relative to the viewport due to |
| + // the constraints. |
| + ClientRect* bounding_client_rect = sticky->getBoundingClientRect(); |
| + EXPECT_EQ(0, bounding_client_rect->top()); |
| + EXPECT_EQ(25, bounding_client_rect->left()); |
| + |
| + // Insert a new <div> above the sticky. This will dirty layout and invalidate |
| + // the sticky constraints. |
| + writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>"); |
| + EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending, |
| + document.Lifecycle().GetState()); |
| + |
| + // Requesting the bounding client rect should cause both layout and |
| + // compositing inputs clean to be run, and the sticky result shouldn't change. |
| + bounding_client_rect = sticky->getBoundingClientRect(); |
| + EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, |
| + document.Lifecycle().GetState()); |
| + EXPECT_FALSE(sticky->GetLayoutBoxModelObject() |
| + ->Layer() |
| + ->NeedsCompositingInputsUpdate()); |
| + EXPECT_EQ(0, bounding_client_rect->top()); |
| + EXPECT_EQ(25, bounding_client_rect->left()); |
| +} |
| + |
| +TEST_F(ElementTest, OffsetTopAndLeftCorrectForStickyElementsAfterInsertion) { |
| + Document& document = GetDocument(); |
| + SetBodyContent( |
| + "<style>body { margin: 0 }" |
| + "#scroller { overflow: scroll; height: 100px; width: 100px; }" |
| + "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }" |
| + "#padding { height: 500px; width: 300px; }</style>" |
| + "<div id='scroller'><div id='writer'></div><div id='sticky'></div>" |
| + "<div id='padding'></div></div>"); |
| + |
| + Element* scroller = document.getElementById("scroller"); |
| + Element* writer = document.getElementById("writer"); |
| + Element* sticky = document.getElementById("sticky"); |
| + |
| + ASSERT_TRUE(scroller); |
| + ASSERT_TRUE(writer); |
| + ASSERT_TRUE(sticky); |
| + |
| + scroller->scrollTo(50.0, 200.0); |
| + |
| + // The sticky element should be offset to stay at (0, 25) relative to the |
| + // viewport due to the constraints. |
| + EXPECT_EQ(scroller->scrollTop(), sticky->OffsetTop()); |
| + EXPECT_EQ(scroller->scrollLeft() + 25, sticky->OffsetLeft()); |
| + |
| + // Insert a new <div> above the sticky. This will dirty layout and invalidate |
| + // the sticky constraints. |
| + writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>"); |
| + EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending, |
| + document.Lifecycle().GetState()); |
| + |
| + // Requesting either offset should cause both layout and compositing inputs |
| + // clean to be run, and the sticky result shouldn't change. |
| + EXPECT_EQ(scroller->scrollTop(), sticky->OffsetTop()); |
| + EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, |
| + document.Lifecycle().GetState()); |
| + EXPECT_FALSE(sticky->GetLayoutBoxModelObject() |
| + ->Layer() |
| + ->NeedsCompositingInputsUpdate()); |
| + |
| + // Dirty layout again, since |OffsetTop| will have cleaned it. |
| + writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>"); |
| + EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending, |
| + document.Lifecycle().GetState()); |
| + |
| + // Again requesting an offset should cause layout and compositing to be clean. |
| + EXPECT_EQ(scroller->scrollLeft() + 25, sticky->OffsetLeft()); |
| + EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, |
| + document.Lifecycle().GetState()); |
| + EXPECT_FALSE(sticky->GetLayoutBoxModelObject() |
| + ->Layer() |
| + ->NeedsCompositingInputsUpdate()); |
| +} |
| + |
| +TEST_F(ElementTest, BoundsInViewportCorrectForStickyElementsAfterInsertion) { |
| + Document& document = GetDocument(); |
| + SetBodyContent( |
| + "<style>body { margin: 0 }" |
| + "#scroller { overflow: scroll; height: 100px; width: 100px; }" |
| + "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }" |
| + "#padding { height: 500px; width: 300px; }</style>" |
| + "<div id='scroller'><div id='writer'></div><div id='sticky'></div>" |
| + "<div id='padding'></div></div>"); |
| + |
| + Element* scroller = document.getElementById("scroller"); |
| + Element* writer = document.getElementById("writer"); |
| + Element* sticky = document.getElementById("sticky"); |
| + |
| + ASSERT_TRUE(scroller); |
| + ASSERT_TRUE(writer); |
| + ASSERT_TRUE(sticky); |
| + |
| + scroller->scrollTo(50.0, 200.0); |
| + |
| + // The sticky element should remain at (0, 25) relative to the viewport due to |
| + // the constraints. |
| + IntRect bounds_in_viewport = sticky->BoundsInViewport(); |
| + EXPECT_EQ(0, bounds_in_viewport.Y()); |
| + EXPECT_EQ(25, bounds_in_viewport.X()); |
| + |
| + // Insert a new <div> above the sticky. This will dirty layout and invalidate |
| + // the sticky constraints. |
| + writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>"); |
| + EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending, |
| + document.Lifecycle().GetState()); |
| + |
| + // Requesting the bounds in viewport should cause both layout and compositing |
| + // inputs clean to be run, and the sticky result shouldn't change. |
| + bounds_in_viewport = sticky->BoundsInViewport(); |
| + EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, |
| + document.Lifecycle().GetState()); |
| + EXPECT_FALSE(sticky->GetLayoutBoxModelObject() |
| + ->Layer() |
| + ->NeedsCompositingInputsUpdate()); |
| + EXPECT_EQ(0, bounds_in_viewport.Y()); |
| + EXPECT_EQ(25, bounds_in_viewport.X()); |
| +} |
| + |
| +TEST_F(ElementTest, StickySubtreesAreTrackedCorrectly) { |
| + Document& document = GetDocument(); |
| + SetBodyContent( |
| + "<div id='ancestor'>" |
| + " <div id='sticky' style='position:sticky;'>" |
| + " <div id='stickyChild'>" |
| + " <div id='stickyGrandchild'></div>" |
| + " </div" |
| + " </div>" |
| + "</div>"); |
| + |
| + LayoutObject* ancestor = |
| + document.getElementById("ancestor")->GetLayoutObject(); |
| + LayoutObject* sticky = document.getElementById("sticky")->GetLayoutObject(); |
| + LayoutObject* sticky_child = |
| + document.getElementById("stickyChild")->GetLayoutObject(); |
| + LayoutObject* sticky_grandchild = |
| + document.getElementById("stickyGrandchild")->GetLayoutObject(); |
| + |
| + EXPECT_FALSE(ancestor->StyleRef().IsInStickySubtree()); |
| + EXPECT_TRUE(sticky->StyleRef().IsInStickySubtree()); |
| + EXPECT_TRUE(sticky_child->StyleRef().IsInStickySubtree()); |
| + EXPECT_TRUE(sticky_grandchild->StyleRef().IsInStickySubtree()); |
| + |
| + // This forces stickyChild to fork it's StyleRareInheritedData, so that we can |
| + // ensure that the sticky subtree update behavior survives forking. |
| + document.getElementById("stickyChild") |
| + ->SetInlineStyleProperty(CSSPropertyWebkitRubyPosition, CSSValueAfter); |
| + document.View()->UpdateAllLifecyclePhases(); |
| + EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState()); |
| + |
| + EXPECT_EQ(kRubyPositionBefore, sticky->StyleRef().GetRubyPosition()); |
| + EXPECT_EQ(kRubyPositionAfter, sticky_child->StyleRef().GetRubyPosition()); |
| + EXPECT_EQ(kRubyPositionAfter, |
| + sticky_grandchild->StyleRef().GetRubyPosition()); |
| + |
| + // Setting a -webkit-ruby value should not have affected the sticky subtree |
| + // status. |
| + EXPECT_TRUE(sticky->StyleRef().IsInStickySubtree()); |
| + EXPECT_TRUE(sticky_child->StyleRef().IsInStickySubtree()); |
| + EXPECT_TRUE(sticky_grandchild->StyleRef().IsInStickySubtree()); |
| + |
| + // Now switch the parent back to being non-sticky - all descendants should be |
|
flackr
2017/05/04 14:40:59
What if there is an intermediate sticky element? i
smcgruer
2017/05/04 15:25:32
Done.
|
| + // updated. |
| + document.getElementById("sticky")->SetInlineStyleProperty(CSSPropertyPosition, |
| + CSSValueStatic); |
| + document.View()->UpdateAllLifecyclePhases(); |
| + EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState()); |
| + |
| + EXPECT_FALSE(sticky->StyleRef().IsInStickySubtree()); |
| + EXPECT_FALSE(sticky_child->StyleRef().IsInStickySubtree()); |
| + EXPECT_FALSE(sticky_grandchild->StyleRef().IsInStickySubtree()); |
| +} |
| + |
| } // namespace blink |