Commit 6fb0fe3f authored by David Bokan's avatar David Bokan Committed by Commit Bot

[Reland] Make touch handles relative to scrolling contents

Selection bounds are sent from Blink to CC as part of a commit cycle so
that we can draw touch handles for it. Currently, the selection bounds
are relative to the main graphics layer of a CompositedLayerMapping. In
the case of a scroller, this will be its clip rect - rather than its
scrolling contents layer. Unfortunately, this means that scrolling on
the compositor isn't applied as part of the ToScreen transformation on
the selection bounds so scrolling wont update the selection bounds
location until another Blink commit. This went unnoticed until now
because the root layer was not considered a scroller. The page would
paint into a document-sized layer and the compositor would provide extra
scrolling layers to handle frame scrolling. In this configuration, the
ToScreen transformation from the root layer does include the scroll
offset.

Now that root layer scrolling has been turned on, frame scrolling works
much the same as other scrollers. Thus, this shortcoming is seen on
frame scrolling also.

The solution in this CL is to move the selection bounds rect to be
relative to the scrolling contents layer - if one exists. The ToScreen
transformation done in CC will correctly compensate for any scroll
offset applied in the compositor and touch selection handles stick to
the selection as its scrolled. For the CC side, see
ComputeViewportSelectionBound in layer_tree_impl.cc

Reland Note: This patch makes the RenderedPositionTests run with mock
scrollbars.

TBR=chrishtr@chromium.org

Bug: 812048
Change-Id: Ib9eacd14b75b71b4e0d4f0a9b57416800f9dfb91
Reviewed-on: https://chromium-review.googlesource.com/967001Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Commit-Queue: David Bokan <bokan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543803}
parent 870c2627
......@@ -248,8 +248,21 @@ Position RenderedPosition::PositionAtRightBoundaryOfBiDiRun() const {
PrevLeafChild()->CaretRightmostOffset());
}
// Note: If the layout object has a scrolling contents layer, the selection
// will be relative to that.
static GraphicsLayer* GetGraphicsLayerBacking(
const LayoutObject& layout_object) {
const LayoutBoxModelObject& paint_invalidation_container =
layout_object.ContainerForPaintInvalidation();
DCHECK(paint_invalidation_container.Layer());
if (paint_invalidation_container.Layer()->GetCompositingState() ==
kNotComposited)
return nullptr;
return paint_invalidation_container.Layer()->GraphicsLayerBacking(
&layout_object);
}
// Convert a local point into the coordinate system of backing coordinates.
// Also returns the backing layer if needed.
static FloatPoint LocalToInvalidationBackingPoint(
const LayoutPoint& local_point,
const LayoutObject& layout_object) {
......@@ -270,26 +283,18 @@ static FloatPoint LocalToInvalidationBackingPoint(
PaintLayer::MapPointInPaintInvalidationContainerToBacking(
paint_invalidation_container, container_point);
// Must not use the scrolling contents layer, so pass
// |paintInvalidationContainer|.
if (GraphicsLayer* graphics_layer =
paint_invalidation_container.Layer()->GraphicsLayerBacking(
&paint_invalidation_container))
if (GraphicsLayer* graphics_layer = GetGraphicsLayerBacking(layout_object))
container_point.Move(-graphics_layer->OffsetFromLayoutObject());
return container_point;
}
// Ensure the coordinates are in the scrolling contents space, if the object
// is a scroller.
if (paint_invalidation_container.UsesCompositedScrolling()) {
container_point.Move(paint_invalidation_container.Layer()
->GetScrollableArea()
->GetScrollOffset());
}
static GraphicsLayer* GetGraphicsLayerBacking(
const LayoutObject& layout_object) {
const LayoutBoxModelObject& paint_invalidation_container =
layout_object.ContainerForPaintInvalidation();
DCHECK(paint_invalidation_container.Layer());
if (paint_invalidation_container.Layer()->GetCompositingState() ==
kNotComposited)
return nullptr;
return paint_invalidation_container.Layer()->GraphicsLayerBacking(
&paint_invalidation_container);
return container_point;
}
std::pair<LayoutPoint, LayoutPoint> static GetLocalSelectionStartpoints(
......
......@@ -10,37 +10,59 @@
#include "core/editing/testing/EditingTestBase.h"
#include "core/frame/Settings.h"
#include "core/html/forms/HTMLInputElement.h"
#include "core/layout/LayoutBox.h"
#include "core/paint/PaintLayerScrollableArea.h"
#include "core/paint/compositing/CompositedSelection.h"
#include "platform/testing/UseMockScrollbarSettings.h"
#include "platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class RenderedPositionTest : public EditingTestBase {};
#if defined(OS_ANDROID)
#define MAYBE_ComputeCompositedSelection DISABLED_ComputeCompositedSelection
#else
#define MAYBE_ComputeCompositedSelection ComputeCompositedSelection
#endif
TEST_F(RenderedPositionTest, MAYBE_ComputeCompositedSelection) {
// Enable compositing.
GetPage().GetSettings().SetAcceleratedCompositingEnabled(true);
GetDocument().View()->SetParentVisible(true);
GetDocument().View()->SetSelfVisible(true);
GetDocument().View()->UpdateAllLifecyclePhases();
SetBodyContent(
"<input id=target width=20 value='test test test test test tes tes test'"
"style='width: 100px; height: 20px;'>");
HTMLInputElement* target =
ToHTMLInputElement(GetDocument().getElementById("target"));
DCHECK(target);
target->focus();
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SelectAllChildren(*target->InnerEditorElement())
.Build(),
SetSelectionOptions::Builder().SetShouldShowHandle(true).Build());
UpdateAllLifecyclePhases();
class RenderedPositionTest : public ::testing::WithParamInterface<bool>,
private ScopedRootLayerScrollingForTest,
public EditingTestBase {
public:
RenderedPositionTest() : ScopedRootLayerScrollingForTest(GetParam()) {}
void SetUp() override {
EditingTestBase::SetUp();
GetPage().GetSettings().SetAcceleratedCompositingEnabled(true);
GetDocument().View()->SetParentVisible(true);
GetDocument().View()->SetSelfVisible(true);
LoadAhem();
}
void FocusAndSelectAll() {
HTMLInputElement* target =
ToHTMLInputElement(GetDocument().getElementById("target"));
DCHECK(target);
target->focus();
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SelectAllChildren(*target->InnerEditorElement())
.Build(),
SetSelectionOptions::Builder().SetShouldShowHandle(true).Build());
UpdateAllLifecyclePhases();
}
private:
UseMockScrollbarSettings mock_scrollbars_;
};
INSTANTIATE_TEST_CASE_P(All, RenderedPositionTest, ::testing::Bool());
TEST_P(RenderedPositionTest, ComputeCompositedSelection) {
SetBodyContent(R"HTML(
<!DOCTYPE html>
input {
font: 10px/1 Ahem;
padding: 0;
border: 0;
}
<input id=target width=20 value='test test test test test tes tes test'
style='width: 100px; height: 20px;'>
)HTML");
FocusAndSelectAll();
const CompositedSelection& composited_selection =
RenderedPosition::ComputeCompositedSelection(Selection());
......@@ -48,4 +70,115 @@ TEST_F(RenderedPositionTest, MAYBE_ComputeCompositedSelection) {
EXPECT_TRUE(composited_selection.end.hidden);
}
TEST_P(RenderedPositionTest, PositionInScrollableRoot) {
SetBodyContent(R"HTML(
<!DOCTYPE html>
<style>
body {
margin: 0;
height: 2000px;
width: 2000px;
}
input {
font: 10px/1 Ahem;
padding: 0;
border: 0;
width: 100px;
height: 20px;
position: absolute;
top: 900px;
left: 1000px;
}
</style>
<input id=target width=20 value='test test test test test tes tes test'>
)HTML");
FocusAndSelectAll();
ScrollableArea* root_scroller = GetDocument().View()->GetScrollableArea();
root_scroller->SetScrollOffset(ScrollOffset(800, 500), kProgrammaticScroll);
ASSERT_EQ(ScrollOffset(800, 500), root_scroller->GetScrollOffset());
UpdateAllLifecyclePhases();
const CompositedSelection& composited_selection =
RenderedPosition::ComputeCompositedSelection(Selection());
// Top-left corner should be around (1000, 905) - 10px centered in 20px
// height.
EXPECT_EQ(FloatPoint(1000, 905),
composited_selection.start.edge_top_in_layer);
EXPECT_EQ(FloatPoint(1000, 915),
composited_selection.start.edge_bottom_in_layer);
EXPECT_EQ(FloatPoint(1369, 905), composited_selection.end.edge_top_in_layer);
EXPECT_EQ(FloatPoint(1369, 915),
composited_selection.end.edge_bottom_in_layer);
}
TEST_P(RenderedPositionTest, PositionInScroller) {
SetBodyContent(R"HTML(
<!DOCTYPE html>
<style>
body {
margin: 0;
height: 2000px;
width: 2000px;
}
input {
font: 10px/1 Ahem;
padding: 0;
border: 0;
width: 100px;
height: 20px;
position: absolute;
top: 900px;
left: 1000px;
}
#scroller {
width: 300px;
height: 300px;
position: absolute;
left: 300px;
top: 400px;
overflow: scroll;
border: 200px;
will-change: transform;
}
#space {
width: 2000px;
height: 2000px;
}
</style>
<div id="scroller">
<div id="space"></div>
<input id=target width=20 value='test test test test test tes tes test'>
</div>
)HTML");
FocusAndSelectAll();
Element* e = GetDocument().getElementById("scroller");
PaintLayerScrollableArea* scroller =
ToLayoutBox(e->GetLayoutObject())->GetScrollableArea();
scroller->SetScrollOffset(ScrollOffset(900, 800), kProgrammaticScroll);
ASSERT_EQ(ScrollOffset(900, 800), scroller->GetScrollOffset());
UpdateAllLifecyclePhases();
const CompositedSelection& composited_selection =
RenderedPosition::ComputeCompositedSelection(Selection());
// Top-left corner should be around (1000, 905) - 10px centered in 20px
// height.
EXPECT_EQ(FloatPoint(1000, 905),
composited_selection.start.edge_top_in_layer);
EXPECT_EQ(FloatPoint(1000, 915),
composited_selection.start.edge_bottom_in_layer);
EXPECT_EQ(FloatPoint(1369, 905), composited_selection.end.edge_top_in_layer);
EXPECT_EQ(FloatPoint(1369, 915),
composited_selection.end.edge_bottom_in_layer);
}
} // namespace blink
......@@ -6332,11 +6332,7 @@ class CompositedSelectionBoundsTest
blink::Node* layer_owner_node_for_start = V8Node::ToImplWithTypeCheck(
v8::Isolate::GetCurrent(), expected_result.Get(0));
ASSERT_TRUE(layer_owner_node_for_start);
EXPECT_EQ(layer_owner_node_for_start->GetLayoutObject()
->EnclosingLayer()
->EnclosingLayerForPaintInvalidation()
->GetCompositedLayerMapping()
->MainGraphicsLayer()
EXPECT_EQ(GetExpectedLayerForSelection(layer_owner_node_for_start)
->PlatformLayer()
->Id(),
select_start->layer_id);
......@@ -6351,11 +6347,7 @@ class CompositedSelectionBoundsTest
expected_result.Get(context, 5).ToLocalChecked());
ASSERT_TRUE(layer_owner_node_for_end);
EXPECT_EQ(layer_owner_node_for_end->GetLayoutObject()
->EnclosingLayer()
->EnclosingLayerForPaintInvalidation()
->GetCompositedLayerMapping()
->MainGraphicsLayer()
EXPECT_EQ(GetExpectedLayerForSelection(layer_owner_node_for_end)
->PlatformLayer()
->Id(),
select_end->layer_id);
......@@ -6407,6 +6399,18 @@ class CompositedSelectionBoundsTest
RunTest(test_file);
}
GraphicsLayer* GetExpectedLayerForSelection(blink::Node* node) const {
CompositedLayerMapping* clm = node->GetLayoutObject()
->EnclosingLayer()
->EnclosingLayerForPaintInvalidation()
->GetCompositedLayerMapping();
// If the Node is a scroller, the selection will be relative to its
// scrolling contents layer.
return clm->ScrollingContentsLayer() ? clm->ScrollingContentsLayer()
: clm->MainGraphicsLayer();
}
CompositedSelectionBoundsTestWebViewClient fake_selection_web_view_client_;
CompositedSelectionBoundsTestLayerTreeView& fake_selection_layer_tree_view_;
FrameTestHelpers::WebViewHelper web_view_helper_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment