// Copyright 2017 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/compute_layer_selection.h" #include "build/build_config.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/editing/position_with_affinity.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/frame/settings.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/html/forms/text_control_element.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/testing/use_mock_scrollbar_settings.h" namespace blink { class ComputeLayerSelectionTest : public EditingTestBase { public: void SetUp() override { EditingTestBase::SetUp(); // This Page is not actually being shown by a compositor, but we act like it // will in order to test behaviour. GetPage().GetSettings().SetAcceleratedCompositingEnabled(true); GetDocument().View()->SetParentVisible(true); GetDocument().View()->SetSelfVisible(true); LoadAhem(); } void FocusAndSelectAll(Element* focus, const Node& select) { DCHECK(focus); focus->focus(); Selection().SetSelection( SelectionInDOMTree::Builder().SelectAllChildren(select).Build(), SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); UpdateAllLifecyclePhasesForTest(); } void FocusAndSelectAll(TextControlElement* target) { FocusAndSelectAll(target, *target->InnerEditorElement()); } private: UseMockScrollbarSettings mock_scrollbars_; }; TEST_F(ComputeLayerSelectionTest, ComputeLayerSelection) { SetBodyContent(R"HTML( )HTML"); FocusAndSelectAll(ToHTMLInputElement(GetDocument().getElementById("target"))); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_FALSE(composited_selection.start.hidden); EXPECT_TRUE(composited_selection.end.hidden); } TEST_F(ComputeLayerSelectionTest, DontCrashOnLayerCreation) { SetBodyContent(R"HTML( )HTML"); Element* target = GetDocument().getElementById("target"); FocusAndSelectAll(ToHTMLInputElement(target)); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_FALSE(composited_selection.start.hidden); EXPECT_TRUE(composited_selection.end.hidden); target->setAttribute(html_names::kStyleAttr, "will-change: transform"); UpdateAllLifecyclePhasesForTest(); // Passes if no crash. } TEST_F(ComputeLayerSelectionTest, PositionInScrollableRoot) { SetBodyContent(R"HTML( )HTML"); FocusAndSelectAll(ToHTMLInputElement(GetDocument().getElementById("target"))); ScrollableArea* root_scroller = GetDocument().View()->GetScrollableArea(); root_scroller->SetScrollOffset(ScrollOffset(800, 500), kProgrammaticScroll); ASSERT_EQ(ScrollOffset(800, 500), root_scroller->GetScrollOffset()); UpdateAllLifecyclePhasesForTest(); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); // Top-left corner should be around (1000, 905) - 10px centered in 20px // height. EXPECT_EQ(gfx::Point(1000, 905), composited_selection.start.edge_top); EXPECT_EQ(gfx::Point(1000, 915), composited_selection.start.edge_bottom); EXPECT_EQ(gfx::Point(1369, 905), composited_selection.end.edge_top); EXPECT_EQ(gfx::Point(1369, 915), composited_selection.end.edge_bottom); } TEST_F(ComputeLayerSelectionTest, PositionInScroller) { SetBodyContent(R"HTML(
)HTML"); FocusAndSelectAll(ToHTMLInputElement(GetDocument().getElementById("target"))); Element* e = GetDocument().getElementById("scroller"); PaintLayerScrollableArea* scroller = ToLayoutBox(e->GetLayoutObject())->GetScrollableArea(); scroller->SetScrollOffset(ScrollOffset(900, 800), kProgrammaticScroll); ASSERT_EQ(ScrollOffset(900, 800), scroller->GetScrollOffset()); UpdateAllLifecyclePhasesForTest(); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); // Top-left corner should be around (1000, 905) - 10px centered in 20px // height. EXPECT_EQ(gfx::Point(1000, 905), composited_selection.start.edge_top); EXPECT_EQ(gfx::Point(1000, 915), composited_selection.start.edge_bottom); EXPECT_EQ(gfx::Point(1369, 905), composited_selection.end.edge_top); EXPECT_EQ(gfx::Point(1369, 915), composited_selection.end.edge_bottom); } // crbug.com/807930 TEST_F(ComputeLayerSelectionTest, ContentEditableLinebreak) { SetBodyContent( "
" "test

"); Element* target = GetDocument().QuerySelector("div"); FocusAndSelectAll(target, *target); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(composited_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(composited_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(composited_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(composited_selection.end.edge_bottom, gfx::Point(8, 28)); } // crbug.com/807930 TEST_F(ComputeLayerSelectionTest, TextAreaLinebreak) { SetBodyContent( ""); FocusAndSelectAll(ToTextControl(GetDocument().QuerySelector("textarea"))); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(composited_selection.start.edge_top, gfx::Point(11, 11)); EXPECT_EQ(composited_selection.start.edge_bottom, gfx::Point(11, 21)); EXPECT_EQ(composited_selection.end.edge_top, gfx::Point(11, 21)); EXPECT_EQ(composited_selection.end.edge_bottom, gfx::Point(11, 31)); } // crbug.com/815099 TEST_F(ComputeLayerSelectionTest, CaretBeforeSoftWrap) { SetBodyContent( "
foo
"); Element* target = GetDocument().QuerySelector("div"); target->focus(); Node* text_foo = target->firstChild(); Selection().SetSelection( SelectionInDOMTree::Builder() .Collapse( PositionWithAffinity({text_foo, 2}, TextAffinity::kUpstream)) .Build(), SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); UpdateAllLifecyclePhasesForTest(); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(composited_selection.start.edge_top, gfx::Point(27, 8)); EXPECT_EQ(composited_selection.start.edge_bottom, gfx::Point(27, 18)); EXPECT_EQ(composited_selection.end.edge_top, gfx::Point(27, 8)); EXPECT_EQ(composited_selection.end.edge_bottom, gfx::Point(27, 18)); } TEST_F(ComputeLayerSelectionTest, CaretAfterSoftWrap) { SetBodyContent( "
foo
"); Element* target = GetDocument().QuerySelector("div"); target->focus(); Node* text_foo = target->firstChild(); Selection().SetSelection( SelectionInDOMTree::Builder() .Collapse( PositionWithAffinity({text_foo, 2}, TextAffinity::kDownstream)) .Build(), SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); UpdateAllLifecyclePhasesForTest(); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(composited_selection.start.edge_top, gfx::Point(8, 18)); EXPECT_EQ(composited_selection.start.edge_bottom, gfx::Point(8, 28)); EXPECT_EQ(composited_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(composited_selection.end.edge_bottom, gfx::Point(8, 28)); } // crbug.com/834686 TEST_F(ComputeLayerSelectionTest, RangeBeginAtBlockEnd) { const SelectionInDOMTree& selection = SetSelectionTextToBody( "
" "
foo\n^
ba|r
"); Selection().SetSelection( selection, SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); Element* target = GetDocument().QuerySelector("div"); target->focus(); UpdateAllLifecyclePhasesForTest(); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(composited_selection.start.edge_top, gfx::Point(38, 8)); EXPECT_EQ(composited_selection.start.edge_bottom, gfx::Point(38, 18)); EXPECT_EQ(composited_selection.end.edge_top, gfx::Point(28, 18)); EXPECT_EQ(composited_selection.end.edge_bottom, gfx::Point(28, 28)); } TEST_F(ComputeLayerSelectionTest, BlockEndBR1) { // LayerSelection should be: // ^test
// |
SetBodyContent( "
" "test

"); Element* target = GetDocument().QuerySelector("div"); FocusAndSelectAll(target, *target); const cc::LayerSelection& layer_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(layer_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(layer_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_bottom, gfx::Point(8, 28)); } TEST_F(ComputeLayerSelectionTest, BlockEndBR2) { // LayerSelection should be: // ^test
// |
SetBodyContent( "
" "
test

"); Element* target = GetDocument().QuerySelector("div"); FocusAndSelectAll(target, *target); const cc::LayerSelection& layer_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(layer_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(layer_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_bottom, gfx::Point(8, 28)); } TEST_F(ComputeLayerSelectionTest, BlockEndBR3) { // LayerSelection should be: // ^test
// |
SetBodyContent( "
" "
test

"); Element* target = GetDocument().QuerySelector("div"); FocusAndSelectAll(target, *target); const cc::LayerSelection& layer_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(layer_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(layer_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_bottom, gfx::Point(8, 28)); } // crbug.com/889799. Checking when edge_bottom on box boundary, bound is still // visible. TEST_F(ComputeLayerSelectionTest, SamplePointOnBoundary) { SetBodyContent(R"HTML( )HTML"); GetDocument().GetFrame()->SetPageZoomFactor(2.625); FocusAndSelectAll(ToHTMLInputElement(GetDocument().getElementById("target"))); const cc::LayerSelection& composited_selection = ComputeLayerSelection(Selection()); EXPECT_FALSE(composited_selection.start.hidden); EXPECT_FALSE(composited_selection.end.hidden); } // https://crbug.com/892584. TEST_F(ComputeLayerSelectionTest, CrossingBlock1) { // TODO(yoichio): To support this case with ComputeLayoutSelection, // we may need to fix LocalCaretRectOfPosition(). Selection().SetSelection( SetSelectionTextToBody("
" "
^
" "
|
" "
"), SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); Selection().CommitAppearanceIfNeeded(); const cc::LayerSelection& layer_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(layer_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(layer_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_bottom, gfx::Point(8, 28)); } // https://crbug.com/892584. TEST_F(ComputeLayerSelectionTest, CrossingBlock2) { // TODO(yoichio): To support this case with ComputeLayoutSelection, // we may need to fix LocalCaretRectOfPosition(). Selection().SetSelection( SetSelectionTextToBody( "
" "
^
" "
|
" "
"), SetSelectionOptions::Builder().SetShouldShowHandle(true).Build()); Selection().CommitAppearanceIfNeeded(); const cc::LayerSelection& layer_selection = ComputeLayerSelection(Selection()); EXPECT_EQ(layer_selection.start.edge_top, gfx::Point(8, 8)); EXPECT_EQ(layer_selection.start.edge_bottom, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_top, gfx::Point(8, 18)); EXPECT_EQ(layer_selection.end.edge_bottom, gfx::Point(8, 28)); } } // namespace blink