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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/dom/Element.h" 5 #include "core/dom/Element.h"
6 6
7 #include <memory>
8 #include "core/dom/ClientRect.h"
7 #include "core/dom/Document.h" 9 #include "core/dom/Document.h"
10 #include "core/editing/EditingTestBase.h"
8 #include "core/frame/FrameView.h" 11 #include "core/frame/FrameView.h"
9 #include "core/html/HTMLHtmlElement.h" 12 #include "core/html/HTMLHtmlElement.h"
13 #include "core/layout/LayoutBoxModelObject.h"
14 #include "core/paint/PaintLayer.h"
10 #include "core/testing/DummyPageHolder.h" 15 #include "core/testing/DummyPageHolder.h"
11 #include "testing/gtest/include/gtest/gtest.h" 16 #include "testing/gtest/include/gtest/gtest.h"
12 #include <memory>
13 17
14 namespace blink { 18 namespace blink {
15 19
16 TEST(ElementTest, SupportsFocus) { 20 class ElementTest : public EditingTestBase {};
17 std::unique_ptr<DummyPageHolder> page_holder = DummyPageHolder::Create(); 21
18 Document& document = page_holder->GetDocument(); 22 TEST_F(ElementTest, SupportsFocus) {
23 Document& document = GetDocument();
19 DCHECK(isHTMLHtmlElement(document.documentElement())); 24 DCHECK(isHTMLHtmlElement(document.documentElement()));
20 document.setDesignMode("on"); 25 document.setDesignMode("on");
21 document.View()->UpdateAllLifecyclePhases(); 26 document.View()->UpdateAllLifecyclePhases();
22 EXPECT_TRUE(document.documentElement()->SupportsFocus()) 27 EXPECT_TRUE(document.documentElement()->SupportsFocus())
23 << "<html> with designMode=on should be focusable."; 28 << "<html> with designMode=on should be focusable.";
24 } 29 }
25 30
31 TEST_F(ElementTest,
32 GetBoundingClientRectCorrectForStickyElementsAfterInsertion) {
33 Document& document = GetDocument();
34 SetBodyContent(
35 "<style>body { margin: 0 }"
36 "#scroller { overflow: scroll; height: 100px; width: 100px; }"
37 "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }"
38 "#padding { height: 500px; width: 300px; }</style>"
39 "<div id='scroller'><div id='writer'></div><div id='sticky'></div>"
40 "<div id='padding'></div></div>");
41
42 Element* scroller = document.getElementById("scroller");
43 Element* writer = document.getElementById("writer");
44 Element* sticky = document.getElementById("sticky");
45
46 ASSERT_TRUE(scroller);
47 ASSERT_TRUE(writer);
48 ASSERT_TRUE(sticky);
49
50 scroller->scrollTo(50.0, 200.0);
51
52 // The sticky element should remain at (0, 25) relative to the viewport due to
53 // the constraints.
54 ClientRect* bounding_client_rect = sticky->getBoundingClientRect();
55 EXPECT_EQ(0, bounding_client_rect->top());
56 EXPECT_EQ(25, bounding_client_rect->left());
57
58 // Insert a new <div> above the sticky. This will dirty layout and invalidate
59 // the sticky constraints.
60 writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>");
61 EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending,
62 document.Lifecycle().GetState());
63
64 // Requesting the bounding client rect should cause both layout and
65 // compositing inputs clean to be run, and the sticky result shouldn't change.
66 bounding_client_rect = sticky->getBoundingClientRect();
67 EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean,
68 document.Lifecycle().GetState());
69 EXPECT_FALSE(sticky->GetLayoutBoxModelObject()
70 ->Layer()
71 ->NeedsCompositingInputsUpdate());
72 EXPECT_EQ(0, bounding_client_rect->top());
73 EXPECT_EQ(25, bounding_client_rect->left());
74 }
75
76 TEST_F(ElementTest, OffsetTopAndLeftCorrectForStickyElementsAfterInsertion) {
77 Document& document = GetDocument();
78 SetBodyContent(
79 "<style>body { margin: 0 }"
80 "#scroller { overflow: scroll; height: 100px; width: 100px; }"
81 "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }"
82 "#padding { height: 500px; width: 300px; }</style>"
83 "<div id='scroller'><div id='writer'></div><div id='sticky'></div>"
84 "<div id='padding'></div></div>");
85
86 Element* scroller = document.getElementById("scroller");
87 Element* writer = document.getElementById("writer");
88 Element* sticky = document.getElementById("sticky");
89
90 ASSERT_TRUE(scroller);
91 ASSERT_TRUE(writer);
92 ASSERT_TRUE(sticky);
93
94 scroller->scrollTo(50.0, 200.0);
95
96 // The sticky element should be offset to stay at (0, 25) relative to the
97 // viewport due to the constraints.
98 EXPECT_EQ(scroller->scrollTop(), sticky->OffsetTop());
99 EXPECT_EQ(scroller->scrollLeft() + 25, sticky->OffsetLeft());
100
101 // Insert a new <div> above the sticky. This will dirty layout and invalidate
102 // the sticky constraints.
103 writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>");
104 EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending,
105 document.Lifecycle().GetState());
106
107 // Requesting either offset should cause both layout and compositing inputs
108 // clean to be run, and the sticky result shouldn't change.
109 EXPECT_EQ(scroller->scrollTop(), sticky->OffsetTop());
110 EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean,
111 document.Lifecycle().GetState());
112 EXPECT_FALSE(sticky->GetLayoutBoxModelObject()
113 ->Layer()
114 ->NeedsCompositingInputsUpdate());
115
116 // Dirty layout again, since |OffsetTop| will have cleaned it.
117 writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>");
118 EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending,
119 document.Lifecycle().GetState());
120
121 // Again requesting an offset should cause layout and compositing to be clean.
122 EXPECT_EQ(scroller->scrollLeft() + 25, sticky->OffsetLeft());
123 EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean,
124 document.Lifecycle().GetState());
125 EXPECT_FALSE(sticky->GetLayoutBoxModelObject()
126 ->Layer()
127 ->NeedsCompositingInputsUpdate());
128 }
129
130 TEST_F(ElementTest, BoundsInViewportCorrectForStickyElementsAfterInsertion) {
131 Document& document = GetDocument();
132 SetBodyContent(
133 "<style>body { margin: 0 }"
134 "#scroller { overflow: scroll; height: 100px; width: 100px; }"
135 "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }"
136 "#padding { height: 500px; width: 300px; }</style>"
137 "<div id='scroller'><div id='writer'></div><div id='sticky'></div>"
138 "<div id='padding'></div></div>");
139
140 Element* scroller = document.getElementById("scroller");
141 Element* writer = document.getElementById("writer");
142 Element* sticky = document.getElementById("sticky");
143
144 ASSERT_TRUE(scroller);
145 ASSERT_TRUE(writer);
146 ASSERT_TRUE(sticky);
147
148 scroller->scrollTo(50.0, 200.0);
149
150 // The sticky element should remain at (0, 25) relative to the viewport due to
151 // the constraints.
152 IntRect bounds_in_viewport = sticky->BoundsInViewport();
153 EXPECT_EQ(0, bounds_in_viewport.Y());
154 EXPECT_EQ(25, bounds_in_viewport.X());
155
156 // Insert a new <div> above the sticky. This will dirty layout and invalidate
157 // the sticky constraints.
158 writer->setInnerHTML("<div style='height: 100px; width: 700px;'></div>");
159 EXPECT_EQ(DocumentLifecycle::kVisualUpdatePending,
160 document.Lifecycle().GetState());
161
162 // Requesting the bounds in viewport should cause both layout and compositing
163 // inputs clean to be run, and the sticky result shouldn't change.
164 bounds_in_viewport = sticky->BoundsInViewport();
165 EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean,
166 document.Lifecycle().GetState());
167 EXPECT_FALSE(sticky->GetLayoutBoxModelObject()
168 ->Layer()
169 ->NeedsCompositingInputsUpdate());
170 EXPECT_EQ(0, bounds_in_viewport.Y());
171 EXPECT_EQ(25, bounds_in_viewport.X());
172 }
173
174 TEST_F(ElementTest, StickySubtreesAreTrackedCorrectly) {
175 Document& document = GetDocument();
176 SetBodyContent(
177 "<div id='ancestor'>"
178 " <div id='outerSticky' style='position:sticky;'>"
179 " <div id='child'>"
180 " <div id='grandchild'></div>"
181 " <div id='innerSticky' style='position:sticky;'>"
182 " <div id='greatGrandchild'></div>"
183 " </div>"
184 " </div"
185 " </div>"
186 "</div>");
187
188 LayoutObject* ancestor =
189 document.getElementById("ancestor")->GetLayoutObject();
190 LayoutObject* outer_sticky =
191 document.getElementById("outerSticky")->GetLayoutObject();
192 LayoutObject* child = document.getElementById("child")->GetLayoutObject();
193 LayoutObject* grandchild =
194 document.getElementById("grandchild")->GetLayoutObject();
195 LayoutObject* inner_sticky =
196 document.getElementById("innerSticky")->GetLayoutObject();
197 LayoutObject* great_grandchild =
198 document.getElementById("greatGrandchild")->GetLayoutObject();
199
200 EXPECT_FALSE(ancestor->StyleRef().SubtreeIsSticky());
201 EXPECT_TRUE(outer_sticky->StyleRef().SubtreeIsSticky());
202 EXPECT_TRUE(child->StyleRef().SubtreeIsSticky());
203 EXPECT_TRUE(grandchild->StyleRef().SubtreeIsSticky());
204 EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
205 EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
206
207 // This forces 'child' to fork it's StyleRareInheritedData, so that we can
208 // ensure that the sticky subtree update behavior survives forking.
209 document.getElementById("child")->SetInlineStyleProperty(
210 CSSPropertyWebkitRubyPosition, CSSValueAfter);
211 document.View()->UpdateAllLifecyclePhases();
212 EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState());
213
214 EXPECT_EQ(kRubyPositionBefore, outer_sticky->StyleRef().GetRubyPosition());
215 EXPECT_EQ(kRubyPositionAfter, child->StyleRef().GetRubyPosition());
216 EXPECT_EQ(kRubyPositionAfter, grandchild->StyleRef().GetRubyPosition());
217 EXPECT_EQ(kRubyPositionAfter, inner_sticky->StyleRef().GetRubyPosition());
218 EXPECT_EQ(kRubyPositionAfter, great_grandchild->StyleRef().GetRubyPosition());
219
220 // Setting -webkit-ruby value shouldn't have affected the sticky subtree bit.
221 EXPECT_TRUE(outer_sticky->StyleRef().SubtreeIsSticky());
222 EXPECT_TRUE(child->StyleRef().SubtreeIsSticky());
223 EXPECT_TRUE(grandchild->StyleRef().SubtreeIsSticky());
224 EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
225 EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
226
227 // Now switch 'outerSticky' back to being non-sticky - all descendents between
228 // it and the 'innerSticky' should be updated, and the 'innerSticky' should
229 // fork it's StyleRareInheritedData to maintain the sticky subtree bit.
230 document.getElementById("outerSticky")
231 ->SetInlineStyleProperty(CSSPropertyPosition, CSSValueStatic);
232 document.View()->UpdateAllLifecyclePhases();
233 EXPECT_EQ(DocumentLifecycle::kPaintClean, document.Lifecycle().GetState());
234
235 EXPECT_FALSE(outer_sticky->StyleRef().SubtreeIsSticky());
236 EXPECT_FALSE(child->StyleRef().SubtreeIsSticky());
237 EXPECT_FALSE(grandchild->StyleRef().SubtreeIsSticky());
238 EXPECT_TRUE(inner_sticky->StyleRef().SubtreeIsSticky());
239 EXPECT_TRUE(great_grandchild->StyleRef().SubtreeIsSticky());
240 }
241
26 } // namespace blink 242 } // namespace blink
OLDNEW
« 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