Commit ae1f178f authored by Oriol Brufau's avatar Oriol Brufau Committed by Chromium LUCI CQ

[editing] Avoid CHECK failure in CreateMarkupAlgorithm::CreateMarkup

CompositeEditCommand::MoveParagraphs has a start and end positions, and
constrains them with Most{Forward,Backward}CaretPosition. The problem
was that the resulting positions could end up reversed, triggering a
CHECK failure in CreateMarkup.

This patch changes CompositeEditCommand::MoveParagraphs so that it only
attempts to contrain the positions if they are different, and also
changes MostBackwardOrForwardCaretPosition to ensure that the shadow-
adjusted candidate is not further from the initial position than the
non-adjusted candidate.

Bug: 1165325

TEST=CompositeEditCommandTest.MoveParagraphContentsToNewBlockWithUAShadowDOM
TEST=SelectionAdjusterTest.AdjustSelectionTypeWithShadow
TEST=VisibleSelectionTest.ShadowCrossing
TEST=VisibleSelectionTest.ShadowNested

Change-Id: Ib52ed472809e15aa551fdfc4ed1fa2323433d4cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2627368
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843406}
parent a64ab567
...@@ -1503,13 +1503,19 @@ void CompositeEditCommand::MoveParagraphs( ...@@ -1503,13 +1503,19 @@ void CompositeEditCommand::MoveParagraphs(
NextPositionOf(end_of_paragraph_to_move, kCannotCrossEditingBoundary) NextPositionOf(end_of_paragraph_to_move, kCannotCrossEditingBoundary)
.DeepEquivalent()); .DeepEquivalent());
const Position& start_candidate = start_of_paragraph_to_move.DeepEquivalent();
const Position& end_candidate = end_of_paragraph_to_move.DeepEquivalent();
DCHECK_LE(start_candidate, end_candidate);
// We upstream() the end and downstream() the start so that we don't include // We upstream() the end and downstream() the start so that we don't include
// collapsed whitespace in the move. When we paste a fragment, spaces after // collapsed whitespace in the move. When we paste a fragment, spaces after
// the end and before the start are treated as though they were rendered. // the end and before the start are treated as though they were rendered.
bool equal = start_candidate == end_candidate;
Position start = Position start =
MostForwardCaretPosition(start_of_paragraph_to_move.DeepEquivalent()); equal ? start_candidate : MostForwardCaretPosition(start_candidate);
Position end = Position end =
MostBackwardCaretPosition(end_of_paragraph_to_move.DeepEquivalent()); equal ? end_candidate : MostBackwardCaretPosition(end_candidate);
DCHECK_LE(start, end);
// FIXME: This is an inefficient way to preserve style on nodes in the // FIXME: This is an inefficient way to preserve style on nodes in the
// paragraph to move. It shouldn't matter though, since moved paragraphs will // paragraph to move. It shouldn't matter though, since moved paragraphs will
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/dom/text.h" #include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/testing/editing_test_base.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/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
namespace blink { namespace blink {
...@@ -142,6 +143,23 @@ TEST_F(CompositeEditCommandTest, ...@@ -142,6 +143,23 @@ TEST_F(CompositeEditCommandTest,
body->innerHTML()); body->innerHTML());
} }
TEST_F(CompositeEditCommandTest,
MoveParagraphContentsToNewBlockWithUAShadowDOM) {
SetBodyContent("<object contenteditable><input></object>");
base::RunLoop().RunUntilIdle();
SampleCommand& sample = *MakeGarbageCollected<SampleCommand>(GetDocument());
Element* input = GetDocument().QuerySelector("input");
Position pos = Position::BeforeNode(*input);
EditingState editing_state;
// Should not crash
sample.MoveParagraphContentsToNewBlockIfNecessary(pos, &editing_state);
EXPECT_FALSE(editing_state.IsAborted());
EXPECT_EQ("<object contenteditable=\"\"><div><input></div></object>",
GetDocument().body()->innerHTML());
}
TEST_F(CompositeEditCommandTest, InsertNodeOnDisconnectedParent) { TEST_F(CompositeEditCommandTest, InsertNodeOnDisconnectedParent) {
SetBodyContent("<p><b></b></p>"); SetBodyContent("<p><b></b></p>");
SampleCommand& sample = *MakeGarbageCollected<SampleCommand>(GetDocument()); SampleCommand& sample = *MakeGarbageCollected<SampleCommand>(GetDocument());
......
...@@ -523,17 +523,17 @@ TEST_F(SelectionAdjusterTest, AdjustSelectionTypeWithShadow) { ...@@ -523,17 +523,17 @@ TEST_F(SelectionAdjusterTest, AdjustSelectionTypeWithShadow) {
SetShadowContent("bar<slot></slot>", "host"); SetShadowContent("bar<slot></slot>", "host");
Element* host = GetDocument().getElementById("host"); Element* host = GetDocument().getElementById("host");
const Position& base = Position(host->firstChild(), 0);
const Position& extent = Position(host, 0);
const SelectionInDOMTree& selection = const SelectionInDOMTree& selection =
SelectionInDOMTree::Builder() SelectionInDOMTree::Builder().Collapse(base).Extend(extent).Build();
.Collapse(Position(host->firstChild(), 0))
.Extend(Position(host, 0))
.Build();
// Should not crash // Should not crash
const SelectionInDOMTree& adjusted = const SelectionInDOMTree& adjusted =
SelectionAdjuster::AdjustSelectionType(selection); SelectionAdjuster::AdjustSelectionType(selection);
EXPECT_EQ(Position::BeforeNode(*host), adjusted.Base()); EXPECT_EQ(base, adjusted.Base());
EXPECT_EQ(Position::BeforeNode(*host), adjusted.Extent()); EXPECT_EQ(extent, adjusted.Extent());
} }
} // namespace blink } // namespace blink
...@@ -462,7 +462,7 @@ TEST_F(VisibleSelectionTest, ShadowCrossing) { ...@@ -462,7 +462,7 @@ TEST_F(VisibleSelectionTest, ShadowCrossing) {
EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor), EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor),
selection.Start()); selection.Start());
EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor), selection.End()); EXPECT_EQ(Position(one->firstChild(), 0), selection.End());
EXPECT_EQ(PositionInFlatTree(one->firstChild(), 0), EXPECT_EQ(PositionInFlatTree(one->firstChild(), 0),
selection_in_flat_tree.Start()); selection_in_flat_tree.Start());
EXPECT_EQ(PositionInFlatTree(six->firstChild(), 2), EXPECT_EQ(PositionInFlatTree(six->firstChild(), 2),
...@@ -510,7 +510,7 @@ TEST_F(VisibleSelectionTest, ShadowNested) { ...@@ -510,7 +510,7 @@ TEST_F(VisibleSelectionTest, ShadowNested) {
EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor), EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor),
selection.Start()); selection.Start());
EXPECT_EQ(Position(host, PositionAnchorType::kBeforeAnchor), selection.End()); EXPECT_EQ(Position(one->firstChild(), 0), selection.End());
EXPECT_EQ(PositionInFlatTree(eight->firstChild(), 2), EXPECT_EQ(PositionInFlatTree(eight->firstChild(), 2),
selection_in_flat_tree.Start()); selection_in_flat_tree.Start());
EXPECT_EQ(PositionInFlatTree(eight->firstChild(), 2), EXPECT_EQ(PositionInFlatTree(eight->firstChild(), 2),
......
...@@ -648,6 +648,12 @@ static Position MostBackwardOrForwardCaretPosition( ...@@ -648,6 +648,12 @@ static Position MostBackwardOrForwardCaretPosition(
const SelectionInDOMTree& shadow_adjusted_selection = const SelectionInDOMTree& shadow_adjusted_selection =
SelectionAdjuster::AdjustSelectionToAvoidCrossingShadowBoundaries( SelectionAdjuster::AdjustSelectionToAvoidCrossingShadowBoundaries(
selection); selection);
const Position& adjusted_candidate = shadow_adjusted_selection.Extent();
// The adjusted candidate should be between the candidate and the original
// position. Otherwise, return the original position.
if (position.CompareTo(candidate) == candidate.CompareTo(adjusted_candidate))
return position;
// If we have to adjust the position, the editability may change, so avoid // If we have to adjust the position, the editability may change, so avoid
// crossing editing boundaries if it's not allowed. // crossing editing boundaries if it's not allowed.
...@@ -658,7 +664,7 @@ static Position MostBackwardOrForwardCaretPosition( ...@@ -658,7 +664,7 @@ static Position MostBackwardOrForwardCaretPosition(
shadow_adjusted_selection); shadow_adjusted_selection);
return editing_adjusted_selection.Extent(); return editing_adjusted_selection.Extent();
} }
return shadow_adjusted_selection.Extent(); return adjusted_candidate;
} }
template <typename Strategy> template <typename Strategy>
......
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