| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/editing/LayoutPosition.h" |
| 6 |
| 7 #include "core/dom/Element.h" |
| 8 #include "core/dom/NodeTraversal.h" |
| 9 #include "core/dom/Text.h" |
| 10 #include "core/editing/EditingTestBase.h" |
| 11 #include "core/editing/VisibleUnits.h" |
| 12 #include "core/layout/LayoutView.h" |
| 13 #include "core/layout/line/InlineTextBox.h" |
| 14 #include "wtf/Noncopyable.h" |
| 15 |
| 16 namespace blink { |
| 17 |
| 18 class LayoutPositionTest : public EditingTestBase { |
| 19 WTF_MAKE_NONCOPYABLE(LayoutPositionTest); |
| 20 |
| 21 public: |
| 22 // |LayoutPosition| constructors |
| 23 LayoutPosition afterAnchorOf(LayoutObject*); |
| 24 LayoutPosition beforeAnchorOf(LayoutObject*); |
| 25 LayoutPosition beforeChildrenOf(LayoutObject*); |
| 26 LayoutPosition textPositionOf(InlineBox*, int offsetInBox); |
| 27 |
| 28 // |LayoutPosition| member variable getters |
| 29 static PositionAnchorType anchorTypeOf(const LayoutPosition&); |
| 30 static InlineBox* inlineBoxOf(const LayoutPosition&); |
| 31 static LayoutObject* layoutObjectOf(const LayoutPosition&); |
| 32 static int textOffsetOf(const LayoutPosition&); |
| 33 |
| 34 protected: |
| 35 LayoutPositionTest() = default; |
| 36 |
| 37 FloatQuad caretQuadOf(const LayoutPosition& position) |
| 38 { |
| 39 return position.computeCaretQuad(); |
| 40 } |
| 41 |
| 42 // Shortcuts |
| 43 LayoutPosition layoutPositionAfter(Node* node) |
| 44 { |
| 45 return LayoutPosition::create(Position::afterNode(node)); |
| 46 } |
| 47 |
| 48 LayoutPosition layoutPositionBefore(Node* node) |
| 49 { |
| 50 return LayoutPosition::create(Position::beforeNode(node)); |
| 51 } |
| 52 |
| 53 LayoutPosition layoutPositionOf(Node* node, int offset) |
| 54 { |
| 55 return LayoutPosition::create(Position(node, offset)); |
| 56 } |
| 57 }; |
| 58 |
| 59 // static |
| 60 LayoutPosition LayoutPositionTest::afterAnchorOf(LayoutObject* layoutObject) |
| 61 { |
| 62 return LayoutPosition::afterAnchorOf(layoutObject); |
| 63 } |
| 64 |
| 65 // static |
| 66 LayoutPosition LayoutPositionTest::beforeAnchorOf(LayoutObject* layoutObject) |
| 67 { |
| 68 return LayoutPosition::beforeAnchorOf(layoutObject); |
| 69 } |
| 70 |
| 71 // static |
| 72 LayoutPosition LayoutPositionTest::beforeChildrenOf(LayoutObject* layoutObject) |
| 73 { |
| 74 return LayoutPosition::beforeChildrenOf(layoutObject); |
| 75 } |
| 76 |
| 77 LayoutPosition LayoutPositionTest::textPositionOf(InlineBox* box, int offsetInBo
x) |
| 78 { |
| 79 return LayoutPosition(InlineBoxPosition(box, offsetInBox)); |
| 80 } |
| 81 |
| 82 // static |
| 83 PositionAnchorType LayoutPositionTest::anchorTypeOf(const LayoutPosition& positi
on) |
| 84 { |
| 85 return position.m_anchorType; |
| 86 } |
| 87 |
| 88 // static |
| 89 InlineBox* LayoutPositionTest::inlineBoxOf(const LayoutPosition& position) |
| 90 { |
| 91 return position.m_inlineBox; |
| 92 } |
| 93 |
| 94 // static |
| 95 LayoutObject* LayoutPositionTest::layoutObjectOf(const LayoutPosition& position) |
| 96 { |
| 97 return position.m_layoutObject; |
| 98 } |
| 99 |
| 100 // static |
| 101 int LayoutPositionTest::textOffsetOf(const LayoutPosition& position) |
| 102 { |
| 103 ASSERT(position.isOffsetInAnchor()); |
| 104 return position.m_offsetInBox; |
| 105 } |
| 106 |
| 107 TEST_F(LayoutPositionTest, canPlaceCaret) |
| 108 { |
| 109 setBodyContent("<div> 234 <br> </div>"); |
| 110 updateAllLifecyclePhases(); |
| 111 |
| 112 // Layout Tree of this test: |
| 113 // +LayoutView {#document} (0,0)+800x600 |
| 114 // | +LayoutBlockFlow {HTML} (0,0)+800x600 |
| 115 // | | +LayoutBlockFlow {BODY} (8,8)+784x584 |
| 116 // | | | +LayoutBlockFlow {DIV} (0,0)+784x2 |
| 117 // | | | | | | +-RootInlineBox DIV frameRect=(0,0)+4x2 bidiLevel=0 |
| 118 // | | | | | | | +InlineTextBox "234 " start=2 end=5 bidiLevel=0 |
| 119 // | | | | +LayoutText {" 234 "} |
| 120 // ------------>LayoutBR {BR} |
| 121 Element* const body = document().body(); |
| 122 Node* const div = body->firstChild(); |
| 123 Node* const text = div->firstChild(); |
| 124 Node* const br = text->nextSibling(); |
| 125 Node* const lastText = div->lastChild(); |
| 126 |
| 127 EXPECT_TRUE(layoutPositionOf(body, 0).canPlaceCaret()); |
| 128 EXPECT_TRUE(layoutPositionBefore(div).canPlaceCaret()); |
| 129 |
| 130 EXPECT_TRUE(layoutPositionOf(div, 0).canPlaceCaret()); |
| 131 EXPECT_TRUE(layoutPositionBefore(text).canPlaceCaret()); |
| 132 EXPECT_TRUE(layoutPositionAfter(text).canPlaceCaret()); |
| 133 EXPECT_TRUE(layoutPositionOf(text, 0).canPlaceCaret()); |
| 134 EXPECT_TRUE(layoutPositionOf(text, 1).canPlaceCaret()); |
| 135 EXPECT_TRUE(layoutPositionOf(text, 2).canPlaceCaret()); |
| 136 EXPECT_TRUE(layoutPositionOf(text, 3).canPlaceCaret()); |
| 137 EXPECT_TRUE(layoutPositionOf(text, 4).canPlaceCaret()); |
| 138 EXPECT_TRUE(layoutPositionOf(text, 5).canPlaceCaret()); |
| 139 EXPECT_TRUE(layoutPositionOf(text, 6).canPlaceCaret()); |
| 140 EXPECT_TRUE(layoutPositionOf(text, 6).canPlaceCaret()); |
| 141 |
| 142 EXPECT_FALSE(layoutPositionBefore(br).canPlaceCaret()); |
| 143 EXPECT_FALSE(layoutPositionOf(div, 1).canPlaceCaret()); |
| 144 EXPECT_FALSE(layoutPositionAfter(br).canPlaceCaret()) |
| 145 << "Since it is hard to compute caret quad for after BR, we don't allow
to place caret after BR."; |
| 146 |
| 147 EXPECT_FALSE(layoutPositionBefore(lastText).canPlaceCaret()) |
| 148 << "We can't place caret before collapsed whitespace."; |
| 149 EXPECT_FALSE(layoutPositionOf(div, 2).canPlaceCaret()) |
| 150 << "We can't place caret before collapsed whitespace."; |
| 151 EXPECT_FALSE(layoutPositionAfter(lastText).canPlaceCaret()) |
| 152 << "We can't place caret before collapsed whitespace."; |
| 153 EXPECT_FALSE(layoutPositionOf(div, 3).canPlaceCaret()) |
| 154 << "We can't place caret before collapsed whitespace."; |
| 155 |
| 156 EXPECT_TRUE(layoutPositionAfter(div).canPlaceCaret()); |
| 157 EXPECT_TRUE(layoutPositionOf(body, 1).canPlaceCaret()); |
| 158 } |
| 159 |
| 160 TEST_F(LayoutPositionTest, ctorLeftToRightSimple) |
| 161 { |
| 162 setBodyContent("<div id=one>222</div>"); |
| 163 updateAllLifecyclePhases(); |
| 164 |
| 165 Element* const body = document().body(); |
| 166 Element* const one = body->querySelector("#one", ASSERT_NO_EXCEPTION); |
| 167 InlineTextBox* const textBox = toLayoutText(one->firstChild()->layoutObject(
))->firstTextBox(); |
| 168 |
| 169 EXPECT_EQ(beforeAnchorOf(one->layoutObject()), layoutPositionOf(body, 0)); |
| 170 EXPECT_EQ(afterAnchorOf(one->layoutObject()), layoutPositionOf(body, 1)); |
| 171 |
| 172 EXPECT_EQ(beforeAnchorOf(one->layoutObject()), layoutPositionBefore(one)) |
| 173 << "beforeAnchor(one) is a valid position"; |
| 174 EXPECT_EQ(textPositionOf(textBox, 0), LayoutPosition::create(Position::first
PositionInNode(one))) |
| 175 << "beforeChildren(one) should be text box position"; |
| 176 EXPECT_EQ(textPositionOf(textBox, 1), layoutPositionOf(one->firstChild(), 1)
) |
| 177 << "offsetInAnchor('222') should be text box position"; |
| 178 EXPECT_EQ(textPositionOf(textBox, 3), LayoutPosition::create(Position::lastP
ositionInNode(one))) |
| 179 << "afterChildren(one) should be end of text box"; |
| 180 EXPECT_EQ(afterAnchorOf(one->layoutObject()), layoutPositionAfter(one)) |
| 181 << "afterAnchor(one) is a valid position"; |
| 182 |
| 183 EXPECT_EQ(layoutPositionOf(body, 0), layoutPositionBefore(one)) |
| 184 << "(body, 0) and before(one) should be same."; |
| 185 EXPECT_EQ(layoutPositionOf(body, 1), layoutPositionAfter(one)) |
| 186 << "(body, 0) and after(one) should be same."; |
| 187 |
| 188 EXPECT_EQ(layoutPositionOf(one, 0), layoutPositionOf(one->firstChild(), 0)) |
| 189 << "(one, 0) and (text, 0) should be same."; |
| 190 EXPECT_EQ(layoutPositionOf(one, 1), layoutPositionOf(one->firstChild(), 3)) |
| 191 << "(one, 1) and (text, 3) should be same."; |
| 192 } |
| 193 |
| 194 // See http://unicode.org/cldr/utility/bidi.jsp for BiDi level and ordering. |
| 195 TEST_F(LayoutPositionTest, ctorRightToLeftMixed) |
| 196 { |
| 197 setBodyContent("<div id=one>mark 3.1% مارْd
3 2.0.</div>"); |
| 198 updateAllLifecyclePhases(); |
| 199 |
| 200 Element* body = document().body(); |
| 201 Element* one = body->querySelector("#one", ASSERT_NO_EXCEPTION); |
| 202 Node* text = one->firstChild(); |
| 203 // |text| node has four inline boxes based on BiIDi level. |
| 204 // Node: "mark 3.1% "(L0) "ARABIC"(L1) "2.0"(L2) "."(L0) |
| 205 // Layout: "mark 3.1% " "2.0" "ARABIC" "." |
| 206 InlineTextBox* const textBox0 = toLayoutText(text->layoutObject())->firstTex
tBox(); |
| 207 InlineTextBox* const textBox1 = textBox0->nextTextBox(); |
| 208 InlineTextBox* const textBox2 = textBox1->nextTextBox(); |
| 209 InlineTextBox* const textBox3 = textBox2->nextTextBox(); |
| 210 ASSERT(textBox3 == toLayoutText(text->layoutObject())->lastTextBox()); |
| 211 |
| 212 EXPECT_EQ(textPositionOf(textBox0, 0), layoutPositionOf(text, 0)); |
| 213 EXPECT_EQ(textPositionOf(textBox0, 1), layoutPositionOf(text, 1)); |
| 214 EXPECT_EQ(textPositionOf(textBox0, 9), layoutPositionOf(text, 9)); |
| 215 |
| 216 EXPECT_EQ(textPositionOf(textBox2, 10), layoutPositionOf(text, 10)); |
| 217 EXPECT_EQ(textPositionOf(textBox2, 11), layoutPositionOf(text, 11)); |
| 218 EXPECT_EQ(textPositionOf(textBox2, 14), layoutPositionOf(text, 14)); |
| 219 EXPECT_EQ(textPositionOf(textBox2, 15), layoutPositionOf(text, 15)); |
| 220 |
| 221 EXPECT_EQ(textPositionOf(textBox1, 16), layoutPositionOf(text, 16)); |
| 222 EXPECT_EQ(textPositionOf(textBox1, 17), layoutPositionOf(text, 17)); |
| 223 EXPECT_EQ(textPositionOf(textBox1, 18), layoutPositionOf(text, 18)); |
| 224 |
| 225 EXPECT_EQ(textPositionOf(textBox3, 19), layoutPositionOf(text, 19)); |
| 226 } |
| 227 |
| 228 TEST_F(LayoutPositionTest, ctorRightToLeftWithBidiOverride) |
| 229 { |
| 230 setBodyContent("<span id=one dir=rtl>abc <bdo id=two dir=rtl>def</bdo> ghi</
span>"); |
| 231 updateAllLifecyclePhases(); |
| 232 |
| 233 Element* body = document().body(); |
| 234 Element* one = body->querySelector("#one", ASSERT_NO_EXCEPTION); |
| 235 Element* two = body->querySelector("#two", ASSERT_NO_EXCEPTION); |
| 236 |
| 237 Node* firstText = one->firstChild(); |
| 238 Node* rtlText = two->firstChild(); |
| 239 Node* lastText = one->lastChild(); |
| 240 |
| 241 InlineTextBox* firstTextBox = toLayoutText(firstText->layoutObject())->lastT
extBox(); |
| 242 InlineTextBox* rtlTextBox = toLayoutText(rtlText->layoutObject())->firstText
Box(); |
| 243 InlineTextBox* lastTextBox = toLayoutText(lastText->layoutObject())->firstTe
xtBox(); |
| 244 |
| 245 EXPECT_EQ(textPositionOf(firstTextBox, 0), layoutPositionOf(firstText, 0)); |
| 246 EXPECT_EQ(textPositionOf(firstTextBox, 1), layoutPositionOf(firstText, 1)); |
| 247 EXPECT_EQ(textPositionOf(firstTextBox, 2), layoutPositionOf(firstText, 2)); |
| 248 |
| 249 EXPECT_EQ(textPositionOf(rtlTextBox, 0), layoutPositionOf(rtlText, 0)); |
| 250 EXPECT_EQ(textPositionOf(rtlTextBox, 1), layoutPositionOf(rtlText, 1)); |
| 251 EXPECT_EQ(textPositionOf(rtlTextBox, 2), layoutPositionOf(rtlText, 2)); |
| 252 |
| 253 EXPECT_EQ(textPositionOf(lastTextBox->nextTextBox(), 0), layoutPositionOf(la
stText, 0)); |
| 254 EXPECT_EQ(textPositionOf(lastTextBox, 1), layoutPositionOf(lastText, 1)); |
| 255 EXPECT_EQ(textPositionOf(lastTextBox, 2), layoutPositionOf(lastText, 2)); |
| 256 EXPECT_EQ(textPositionOf(lastTextBox, 3), layoutPositionOf(lastText, 3)); |
| 257 } |
| 258 |
| 259 TEST_F(LayoutPositionTest, isCaretEquivalentWithBr) |
| 260 { |
| 261 setBodyContent("<div>one<br>two</div>"); |
| 262 updateAllLifecyclePhases(); |
| 263 Element* div = document().querySelector("div", ASSERT_NO_EXCEPTION); |
| 264 Node* one = div->firstChild(); |
| 265 Node* two = div->lastChild(); |
| 266 Element* br = document().querySelector("br", ASSERT_NO_EXCEPTION); |
| 267 |
| 268 const LayoutPosition& beforeBr = layoutPositionBefore(br); |
| 269 const LayoutPosition& afterBr = layoutPositionAfter(br); |
| 270 const LayoutPosition& one3 = layoutPositionOf(one, 3); |
| 271 const LayoutPosition& two0 = layoutPositionOf(two, 0); |
| 272 |
| 273 EXPECT_FALSE(beforeBr.isCaretEquivalent(afterBr)); |
| 274 EXPECT_TRUE(one3.isCaretEquivalent(beforeBr)) |
| 275 << "End of text and before BR should be equivalent."; |
| 276 EXPECT_FALSE(two0.isCaretEquivalent(afterBr)) |
| 277 << "Since we can't place caret after BR, after BR and first position of
next line aren't equivalent."; |
| 278 } |
| 279 |
| 280 TEST_F(LayoutPositionTest, isCaretEquivalentWithCollapsedWhitespace) |
| 281 { |
| 282 setBodyContent("<div> 234 </div>"); |
| 283 updateAllLifecyclePhases(); |
| 284 Element* div = document().querySelector("div", ASSERT_NO_EXCEPTION); |
| 285 Node* text = div->firstChild(); |
| 286 |
| 287 const LayoutPosition& before2 = layoutPositionOf(text, 2); |
| 288 EXPECT_TRUE(before2.isCaretEquivalent(layoutPositionOf(text, 0))); |
| 289 EXPECT_TRUE(before2.isCaretEquivalent(layoutPositionOf(text, 1))); |
| 290 EXPECT_FALSE(before2.isCaretEquivalent(layoutPositionBefore(div))); |
| 291 EXPECT_EQ(Position(div, 0), mostBackwardCaretPosition(Position(text, 2))); |
| 292 |
| 293 const LayoutPosition& before5 = layoutPositionOf(text, 5); |
| 294 EXPECT_TRUE(before5.isCaretEquivalent(layoutPositionOf(text, 6))); |
| 295 EXPECT_TRUE(before5.isCaretEquivalent(layoutPositionOf(text, 7))); |
| 296 EXPECT_FALSE(before2.isCaretEquivalent(layoutPositionAfter(div))); |
| 297 } |
| 298 |
| 299 TEST_F(LayoutPositionTest, null) |
| 300 { |
| 301 EXPECT_TRUE(LayoutPosition().isNull()); |
| 302 EXPECT_FALSE(LayoutPosition().isNotNull()); |
| 303 EXPECT_TRUE(LayoutPosition() == LayoutPosition()); |
| 304 EXPECT_FALSE(LayoutPosition() != LayoutPosition()); |
| 305 } |
| 306 |
| 307 } // namespace blink |
| OLD | NEW |