Commit 1adbfdec authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

[LayoutNG] Implement InSameLine()

This patch implements the LayoutNG version of InSameLine() on top
of ComputeNGCaretPosition():
1. Use ComputeNGCaretPosition() to find the fragment for a position
2. Traverse up from the fragment to obtain line box
3. Compare line boxes to decide if positions are in the same line

The implementation is simple in concept but inefficient, since
ComputeNGCaretPosition() traverses the whole block flow. Follow up
work will optimize ComputeNGCaretPosition() to reduce traversal.

Bug: 830956
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_layout_ng
Change-Id: I34a7bc19953bd5995b70afa1b2890d15ef8da744
Reviewed-on: https://chromium-review.googlesource.com/1008631
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarYoichi Osato <yoichio@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551119}
parent 7aca60bd
......@@ -11,6 +11,9 @@ crbug.com/789878 fast/css/readwrite-contenteditable.html [ Failure ]
crbug.com/707656 fast/inline-block/14498-positionForCoordinates.html [ Failure ]
crbug.com/789878 fast/inline/inline-focus-ring.html [ Failure ]
# Need support for selection modification by granularity
crbug.com/708452 editing/selection/skip-over-contenteditable.html [ Failure ]
# Non-interoperable behavior not worth to fix.
crbug.com/591099 fast/text/apply-start-width-after-skipped-text.html [ Skip ]
......
......@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/editing/position.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
namespace blink {
......@@ -22,4 +23,10 @@ LocalCaretRect ComputeNGLocalCaretRect(
return ComputeNGLocalCaretRect(ToPositionInDOMTreeWithAffinity(position));
}
bool InSameNGLineBox(const PositionInFlatTreeWithAffinity& position1,
const PositionInFlatTreeWithAffinity& position2) {
return InSameNGLineBox(ToPositionInDOMTreeWithAffinity(position1),
ToPositionInDOMTreeWithAffinity(position2));
}
} // namespace blink
......@@ -20,6 +20,9 @@ const LayoutBlockFlow* NGInlineFormattingContextOf(const PositionInFlatTree&);
LocalCaretRect ComputeNGLocalCaretRect(const PositionInFlatTreeWithAffinity&);
bool InSameNGLineBox(const PositionInFlatTreeWithAffinity&,
const PositionInFlatTreeWithAffinity&);
} // namespace blink
#endif
......@@ -33,11 +33,14 @@
#include "third_party/blink/renderer/core/dom/ax_object_cache.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/inline_box_position.h"
#include "third_party/blink/renderer/core/editing/ng_flat_tree_shorthands.h"
#include "third_party/blink/renderer/core/editing/rendered_position.h"
#include "third_party/blink/renderer/core/editing/visible_position.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
#include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
namespace blink {
......@@ -594,6 +597,23 @@ static bool InSameLineAlgorithm(
DCHECK_EQ(position1.GetDocument(), position2.GetDocument());
DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate());
if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
const LayoutBlockFlow* block1 =
NGInlineFormattingContextOf(position1.GetPosition());
const LayoutBlockFlow* block2 =
NGInlineFormattingContextOf(position2.GetPosition());
if (block1 || block2) {
if (block1 != block2)
return false;
// TODO(editing-dev): We may incorrectly return false if a position is in
// an empty NG block with height, in which case there is no line box. We
// must handle this case when enabling Layout NG for contenteditable.
return InSameNGLineBox(position1, position2);
}
// Neither positions are in LayoutNG. Fall through to legacy handling.
}
PositionWithAffinityTemplate<Strategy> start_of_line1 =
StartOfLine(position1);
PositionWithAffinityTemplate<Strategy> start_of_line2 =
......
......@@ -7,10 +7,12 @@
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/core/dom/text.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/editing/visible_position.h"
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
......@@ -47,6 +49,20 @@ class VisibleUnitsLineTest : public EditingTestBase {
}
};
class ParameterizedVisibleUnitsLineTest
: public ::testing::WithParamInterface<bool>,
private ScopedLayoutNGForTest,
public VisibleUnitsLineTest {
protected:
ParameterizedVisibleUnitsLineTest() : ScopedLayoutNGForTest(GetParam()) {}
bool LayoutNGEnabled() const { return GetParam(); }
};
INSTANTIATE_TEST_CASE_P(All,
ParameterizedVisibleUnitsLineTest,
::testing::Bool());
TEST_F(VisibleUnitsLineTest, endOfLine) {
const char* body_content =
"<a id=host><b id=one>11</b><b id=two>22</b></a><i id=three>333</i><i "
......@@ -220,7 +236,7 @@ TEST_F(VisibleUnitsLineTest, isLogicalEndOfLine) {
EXPECT_TRUE(IsLogicalEndOfLine(CreateVisiblePositionInFlatTree(*seven, 7)));
}
TEST_F(VisibleUnitsLineTest, inSameLine) {
TEST_P(ParameterizedVisibleUnitsLineTest, inSameLine) {
const char* body_content =
"<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>";
const char* shadow_content =
......@@ -599,7 +615,7 @@ TEST_F(VisibleUnitsLineTest,
two->lastChild(), visible_position, kContentIsEditable));
}
TEST_F(VisibleUnitsLineTest, InSameLineSkippingEmptyEditableDiv) {
TEST_P(ParameterizedVisibleUnitsLineTest, InSameLineSkippingEmptyEditableDiv) {
// This test records the InSameLine() results in
// editing/selection/skip-over-contenteditable.html
SetBodyContent(
......@@ -623,4 +639,16 @@ TEST_F(VisibleUnitsLineTest, InSameLineSkippingEmptyEditableDiv) {
PositionWithAffinity(Position(bar, 0), TextAffinity::kDownstream)));
}
TEST_P(ParameterizedVisibleUnitsLineTest, InSameLineWithMixedEditability) {
SelectionInDOMTree selection =
SetSelectionTextToBody("<span contenteditable>f^oo</span>b|ar");
PositionWithAffinity position1(selection.Base());
PositionWithAffinity position2(selection.Extent());
// "Same line" is restricted by editability boundaries.
// TODO(editing-dev): Make sure this test doesn't fail when we stop wrapping
// inline contenteditables with inline blocks.
EXPECT_FALSE(InSameLine(position1, position2));
}
} // namespace blink
......@@ -346,6 +346,8 @@ blink_core_sources("layout") {
"ng/inline/ng_line_breaker.h",
"ng/inline/ng_line_height_metrics.cc",
"ng/inline/ng_line_height_metrics.h",
"ng/inline/ng_line_utils.cc",
"ng/inline/ng_line_utils.h",
"ng/inline/ng_offset_mapping.cc",
"ng/inline/ng_offset_mapping.h",
"ng/inline/ng_offset_mapping_builder.cc",
......
// 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 "third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
namespace blink {
const NGPaintFragment* NGContainingLineBoxOf(
const PositionWithAffinity& position) {
const NGCaretPosition caret_position = ComputeNGCaretPosition(position);
if (caret_position.IsNull())
return nullptr;
return caret_position.fragment->ContainerLineBox();
}
bool InSameNGLineBox(const PositionWithAffinity& position1,
const PositionWithAffinity& position2) {
const NGPaintFragment* line_box1 = NGContainingLineBoxOf(position1);
if (!line_box1)
return false;
const NGPaintFragment* line_box2 = NGContainingLineBoxOf(position2);
return line_box1 == line_box2;
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_
#include "third_party/blink/renderer/core/editing/forward.h"
namespace blink {
class NGPaintFragment;
// Returns the NG line box fragment containing the caret position of the given
// position. Returns false if the position is not in Layout NG, or does not
// have any caret position.
const NGPaintFragment* NGContainingLineBoxOf(const PositionWithAffinity&);
// Returns true if the caret positions of the two positions are in the same NG
// line box. Returns false in all other cases.
bool InSameNGLineBox(const PositionWithAffinity&, const PositionWithAffinity&);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_UTILS_H_
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