| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/editing/LocalCaretRect.h" |
| |
| #include "core/editing/PositionWithAffinity.h" |
| #include "core/editing/TextAffinity.h" |
| #include "core/editing/testing/EditingTestBase.h" |
| #include "core/layout/LayoutObject.h" |
| #include "core/layout/ng/ng_physical_box_fragment.h" |
| #include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h" |
| |
| namespace blink { |
| |
| bool operator==(const LocalCaretRect& rect1, const LocalCaretRect& rect2) { |
| return rect1.layout_object == rect2.layout_object && rect1.rect == rect2.rect; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const LocalCaretRect& caret_rect) { |
| return out << "layout_object = " << caret_rect.layout_object->GetNode() |
| << ", rect = " << caret_rect.rect; |
| } |
| |
| class LocalCaretRectTest : public EditingTestBase {}; |
| |
| // Helper class to run the same test code with and without LayoutNG |
| class ParameterizedLocalCaretRectTest |
| : public ::testing::WithParamInterface<bool>, |
| private ScopedLayoutNGForTest, |
| public LocalCaretRectTest { |
| public: |
| ParameterizedLocalCaretRectTest() : ScopedLayoutNGForTest(GetParam()) {} |
| |
| protected: |
| bool LayoutNGEnabled() const { return GetParam(); } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(All, |
| ParameterizedLocalCaretRectTest, |
| ::testing::Bool()); |
| |
| TEST_P(ParameterizedLocalCaretRectTest, DOMAndFlatTrees) { |
| const char* body_content = |
| "<p id='host'><b id='one'>1</b></p><b id='two'>22</b>"; |
| const char* shadow_content = |
| "<b id='two'>22</b><content select=#one></content><b id='three'>333</b>"; |
| SetBodyContent(body_content); |
| SetShadowContent(shadow_content, "host"); |
| |
| Element* one = GetDocument().getElementById("one"); |
| |
| const LocalCaretRect& caret_rect_from_dom_tree = |
| LocalCaretRectOfPosition(Position(one->firstChild(), 0)); |
| |
| const LocalCaretRect& caret_rect_from_flat_tree = |
| LocalCaretRectOfPosition(PositionInFlatTree(one->firstChild(), 0)); |
| |
| EXPECT_FALSE(caret_rect_from_dom_tree.IsEmpty()); |
| EXPECT_EQ(caret_rect_from_dom_tree, caret_rect_from_flat_tree); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, SimpleText) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='font: 10px/10px Ahem; width: 30px'>XXX</div>"); |
| const Node* foo = GetElementById("div")->firstChild(); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, MixedHeightText) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='font: 10px/10px Ahem; width: 30px'>Xpp</div>"); |
| const Node* foo = GetElementById("div")->firstChild(); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, RtlText) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo dir=rtl id=bdo style='display: block; " |
| "font: 10px/10px Ahem; width: 30px'>XXX</bdo>"); |
| const Node* foo = GetElementById("bdo")->firstChild(); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, OverflowTextLtr) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=root style='font: 10px/10px Ahem; width: 30px'>" |
| "XXXX" |
| "</div>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(39, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 4), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextLtr) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=root style='font: 10px/10px Ahem; width: 30px'>" |
| "XX" |
| "</div>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 2), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, OverflowTextRtl) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo id=root style='display:block; font: 10px/10px Ahem; width: 30px' " |
| "dir=rtl>" |
| "XXXX" |
| "</bdo>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(-10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 4), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextRtl) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo id=root style='display:block; font: 10px/10px Ahem; width: 30px' " |
| "dir=rtl>" |
| "XX" |
| "</bdo>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(text, 2), TextAffinity::kDownstream})); |
| } |
| |
| // TODO(xiaochengh): Fix NG LocalCaretText computation for vertical text. |
| TEST_F(LocalCaretRectTest, VerticalRLText) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='writing-mode: vertical-rl; word-break: break-all; " |
| "font: 10px/10px Ahem; width: 30px; height: 30px'>XXXYYYZZZ</div>"); |
| const Node* foo = GetElementById("div")->firstChild(); |
| |
| // TODO(xiaochengh): In vertical-rl writing mode, the |X| property of |
| // LocalCaretRect's LayoutRect seems to refer to the distance to the right |
| // edge of the inline formatting context instead. Figure out if this is |
| // correct. |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kUpstream})); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 4), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 5), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 6), TextAffinity::kUpstream})); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 6), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 7), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 8), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 9), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, VerticalLRText) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='writing-mode: vertical-lr; word-break: break-all; " |
| "font: 10px/10px Ahem; width: 30px; height: 30px'>XXXYYYZZZ</div>"); |
| const Node* foo = GetElementById("div")->firstChild(); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kUpstream})); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 4), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 5), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 6), TextAffinity::kUpstream})); |
| |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 6), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 7), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 8), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(foo, 9), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, OverflowTextVerticalLtr) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=root style='font: 10px/10px Ahem; height: 30px; writing-mode: " |
| "vertical-lr'>" |
| "XXXX" |
| "</div>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 39, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 4), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextVerticalLtr) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=root style='font: 10px/10px Ahem; height: 30px; writing-mode: " |
| "vertical-lr'>" |
| "XX" |
| "</div>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 0, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 20, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 2), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, OverflowTextVerticalRtl) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo id=root style='display:block; font: 10px/10px Ahem; height: 30px; " |
| "writing-mode: vertical-lr' dir=rtl>" |
| "XXXX" |
| "</bdo>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, -10, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 4), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextVerticalRtl) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo id=root style='display:block; font: 10px/10px Ahem; height: 30px; " |
| "writing-mode: vertical-lr' dir=rtl>" |
| "XX" |
| "</bdo>"); |
| const Node* text = GetElementById("root")->firstChild(); |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 29, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 0), TextAffinity::kDownstream})); |
| // LocalCaretRect may be outside the containing block. |
| EXPECT_EQ( |
| LocalCaretRect(text->GetLayoutObject(), LayoutRect(0, 10, 10, 1)), |
| LocalCaretRectOfPosition({Position(text, 2), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, TwoLinesOfTextWithSoftWrap) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='font: 10px/10px Ahem; width: 30px; " |
| "word-break: break-all'>XXXXXX</div>"); |
| const Node* foo = GetElementById("div")->firstChild(); |
| |
| // First line |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kUpstream})); |
| |
| // Second line |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 10, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(10, 10, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 4), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(20, 10, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 5), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(foo->GetLayoutObject(), LayoutRect(29, 10, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 6), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, SoftLineWrapBetweenMultipleTextNodes) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div style='font: 10px/10px Ahem; width: 30px; word-break: break-all'>" |
| "<span>A</span>" |
| "<span>B</span>" |
| "<span id=span-c>C</span>" |
| "<span id=span-d>D</span>" |
| "<span>E</span>" |
| "<span>F</span>" |
| "</div>"); |
| const Node* text_c = GetElementById("span-c")->firstChild(); |
| const Node* text_d = GetElementById("span-d")->firstChild(); |
| |
| const Position after_c(text_c, 1); |
| EXPECT_EQ(LocalCaretRect(text_c->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition({after_c, TextAffinity::kUpstream})); |
| EXPECT_EQ(LocalCaretRect(text_d->GetLayoutObject(), LayoutRect(0, 10, 1, 10)), |
| LocalCaretRectOfPosition({after_c, TextAffinity::kDownstream})); |
| |
| const Position before_d(text_d, 0); |
| // TODO(xiaochengh): Should return the same result for legacy and LayoutNG. |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(text_c->GetLayoutObject(), LayoutRect(29, 0, 1, 10)) |
| : LocalCaretRect(text_d->GetLayoutObject(), LayoutRect(0, 10, 1, 10)), |
| LocalCaretRectOfPosition({before_d, TextAffinity::kUpstream})); |
| EXPECT_EQ(LocalCaretRect(text_d->GetLayoutObject(), LayoutRect(0, 10, 1, 10)), |
| LocalCaretRectOfPosition({before_d, TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, |
| SoftLineWrapBetweenMultipleTextNodesRtl) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo dir=rtl style='font: 10px/10px Ahem; width: 30px; " |
| "word-break: break-all; display: block'>" |
| "<span>A</span>" |
| "<span>B</span>" |
| "<span id=span-c>C</span>" |
| "<span id=span-d>D</span>" |
| "<span>E</span>" |
| "<span>F</span>" |
| "</bdo>"); |
| const Node* text_c = GetElementById("span-c")->firstChild(); |
| const Node* text_d = GetElementById("span-d")->firstChild(); |
| |
| const Position after_c(text_c, 1); |
| EXPECT_EQ(LocalCaretRect(text_c->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({after_c, TextAffinity::kUpstream})); |
| EXPECT_EQ( |
| LocalCaretRect(text_d->GetLayoutObject(), LayoutRect(29, 10, 1, 10)), |
| LocalCaretRectOfPosition({after_c, TextAffinity::kDownstream})); |
| |
| const Position before_d(text_d, 0); |
| // TODO(xiaochengh): Should return the same result for legacy and LayoutNG. |
| EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(text_c->GetLayoutObject(), |
| LayoutRect(0, 0, 1, 10)) |
| : LocalCaretRect(text_d->GetLayoutObject(), |
| LayoutRect(29, 10, 1, 10)), |
| LocalCaretRectOfPosition({before_d, TextAffinity::kUpstream})); |
| EXPECT_EQ( |
| LocalCaretRect(text_d->GetLayoutObject(), LayoutRect(29, 10, 1, 10)), |
| LocalCaretRectOfPosition({before_d, TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, CaretRectAtBR) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div style='font: 10px/10px Ahem; width: 30px'><br>foo</div>"); |
| const Element& br = *GetDocument().QuerySelector("br"); |
| |
| EXPECT_EQ(LocalCaretRect(br.GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(br), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, CaretRectAtRtlBR) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo dir=rtl style='display: block; font: 10px/10px Ahem; width: 30px'>" |
| "<br>foo</bdo>"); |
| const Element& br = *GetDocument().QuerySelector("br"); |
| |
| EXPECT_EQ(LocalCaretRect(br.GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(br), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, Images) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='font: 10px/10px Ahem; width: 30px'>" |
| "<img id=img1 width=10px height=10px>" |
| "<img id=img2 width=10px height=10px>" |
| "</div>"); |
| |
| const Element& img1 = *GetElementById("img1"); |
| |
| EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), LayoutRect(0, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img1), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), LayoutRect(9, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img1), TextAffinity::kDownstream})); |
| |
| const Element& img2 = *GetElementById("img2"); |
| |
| // Box-anchored LocalCaretRect is local to the box itself, instead of its |
| // containing block. |
| // TODO(xiaochengh): Should return the same result for legacy and LayoutNG. |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(img1.GetLayoutObject(), LayoutRect(9, 0, 1, 12)) |
| : LocalCaretRect(img2.GetLayoutObject(), LayoutRect(0, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img2), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), LayoutRect(9, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img2), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, RtlImages) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<bdo dir=rtl style='font: 10px/10px Ahem; width: 30px; display: block'>" |
| "<img id=img1 width=10px height=10px>" |
| "<img id=img2 width=10px height=10px>" |
| "</bdo>"); |
| |
| const Element& img1 = *GetElementById("img1"); |
| const Element& img2 = *GetElementById("img2"); |
| |
| // Box-anchored LocalCaretRect is local to the box itself, instead of its |
| // containing block. |
| EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), LayoutRect(9, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(img2.GetLayoutObject(), LayoutRect(9, 0, 1, 12)) |
| : LocalCaretRect(img1.GetLayoutObject(), LayoutRect(0, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img1), TextAffinity::kDownstream})); |
| |
| EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), LayoutRect(9, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img2), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), LayoutRect(0, 0, 1, 12)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img2), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, VerticalImage) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| SetBodyContent( |
| "<div style='writing-mode: vertical-rl'>" |
| "<img id=img width=10px height=20px>" |
| "</div>"); |
| |
| const Element& img = *GetElementById("img"); |
| |
| // Box-anchored LocalCaretRect is local to the box itself, instead of its |
| // containing block. |
| EXPECT_EQ(LocalCaretRect(img.GetLayoutObject(), LayoutRect(0, 0, 10, 1)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img), TextAffinity::kDownstream})); |
| |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(img.GetLayoutObject(), LayoutRect(0, 19, 10, 1)) |
| // TODO(crbug.com/805064): The legacy behavior is wrong. Fix it. |
| : LocalCaretRect(img.GetLayoutObject(), LayoutRect(0, 9, 10, 1)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, TextAndImageMixedHeight) { |
| // This test only records the current behavior. Future changes are allowed. |
| |
| LoadAhem(); |
| SetBodyContent( |
| "<div id=div style='font: 10px/10px Ahem; width: 30px'>" |
| "X" |
| "<img id=img width=10px height=5px style='vertical-align: text-bottom'>" |
| "p</div>"); |
| |
| const Element& img = *GetElementById("img"); |
| const Node* text1 = img.previousSibling(); |
| const Node* text2 = img.nextSibling(); |
| |
| EXPECT_EQ(LocalCaretRect(text1->GetLayoutObject(), LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position(text1, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(text1->GetLayoutObject(), LayoutRect(10, 0, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position(text1, 1), TextAffinity::kDownstream})); |
| |
| // TODO(xiaochengh): Should return the same result for legacy and LayoutNG. |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(text1->GetLayoutObject(), LayoutRect(10, 0, 1, 10)) |
| : LocalCaretRect(img.GetLayoutObject(), LayoutRect(0, -5, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position::BeforeNode(img), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(img.GetLayoutObject(), LayoutRect(9, -5, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position::AfterNode(img), TextAffinity::kDownstream})); |
| |
| // TODO(xiaochengh): Should return the same result for legacy and LayoutNG. |
| EXPECT_EQ( |
| LayoutNGEnabled() |
| ? LocalCaretRect(img.GetLayoutObject(), LayoutRect(9, -5, 1, 10)) |
| : LocalCaretRect(text2->GetLayoutObject(), LayoutRect(20, 5, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position(text2, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ(LocalCaretRect(text2->GetLayoutObject(), LayoutRect(29, 0, 1, 10)), |
| LocalCaretRectOfPosition( |
| {Position(text2, 1), TextAffinity::kDownstream})); |
| } |
| |
| TEST_P(ParameterizedLocalCaretRectTest, FloatFirstLetter) { |
| LoadAhem(); |
| InsertStyleElement("#container::first-letter{float:right}"); |
| SetBodyContent( |
| "<div id=container style='font: 10px/10px Ahem; width: 40px'>foo</div>"); |
| const Node* foo = GetElementById("container")->firstChild(); |
| const LayoutObject* first_letter = AssociatedLayoutObjectOf(*foo, 0); |
| const LayoutObject* remaining_text = AssociatedLayoutObjectOf(*foo, 1); |
| |
| // TODO(editing-dev): Legacy LocalCaretRectOfPosition() is not aware of the |
| // first-letter LayoutObject. Fix it. |
| |
| EXPECT_EQ( |
| LocalCaretRect(LayoutNGEnabled() ? first_letter : remaining_text, |
| LayoutRect(0, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 0), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(remaining_text, |
| LayoutRect(LayoutNGEnabled() ? 0 : 10, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 1), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(remaining_text, |
| LayoutRect(LayoutNGEnabled() ? 10 : 20, 0, 1, 10)), |
| LocalCaretRectOfPosition({Position(foo, 2), TextAffinity::kDownstream})); |
| EXPECT_EQ( |
| LocalCaretRect(remaining_text, LayoutNGEnabled() |
| ? LayoutRect(20, 0, 1, 10) |
| : LayoutRect()), |
| LocalCaretRectOfPosition({Position(foo, 3), TextAffinity::kDownstream})); |
| } |
| |
| } // namespace blink |