OLD | NEW |
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 "core/dom/Document.h" | 7 #include "core/dom/Document.h" |
8 #include "core/frame/FrameView.h" | 8 #include "core/frame/FrameView.h" |
9 #include "core/html/HTMLHtmlElement.h" | 9 #include "core/html/HTMLHtmlElement.h" |
| 10 #include "core/layout/LayoutBoxModelObject.h" |
| 11 #include "core/paint/PaintLayer.h" |
10 #include "core/testing/DummyPageHolder.h" | 12 #include "core/testing/DummyPageHolder.h" |
11 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
12 #include <memory> | 14 #include <memory> |
13 | 15 |
14 namespace blink { | 16 namespace blink { |
15 | 17 |
16 TEST(ElementTest, SupportsFocus) { | 18 TEST(ElementTest, SupportsFocus) { |
17 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); | 19 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); |
18 Document& document = pageHolder->document(); | 20 Document& document = pageHolder->document(); |
19 DCHECK(isHTMLHtmlElement(document.documentElement())); | 21 DCHECK(isHTMLHtmlElement(document.documentElement())); |
20 document.setDesignMode("on"); | 22 document.setDesignMode("on"); |
21 document.view()->updateAllLifecyclePhases(); | 23 document.view()->updateAllLifecyclePhases(); |
22 EXPECT_TRUE(document.documentElement()->supportsFocus()) | 24 EXPECT_TRUE(document.documentElement()->supportsFocus()) |
23 << "<html> with designMode=on should be focusable."; | 25 << "<html> with designMode=on should be focusable."; |
24 } | 26 } |
25 | 27 |
| 28 // Verifies that calling getBoundingClientRect cleans compositor inputs. |
| 29 // Cleaning the compositor inputs is required so that position:sticky elements |
| 30 // and their descendants have the correct location. |
| 31 TEST(ElementTest, GetBoundingClientRectUpdatesCompositorInputs) { |
| 32 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); |
| 33 Document& document = pageHolder->document(); |
| 34 |
| 35 document.view()->updateAllLifecyclePhases(); |
| 36 EXPECT_EQ(DocumentLifecycle::PaintClean, document.lifecycle().state()); |
| 37 |
| 38 document.body()->setInnerHTML("<div id='test'></div>"); |
| 39 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 40 document.lifecycle().state()); |
| 41 |
| 42 document.body()->getBoundingClientRect(); |
| 43 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 44 } |
| 45 |
| 46 // Verifies that calling scrollIntoView* cleans compositor inputs. Cleaning the |
| 47 // compositor inputs is required so that position:sticky elements and their |
| 48 // descendants have the correct location. |
| 49 TEST(ElementTest, ScrollIntoViewUpdatesCompositorInputs) { |
| 50 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); |
| 51 Document& document = pageHolder->document(); |
| 52 |
| 53 document.view()->updateAllLifecyclePhases(); |
| 54 EXPECT_EQ(DocumentLifecycle::PaintClean, document.lifecycle().state()); |
| 55 |
| 56 document.body()->setInnerHTML("<div id='test'></div>"); |
| 57 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 58 document.lifecycle().state()); |
| 59 |
| 60 document.body()->scrollIntoView(); |
| 61 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 62 |
| 63 document.body()->setInnerHTML("<div id='test2'></div>"); |
| 64 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 65 document.lifecycle().state()); |
| 66 |
| 67 document.body()->scrollIntoViewIfNeeded(); |
| 68 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 69 } |
| 70 |
| 71 // Verifies that calling offsetTop/offsetLeft cleans compositor inputs. |
| 72 // Cleaning the compositor inputs is required so that position:sticky elements |
| 73 // and their descendants have the correct location. |
| 74 TEST(ElementTest, OffsetTopAndLeftUpdateCompositorInputs) { |
| 75 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); |
| 76 Document& document = pageHolder->document(); |
| 77 |
| 78 document.view()->updateAllLifecyclePhases(); |
| 79 EXPECT_EQ(DocumentLifecycle::PaintClean, document.lifecycle().state()); |
| 80 |
| 81 document.body()->setInnerHTML("<div id='test'></div>"); |
| 82 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 83 document.lifecycle().state()); |
| 84 |
| 85 document.body()->offsetTop(); |
| 86 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 87 |
| 88 document.body()->setInnerHTML("<div id='test2'></div>"); |
| 89 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 90 document.lifecycle().state()); |
| 91 |
| 92 document.body()->offsetLeft(); |
| 93 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 94 } |
| 95 |
| 96 TEST(ElementTest, OffsetTopAndLeftCorrectForStickyElementsAfterInsertion) { |
| 97 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(); |
| 98 Document& document = pageHolder->document(); |
| 99 |
| 100 document.body()->setInnerHTML( |
| 101 "<style>body { margin: 0 }" |
| 102 "#scroller { overflow: scroll; height: 100px; width: 100px; }" |
| 103 "#sticky { height: 25px; position: sticky; top: 0; left: 25px; }" |
| 104 "#padding { height: 500px; width: 300px; }</style>" |
| 105 "<div id='scroller'><div id='writer'></div><div id='sticky'></div>" |
| 106 "<div id='padding'></div></div>"); |
| 107 document.view()->updateAllLifecyclePhases(); |
| 108 |
| 109 Element* scroller = document.getElementById("scroller"); |
| 110 Element* writer = document.getElementById("writer"); |
| 111 Element* sticky = document.getElementById("sticky"); |
| 112 |
| 113 ASSERT_TRUE(scroller); |
| 114 ASSERT_TRUE(writer); |
| 115 ASSERT_TRUE(sticky); |
| 116 |
| 117 scroller->scrollTo(0, 200.0); |
| 118 |
| 119 // After scrolling, the position:sticky element should be offset to stay at |
| 120 // the top of the viewport. |
| 121 EXPECT_EQ(scroller->scrollTop(), sticky->offsetTop()); |
| 122 |
| 123 // Insert a new <div> above the sticky. This will dirty layout. |
| 124 writer->setInnerHTML("<div style='height: 100px;'></div>"); |
| 125 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 126 document.lifecycle().state()); |
| 127 |
| 128 // Querying the new offset of the sticky element should now cause both layout |
| 129 // and compositing inputs to be clean and should return the correct offset. |
| 130 int offsetTop = sticky->offsetTop(); |
| 131 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 132 EXPECT_FALSE( |
| 133 sticky->layoutBoxModelObject()->layer()->needsCompositingInputsUpdate()); |
| 134 EXPECT_EQ(scroller->scrollTop(), offsetTop); |
| 135 |
| 136 scroller->scrollTo(50.0, 0); |
| 137 |
| 138 // After scrolling, the position:sticky element should be offset to stay 25 |
| 139 // pixels from the left of the viewport. |
| 140 EXPECT_EQ(scroller->scrollLeft() + 25, sticky->offsetLeft()); |
| 141 |
| 142 // Insert a new <div> above the sticky. This will dirty layout. |
| 143 writer->setInnerHTML("<div style='width: 700px;'></div>"); |
| 144 EXPECT_EQ(DocumentLifecycle::VisualUpdatePending, |
| 145 document.lifecycle().state()); |
| 146 |
| 147 // Again getting the offset should cause layout and compositing to be clean. |
| 148 int offsetLeft = sticky->offsetLeft(); |
| 149 EXPECT_EQ(DocumentLifecycle::CompositingClean, document.lifecycle().state()); |
| 150 EXPECT_FALSE( |
| 151 sticky->layoutBoxModelObject()->layer()->needsCompositingInputsUpdate()); |
| 152 EXPECT_EQ(scroller->scrollLeft() + 25, offsetLeft); |
| 153 } |
| 154 |
26 } // namespace blink | 155 } // namespace blink |
OLD | NEW |