| // Copyright 2016 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 "third_party/blink/renderer/core/editing/selection_controller.h" |
| |
| #include "third_party/blink/renderer/core/editing/frame_selection.h" |
| #include "third_party/blink/renderer/core/editing/selection_template.h" |
| #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h" |
| #include "third_party/blink/renderer/core/editing/visible_selection.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/input/event_handler.h" |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" |
| #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" |
| |
| namespace blink { |
| |
| class SelectionControllerTest : public EditingTestBase { |
| protected: |
| using AppendTrailingWhitespace = |
| SelectionController::AppendTrailingWhitespace; |
| using SelectInputEventType = SelectionController::SelectInputEventType; |
| |
| SelectionControllerTest() = default; |
| |
| SelectionController& Controller() { |
| return GetFrame().GetEventHandler().GetSelectionController(); |
| } |
| |
| HitTestResult HitTestResultAtLocation(const HitTestLocation& location) { |
| return GetFrame().GetEventHandler().HitTestResultAtLocation(location); |
| } |
| |
| HitTestResult HitTestResultAtLocation(int x, int y) { |
| HitTestLocation location(IntPoint(x, y)); |
| return HitTestResultAtLocation(location); |
| } |
| |
| static PositionWithAffinity GetPositionFromHitTestResult( |
| const HitTestResult& hit_test_result) { |
| return hit_test_result.GetPosition(); |
| } |
| |
| VisibleSelection VisibleSelectionInDOMTree() const { |
| return Selection().ComputeVisibleSelectionInDOMTree(); |
| } |
| |
| VisibleSelectionInFlatTree GetVisibleSelectionInFlatTree() const { |
| return Selection().ComputeVisibleSelectionInFlatTree(); |
| } |
| |
| bool SelectClosestWordFromHitTestResult( |
| const HitTestResult& result, |
| AppendTrailingWhitespace append_trailing_whitespace, |
| SelectInputEventType select_input_event_type); |
| void SetCaretAtHitTestResult(const HitTestResult&); |
| void SetNonDirectionalSelectionIfNeeded(const SelectionInFlatTree&, |
| TextGranularity); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SelectionControllerTest); |
| }; |
| |
| bool SelectionControllerTest::SelectClosestWordFromHitTestResult( |
| const HitTestResult& result, |
| AppendTrailingWhitespace append_trailing_whitespace, |
| SelectInputEventType select_input_event_type) { |
| return Controller().SelectClosestWordFromHitTestResult( |
| result, append_trailing_whitespace, select_input_event_type); |
| } |
| |
| void SelectionControllerTest::SetCaretAtHitTestResult( |
| const HitTestResult& hit_test_result) { |
| GetFrame().GetEventHandler().GetSelectionController().SetCaretAtHitTestResult( |
| hit_test_result); |
| } |
| |
| void SelectionControllerTest::SetNonDirectionalSelectionIfNeeded( |
| const SelectionInFlatTree& new_selection, |
| TextGranularity granularity) { |
| GetFrame() |
| .GetEventHandler() |
| .GetSelectionController() |
| .SetNonDirectionalSelectionIfNeeded( |
| new_selection, |
| SetSelectionOptions::Builder().SetGranularity(granularity).Build(), |
| SelectionController::kDoNotAdjustEndpoints); |
| } |
| |
| class ParameterizedSelectionControllerTest |
| : public SelectionControllerTest, |
| public testing::WithParamInterface<bool>, |
| private ScopedLayoutNGForTest { |
| public: |
| ParameterizedSelectionControllerTest() : ScopedLayoutNGForTest(GetParam()) {} |
| |
| bool LayoutNGEnabled() const { return GetParam(); } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(SelectionControllerTest, |
| ParameterizedSelectionControllerTest, |
| testing::Bool()); |
| |
| TEST_F(SelectionControllerTest, setNonDirectionalSelectionIfNeeded) { |
| const char* body_content = "<span id=top>top</span><span id=host></span>"; |
| const char* shadow_content = "<span id=bottom>bottom</span>"; |
| SetBodyContent(body_content); |
| ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host"); |
| |
| Node* top = GetDocument().getElementById("top")->firstChild(); |
| Node* bottom = shadow_root->getElementById("bottom")->firstChild(); |
| |
| // top to bottom |
| SetNonDirectionalSelectionIfNeeded(SelectionInFlatTree::Builder() |
| .Collapse(PositionInFlatTree(top, 1)) |
| .Extend(PositionInFlatTree(bottom, 3)) |
| .Build(), |
| TextGranularity::kCharacter); |
| EXPECT_EQ(VisibleSelectionInDOMTree().Start(), |
| VisibleSelectionInDOMTree().Base()); |
| EXPECT_EQ(VisibleSelectionInDOMTree().End(), |
| VisibleSelectionInDOMTree().Extent()); |
| EXPECT_EQ(Position(top, 1), VisibleSelectionInDOMTree().Start()); |
| EXPECT_EQ(Position(top, 3), VisibleSelectionInDOMTree().End()); |
| |
| EXPECT_EQ(PositionInFlatTree(top, 1), GetVisibleSelectionInFlatTree().Base()); |
| EXPECT_EQ(PositionInFlatTree(bottom, 3), |
| GetVisibleSelectionInFlatTree().Extent()); |
| EXPECT_EQ(PositionInFlatTree(top, 1), |
| GetVisibleSelectionInFlatTree().Start()); |
| EXPECT_EQ(PositionInFlatTree(bottom, 3), |
| GetVisibleSelectionInFlatTree().End()); |
| |
| // bottom to top |
| SetNonDirectionalSelectionIfNeeded( |
| SelectionInFlatTree::Builder() |
| .Collapse(PositionInFlatTree(bottom, 3)) |
| .Extend(PositionInFlatTree(top, 1)) |
| .Build(), |
| TextGranularity::kCharacter); |
| EXPECT_EQ(VisibleSelectionInDOMTree().End(), |
| VisibleSelectionInDOMTree().Base()); |
| EXPECT_EQ(VisibleSelectionInDOMTree().Start(), |
| VisibleSelectionInDOMTree().Extent()); |
| EXPECT_EQ(Position(bottom, 0), VisibleSelectionInDOMTree().Start()); |
| EXPECT_EQ(Position(bottom, 3), VisibleSelectionInDOMTree().End()); |
| |
| EXPECT_EQ(PositionInFlatTree(bottom, 3), |
| GetVisibleSelectionInFlatTree().Base()); |
| EXPECT_EQ(PositionInFlatTree(top, 1), |
| GetVisibleSelectionInFlatTree().Extent()); |
| EXPECT_EQ(PositionInFlatTree(top, 1), |
| GetVisibleSelectionInFlatTree().Start()); |
| EXPECT_EQ(PositionInFlatTree(bottom, 3), |
| GetVisibleSelectionInFlatTree().End()); |
| } |
| |
| TEST_F(SelectionControllerTest, setCaretAtHitTestResult) { |
| const char* body_content = "<div id='sample' contenteditable>sample</div>"; |
| SetBodyContent(body_content); |
| GetDocument().GetSettings()->SetScriptEnabled(true); |
| Element* script = GetDocument().CreateRawElement(html_names::kScriptTag); |
| script->setInnerHTML( |
| "var sample = document.getElementById('sample');" |
| "sample.addEventListener('onselectstart', " |
| " event => elem.parentNode.removeChild(elem));"); |
| GetDocument().body()->AppendChild(script); |
| UpdateAllLifecyclePhasesForTest(); |
| HitTestLocation location((IntPoint(8, 8))); |
| GetFrame().GetEventHandler().GetSelectionController().HandleGestureLongPress( |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location)); |
| } |
| |
| // For http://crbug.com/704827 |
| TEST_F(SelectionControllerTest, setCaretAtHitTestResultWithNullPosition) { |
| SetBodyContent( |
| "<style>" |
| "#sample:before {content: ' '}" |
| "#sample { user-select: none; }" |
| "</style>" |
| "<div id=sample></div>"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| // Hit " " in before pseudo element of "sample". |
| HitTestLocation location((IntPoint(10, 10))); |
| SetCaretAtHitTestResult( |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location)); |
| |
| EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone()); |
| } |
| |
| // For http://crbug.com/759971 |
| TEST_F(SelectionControllerTest, |
| SetCaretAtHitTestResultWithDisconnectedPosition) { |
| GetDocument().GetSettings()->SetScriptEnabled(true); |
| Element* script = GetDocument().CreateRawElement(html_names::kScriptTag); |
| script->setInnerHTML( |
| "document.designMode = 'on';" |
| "const selection = window.getSelection();" |
| "const html = document.getElementsByTagName('html')[0];" |
| "selection.collapse(html);" |
| "const range = selection.getRangeAt(0);" |
| |
| "function selectstart() {" |
| " const body = document.getElementsByTagName('body')[0];" |
| " range.surroundContents(body);" |
| " range.deleteContents();" |
| "}" |
| "document.addEventListener('selectstart', selectstart);"); |
| GetDocument().body()->AppendChild(script); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| // Simulate a tap somewhere in the document |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebInputEvent::kIsCompatibilityEventForTouch, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| mouse_event.SetFrameScale(1); |
| HitTestLocation location((IntPoint(0, 0))); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults( |
| mouse_event, location, |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location))); |
| |
| // The original bug was that this test would cause |
| // TextSuggestionController::HandlePotentialMisspelledWordTap() to crash. So |
| // the primary thing this test cases tests is that we can get here without |
| // crashing. |
| |
| // Verify no selection was set. |
| EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone()); |
| } |
| |
| // For http://crbug.com/700368 |
| TEST_F(SelectionControllerTest, AdjustSelectionWithTrailingWhitespace) { |
| SetBodyContent( |
| "<input type=checkbox>" |
| "<div style='user-select:none'>abc</div>"); |
| Element* const input = GetDocument().QuerySelector("input"); |
| |
| const SelectionInFlatTree& selection = ExpandWithGranularity( |
| SelectionInFlatTree::Builder() |
| .Collapse(PositionInFlatTree::BeforeNode(*input)) |
| .Extend(PositionInFlatTree::AfterNode(*input)) |
| .Build(), |
| TextGranularity::kWord); |
| const SelectionInFlatTree& result = |
| AdjustSelectionWithTrailingWhitespace(selection); |
| |
| EXPECT_EQ(PositionInFlatTree::BeforeNode(*input), |
| result.ComputeStartPosition()); |
| EXPECT_EQ(PositionInFlatTree::AfterNode(*input), result.ComputeEndPosition()); |
| } |
| |
| // For http://crbug.com/974569 |
| TEST_F(SelectionControllerTest, |
| SelectClosestWordFromHitTestResultAtEndOfLine1) { |
| InsertStyleElement("body { margin: 0; padding: 0; font: 10px monospace; }"); |
| SetBodyContent("<pre>(1)\n(2)</pre>"); |
| |
| // Click/Tap after "(1)" |
| HitTestLocation location(IntPoint(40, 10)); |
| HitTestResult result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location); |
| ASSERT_EQ("<pre>(1)|\n(2)</pre>", |
| GetSelectionTextFromBody( |
| SelectionInDOMTree::Builder() |
| .Collapse(GetPositionFromHitTestResult(result)) |
| .Build())); |
| |
| // Select word by mouse |
| EXPECT_TRUE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kMouse)); |
| EXPECT_EQ("<pre>(1)^\n|(2)</pre>", GetSelectionTextFromBody()); |
| |
| // Select word by tap |
| EXPECT_FALSE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kTouch)); |
| EXPECT_EQ("<pre>(1)^\n|(2)</pre>", GetSelectionTextFromBody()) |
| << "selection isn't changed"; |
| } |
| |
| TEST_F(SelectionControllerTest, |
| SelectClosestWordFromHitTestResultAtEndOfLine2) { |
| InsertStyleElement("body { margin: 0; padding: 0; font: 10px monospace; }"); |
| SetBodyContent("<pre>ab:\ncd</pre>"); |
| |
| // Click/Tap after "(1)" |
| HitTestLocation location(IntPoint(40, 10)); |
| HitTestResult result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location); |
| ASSERT_EQ("<pre>ab:|\ncd</pre>", |
| GetSelectionTextFromBody( |
| SelectionInDOMTree::Builder() |
| .Collapse(GetPositionFromHitTestResult(result)) |
| .Build())); |
| |
| // Select word by mouse |
| EXPECT_TRUE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kMouse)); |
| EXPECT_EQ("<pre>ab:^\n|cd</pre>", GetSelectionTextFromBody()); |
| |
| // Select word by tap |
| EXPECT_FALSE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kTouch)); |
| EXPECT_EQ("<pre>ab:^\n|cd</pre>", GetSelectionTextFromBody()) |
| << "selection isn't changed"; |
| } |
| |
| // For http://crbug.com/1092554 |
| TEST_F(SelectionControllerTest, SelectWordToEndOfLine) { |
| LoadAhem(); |
| InsertStyleElement("body { margin: 0; padding: 0; font: 10px/10px Ahem; }"); |
| SetBodyContent("<div>abc def<br/>ghi</div>"); |
| |
| // Select foo |
| blink::WebMouseEvent double_click( |
| blink::WebMouseEvent::Type::kMouseDown, 0, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| double_click.SetFrameScale(1); |
| HitTestLocation location((IntPoint(20, 5))); |
| double_click.button = blink::WebMouseEvent::Button::kLeft; |
| double_click.click_count = 2; |
| HitTestResult result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults(double_click, location, result)); |
| ASSERT_EQ("<div>ab|c def<br>ghi</div>", |
| GetSelectionTextFromBody( |
| SelectionInDOMTree::Builder() |
| .Collapse(GetPositionFromHitTestResult(result)) |
| .Build())); |
| |
| // Select word by mouse |
| EXPECT_TRUE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kMouse)); |
| EXPECT_EQ("<div>^abc| def<br>ghi</div>", GetSelectionTextFromBody()); |
| |
| // Select to end of line |
| blink::WebMouseEvent single_shift_click( |
| blink::WebMouseEvent::Type::kMouseDown, |
| blink::WebInputEvent::Modifiers::kShiftKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| single_shift_click.SetFrameScale(1); |
| HitTestLocation single_click_location((IntPoint(400, 5))); |
| single_shift_click.button = blink::WebMouseEvent::Button::kLeft; |
| single_shift_click.click_count = 1; |
| HitTestResult single_click_result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation( |
| single_click_location); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults(single_shift_click, single_click_location, |
| single_click_result)); |
| EXPECT_EQ("<div>^abc def<br>|ghi</div>", GetSelectionTextFromBody()); |
| } |
| |
| // For http://crbug.com/892750 |
| TEST_F(SelectionControllerTest, SelectWordToEndOfTableCell) { |
| LoadAhem(); |
| InsertStyleElement( |
| "body { margin: 0; padding: 0; font: 10px/10px Ahem; } td {width: " |
| "200px}"); |
| SetBodyContent("<table><td>foo</td><td>bar</td></table>"); |
| |
| // Select foo |
| blink::WebMouseEvent double_click( |
| blink::WebMouseEvent::Type::kMouseDown, 0, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| double_click.SetFrameScale(1); |
| HitTestLocation location((IntPoint(20, 5))); |
| double_click.button = WebMouseEvent::Button::kLeft; |
| double_click.click_count = 2; |
| HitTestResult result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation(location); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults(double_click, location, result)); |
| ASSERT_EQ("<table><tbody><tr><td>fo|o</td><td>bar</td></tr></tbody></table>", |
| GetSelectionTextFromBody( |
| SelectionInDOMTree::Builder() |
| .Collapse(GetPositionFromHitTestResult(result)) |
| .Build())); |
| // Select word by mouse |
| EXPECT_TRUE(SelectClosestWordFromHitTestResult( |
| result, AppendTrailingWhitespace::kDontAppend, |
| SelectInputEventType::kMouse)); |
| EXPECT_EQ("<table><tbody><tr><td>^foo|</td><td>bar</td></tr></tbody></table>", |
| GetSelectionTextFromBody()); |
| |
| // Select to end of cell 1 |
| blink::WebMouseEvent cell1_single_shift_click( |
| blink::WebMouseEvent::Type::kMouseDown, |
| blink::WebInputEvent::Modifiers::kShiftKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| cell1_single_shift_click.SetFrameScale(1); |
| HitTestLocation cell1_single_click_location((IntPoint(175, 5))); |
| cell1_single_shift_click.button = blink::WebMouseEvent::Button::kLeft; |
| cell1_single_shift_click.click_count = 1; |
| HitTestResult cell1_single_click_result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation( |
| cell1_single_click_location); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults(cell1_single_shift_click, |
| cell1_single_click_location, |
| cell1_single_click_result)); |
| EXPECT_EQ("<table><tbody><tr><td>^foo|</td><td>bar</td></tr></tbody></table>", |
| GetSelectionTextFromBody()); |
| |
| // Select to end of cell 2 |
| blink::WebMouseEvent cell2_single_shift_click( |
| blink::WebMouseEvent::Type::kMouseDown, |
| blink::WebInputEvent::Modifiers::kShiftKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| // Frame scale defaults to 0, which would cause a divide-by-zero problem. |
| cell2_single_shift_click.SetFrameScale(1); |
| HitTestLocation cell2_single_click_location((IntPoint(375, 5))); |
| cell2_single_shift_click.button = blink::WebMouseEvent::Button::kLeft; |
| cell2_single_shift_click.click_count = 1; |
| HitTestResult cell2_single_click_result = |
| GetFrame().GetEventHandler().HitTestResultAtLocation( |
| cell2_single_click_location); |
| GetFrame().GetEventHandler().GetSelectionController().HandleMousePressEvent( |
| MouseEventWithHitTestResults(cell2_single_shift_click, |
| cell2_single_click_location, |
| cell2_single_click_result)); |
| EXPECT_EQ("<table><tbody><tr><td>^foo</td><td>bar|</td></tr></tbody></table>", |
| GetSelectionTextFromBody()); |
| } |
| |
| TEST_P(ParameterizedSelectionControllerTest, Scroll) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| html, body { |
| margin: 0; |
| font-size: 50px; |
| line-height: 2; |
| } |
| #scroller { |
| width: 400px; |
| height: 600px; |
| overflow: scroll; |
| } |
| </style> |
| <div id="scroller"> |
| <span>line1</span><br> |
| <span>line2</span><br> |
| <span>line3</span><br> |
| <span>line4</span><br> |
| <span style="padding-left: 100px">line5</span><br> |
| <span style="border-left: 100px solid blue">line6</span><br> |
| <span style="margin-left: 100px">line7</span><br> |
| <span style="display: inline-block; width: 100px; height: 1em; line-height: 1">x</span>line8<br> |
| <span>line9</span><br> |
| </div> |
| )HTML"); |
| |
| // Scroll #scroller by 2 lines. "line3" should be at the top. |
| Element* scroller = GetElementById("scroller"); |
| scroller->setScrollTop(200); |
| |
| // Hit-test on the first visible line. This should be "line3". |
| HitTestResult line3_result = HitTestResultAtLocation(5, 50); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line3_result.LocalPoint(), PhysicalOffset(5, 50)); |
| PositionWithAffinity line3 = line3_result.GetPosition(); |
| Node* line3_node = line3.AnchorNode(); |
| EXPECT_EQ(line3_node->nodeName(), "#text"); |
| EXPECT_EQ(line3_node->textContent(), "line3"); |
| |
| // Then hit-test beyond the end of the first visible line. This should snap to |
| // the end of the "line3". |
| // |
| // +------------ |
| // |line3 x <-- Click here |
| // |line4 |
| HitTestResult line3_end_result = HitTestResultAtLocation(300, 50); |
| EXPECT_EQ(line3_end_result.LocalPoint(), PhysicalOffset(300, 50)); |
| PositionWithAffinity line3_end = line3_end_result.GetPosition(); |
| Node* line3_end_node = line3_end.AnchorNode(); |
| EXPECT_EQ(line3_end_node->nodeName(), "#text"); |
| EXPECT_EQ(line3_end_node->textContent(), "line3"); |
| |
| // At the line-gap between line3 and line4. |
| // There is no |LayoutText| here, but it should snap to line4. |
| HitTestResult line4_over_result = HitTestResultAtLocation(5, 101); |
| EXPECT_EQ(line4_over_result.LocalPoint(), PhysicalOffset(5, 101)); |
| PositionWithAffinity line4_over = line4_over_result.GetPosition(); |
| Node* line4_over_node = line4_over.AnchorNode(); |
| EXPECT_EQ(line4_over_node->nodeName(), "#text"); |
| EXPECT_EQ(line4_over_node->textContent(), "line4"); |
| |
| // At the padding of an inline box. |
| HitTestResult line5_result = HitTestResultAtLocation(5, 250); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line5_result.LocalPoint(), PhysicalOffset(5, 250)); |
| PositionWithAffinity line5 = line5_result.GetPosition(); |
| Node* line5_node = line5.AnchorNode(); |
| EXPECT_EQ(line5_node->nodeName(), "#text"); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line5_node->textContent(), "line5"); |
| |
| // At the border of an inline box. |
| HitTestResult line6_result = HitTestResultAtLocation(5, 350); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line6_result.LocalPoint(), PhysicalOffset(5, 350)); |
| PositionWithAffinity line6 = line6_result.GetPosition(); |
| Node* line6_node = line6.AnchorNode(); |
| EXPECT_EQ(line6_node->nodeName(), "#text"); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line6_node->textContent(), "line6"); |
| |
| // At the margin of an inline box. |
| HitTestResult line7_result = HitTestResultAtLocation(5, 450); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line7_result.LocalPoint(), PhysicalOffset(5, 450)); |
| PositionWithAffinity line7 = line7_result.GetPosition(); |
| Node* line7_node = line7.AnchorNode(); |
| EXPECT_EQ(line7_node->nodeName(), "#text"); |
| if (LayoutNGEnabled()) // Legacy fails this test. |
| EXPECT_EQ(line7_node->textContent(), "line7"); |
| |
| // At the inline-block. |
| HitTestResult line8_result = HitTestResultAtLocation(5, 550); |
| EXPECT_EQ(line8_result.LocalPoint(), PhysicalOffset(5, 25)); |
| PositionWithAffinity line8 = line8_result.GetPosition(); |
| Node* line8_node = line8.AnchorNode(); |
| EXPECT_EQ(line8_node->nodeName(), "#text"); |
| EXPECT_EQ(line8_node->textContent(), "x"); |
| } |
| |
| } // namespace blink |