Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Unified Diff: third_party/WebKit/Source/core/dom/ElementTest.cpp

Issue 2825343003: Clean compositing inputs for location APIs for sticky-affected elements. (Closed)
Patch Set: Rebase Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/core/dom/Element.cpp ('k') | third_party/WebKit/Source/core/html/HTMLElement.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..afffcd8eb1a20b775cf6b275f81c09d22d0854db 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,215 @@ 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='outerSticky' style='position:sticky;'>"
+ " <div id='child'>"
+ " <div id='grandchild'></div>"
+ " <div id='innerSticky' style='position:sticky;'>"
+ " <div id='greatGrandchild'></div>"
+ " </div>"
+ " </div"
+ " </div>"
+ "</div>");
+
+ LayoutObject* ancestor =
+ document.getElementById("ancestor")->GetLayoutObject();
+ LayoutObject* outer_sticky =
+ document.getElementById("outerSticky")->GetLayoutObject();
+ LayoutObject* child = document.getElementById("child")->GetLayoutObject();
+ LayoutObject* grandchild =
+ document.getElementById("grandchild")->GetLayoutObject();
+ LayoutObject* inner_sticky =
+ document.getElementById("innerSticky")->GetLayoutObject();
+ LayoutObject* great_grandchild =
+ document.getElementById("greatGrandchild")->GetLayoutObject();
+
+ EXPECT_FALSE(ancestor->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(outer_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(child->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(grandchild->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
+
+ // This forces 'child' to fork it's StyleRareInheritedData, so that we can
+ // ensure that the sticky subtree update behavior survives forking.
+ document.getElementById("child")->SetInlineStyleProperty(
+ CSSPropertyWebkitRubyPosition, CSSValueAfter);
+ document.View()->UpdateAllLifecyclePhases();
+ EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState());
+
+ EXPECT_EQ(kRubyPositionBefore, outer_sticky->StyleRef().GetRubyPosition());
+ EXPECT_EQ(kRubyPositionAfter, child->StyleRef().GetRubyPosition());
+ EXPECT_EQ(kRubyPositionAfter, grandchild->StyleRef().GetRubyPosition());
+ EXPECT_EQ(kRubyPositionAfter, inner_sticky->StyleRef().GetRubyPosition());
+ EXPECT_EQ(kRubyPositionAfter, great_grandchild->StyleRef().GetRubyPosition());
+
+ // Setting -webkit-ruby value shouldn't have affected the sticky subtree bit.
+ EXPECT_TRUE(outer_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(child->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(grandchild->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
+
+ // Now switch 'outerSticky' back to being non-sticky - all descendents between
+ // it and the 'innerSticky' should be updated, and the 'innerSticky' should
+ // fork it's StyleRareInheritedData to maintain the sticky subtree bit.
+ document.getElementById("outerSticky")
+ ->SetInlineStyleProperty(CSSPropertyPosition, CSSValueStatic);
+ document.View()->UpdateAllLifecyclePhases();
+ EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState());
+
+ EXPECT_FALSE(outer_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_FALSE(child->StyleRef().SubtreeIsSticky());
+ EXPECT_FALSE(grandchild->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
+ EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
+}
+
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/core/dom/Element.cpp ('k') | third_party/WebKit/Source/core/html/HTMLElement.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698