Commit 8be3b1a3 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Make CharacterIterator::GetPosition{Before,After}() not to crash with block content

This patch introduces |TextIterator::GetPosition{Before,After}()| as
implementation of |CharacterIterator::GetPosition{Before,After}()| not to crash
with block content.

This is follow-up of the patch[1].

[1] http://crrev.com/c/1021430 Make CharacterIterator::GetPosition{After,Before}()
to work with BR

Change-Id: I0e5aae4cef69d6c1d902cbeb86e8e3552968c280
Reviewed-on: https://chromium-review.googlesource.com/1023502
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarXiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#557103}
parent 330aa519
...@@ -90,37 +90,13 @@ int CharacterIteratorAlgorithm<Strategy>::EndOffset() const { ...@@ -90,37 +90,13 @@ int CharacterIteratorAlgorithm<Strategy>::EndOffset() const {
template <typename Strategy> template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>
CharacterIteratorAlgorithm<Strategy>::GetPositionBefore() const { CharacterIteratorAlgorithm<Strategy>::GetPositionBefore() const {
if (text_iterator_.AtEnd()) { return text_iterator_.GetPositionBefore(run_offset_);
DCHECK_EQ(run_offset_, 0);
return PositionTemplate<Strategy>(
text_iterator_.CurrentContainer(),
text_iterator_.StartOffsetInCurrentContainer());
}
const Node& node = *text_iterator_.GetNode();
DCHECK_GE(text_iterator_.length(), 1);
if (node.IsTextNode()) {
const int offset = text_iterator_.StartOffsetInCurrentContainer();
return PositionTemplate<Strategy>(node, offset + run_offset_);
}
return PositionTemplate<Strategy>::BeforeNode(node);
} }
template <typename Strategy> template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>
CharacterIteratorAlgorithm<Strategy>::GetPositionAfter() const { CharacterIteratorAlgorithm<Strategy>::GetPositionAfter() const {
if (text_iterator_.AtEnd()) { return text_iterator_.GetPositionAfter(run_offset_);
DCHECK_EQ(run_offset_, 0);
return PositionTemplate<Strategy>(
text_iterator_.CurrentContainer(),
text_iterator_.EndOffsetInCurrentContainer());
}
DCHECK_GE(text_iterator_.length(), 1);
const Node& node = *text_iterator_.GetNode();
if (node.IsTextNode()) {
const int offset = text_iterator_.StartOffsetInCurrentContainer();
return PositionTemplate<Strategy>(node, offset + run_offset_ + 1);
}
return PositionTemplate<Strategy>::AfterNode(node);
} }
template <typename Strategy> template <typename Strategy>
......
...@@ -72,6 +72,107 @@ TEST_F(CharacterIteratorTest, CollapsedSubrange) { ...@@ -72,6 +72,107 @@ TEST_F(CharacterIteratorTest, CollapsedSubrange) {
EXPECT_EQ(Position(text_node, 3), result.EndPosition()); EXPECT_EQ(Position(text_node, 3), result.EndPosition());
} }
TEST_F(CharacterIteratorTest, GetPositionWithBlock) {
SetBodyContent("a<div>b</div>c");
const Element& body = *GetDocument().body();
CharacterIterator it(EphemeralRange::RangeOfContents(body));
const Node& text_a = *body.firstChild();
const Node& div = *text_a.nextSibling();
const Node& text_b = *div.firstChild();
const Node& text_c = *body.lastChild();
EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_a, 0), it.StartPosition());
EXPECT_EQ(Position(text_a, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position::BeforeNode(div), it.GetPositionBefore());
EXPECT_EQ(Position::BeforeNode(div), it.GetPositionAfter());
EXPECT_EQ(Position(body, 1), it.StartPosition());
EXPECT_EQ(Position(body, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_b, 0), it.StartPosition());
EXPECT_EQ(Position(text_b, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
EXPECT_EQ(Position(div, 1), it.StartPosition());
EXPECT_EQ(Position(div, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_c, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_c, 0), it.StartPosition());
EXPECT_EQ(Position(text_c, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(body, 3), it.GetPositionBefore());
EXPECT_EQ(Position(body, 3), it.GetPositionAfter());
EXPECT_EQ(Position(body, 3), it.StartPosition());
EXPECT_EQ(Position(body, 3), it.EndPosition());
EXPECT_TRUE(it.AtEnd());
}
TEST_F(CharacterIteratorTest, GetPositionWithBlocks) {
SetBodyContent("<p id=a>b</p><p id=c>d</p>");
const Element& body = *GetDocument().body();
CharacterIterator it(EphemeralRange::RangeOfContents(body));
const Node& element_p_a = *GetDocument().getElementById("a");
const Node& text_b = *element_p_a.firstChild();
const Node& element_p_c = *GetDocument().getElementById("c");
const Node& text_d = *element_p_c.firstChild();
EXPECT_EQ(Position(text_b, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_b, 0), it.StartPosition());
EXPECT_EQ(Position(text_b, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
EXPECT_EQ(Position(element_p_a, 1), it.StartPosition());
EXPECT_EQ(Position(element_p_a, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
EXPECT_EQ(Position(element_p_a, 1), it.StartPosition());
EXPECT_EQ(Position(element_p_a, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_d, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_d, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_d, 0), it.StartPosition());
EXPECT_EQ(Position(text_d, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(body, 2), it.GetPositionBefore());
EXPECT_EQ(Position(body, 2), it.GetPositionAfter());
EXPECT_EQ(Position(body, 2), it.StartPosition());
EXPECT_EQ(Position(body, 2), it.EndPosition());
EXPECT_TRUE(it.AtEnd());
}
TEST_F(CharacterIteratorTest, GetPositionWithBR) { TEST_F(CharacterIteratorTest, GetPositionWithBR) {
SetBodyContent("a<br>b"); SetBodyContent("a<br>b");
...@@ -111,4 +212,115 @@ TEST_F(CharacterIteratorTest, GetPositionWithBR) { ...@@ -111,4 +212,115 @@ TEST_F(CharacterIteratorTest, GetPositionWithBR) {
EXPECT_TRUE(it.AtEnd()); EXPECT_TRUE(it.AtEnd());
} }
TEST_F(CharacterIteratorTest, GetPositionWithCollapsedWhitespaces) {
SetBodyContent("a <div> b </div> c");
const Element& body = *GetDocument().body();
CharacterIterator it(EphemeralRange::RangeOfContents(body));
const Node& text_a = *body.firstChild();
const Node& div = *text_a.nextSibling();
const Node& text_b = *div.firstChild();
const Node& text_c = *body.lastChild();
EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_a, 0), it.StartPosition());
EXPECT_EQ(Position(text_a, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position::BeforeNode(div), it.GetPositionBefore());
EXPECT_EQ(Position::BeforeNode(div), it.GetPositionAfter());
EXPECT_EQ(Position(body, 1), it.StartPosition());
EXPECT_EQ(Position(body, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 2), it.GetPositionAfter());
EXPECT_EQ(Position(text_b, 1), it.StartPosition());
EXPECT_EQ(Position(text_b, 2), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_b, 3), it.GetPositionBefore());
EXPECT_EQ(Position(text_b, 3), it.GetPositionAfter());
EXPECT_EQ(Position(div, 1), it.StartPosition());
EXPECT_EQ(Position(div, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_c, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_c, 2), it.GetPositionAfter());
EXPECT_EQ(Position(text_c, 1), it.StartPosition());
EXPECT_EQ(Position(text_c, 2), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(body, 3), it.GetPositionBefore());
EXPECT_EQ(Position(body, 3), it.GetPositionAfter());
EXPECT_EQ(Position(body, 3), it.StartPosition());
EXPECT_EQ(Position(body, 3), it.EndPosition());
EXPECT_TRUE(it.AtEnd());
}
TEST_F(CharacterIteratorTest, GetPositionWithEmitChar16Before) {
InsertStyleElement("b { white-space: pre; }");
SetBodyContent("a <b> c</b>");
const Element& body = *GetDocument().body();
CharacterIterator it(EphemeralRange::RangeOfContents(body));
const Node& text_a = *body.firstChild();
const Node& element_b = *text_a.nextSibling();
const Node& text_c = *element_b.firstChild();
EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_a, 0), it.StartPosition());
EXPECT_EQ(Position(text_a, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_a, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_a, 2), it.GetPositionAfter());
EXPECT_EQ(Position(text_a, 1), it.StartPosition());
EXPECT_EQ(Position(text_a, 2), it.EndPosition());
// TODO(editing-dev): We should know why we emit a space character for
// "white-space: pre" element after trailing whitespace.
// A space character emitted by |EmitChar16Before()|.
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_c, 0), it.GetPositionAfter());
EXPECT_EQ(Position(text_c, 0), it.StartPosition());
EXPECT_EQ(Position(text_c, 0), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
EXPECT_EQ(Position(text_c, 1), it.GetPositionAfter());
EXPECT_EQ(Position(text_c, 0), it.StartPosition());
EXPECT_EQ(Position(text_c, 1), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(text_c, 1), it.GetPositionBefore());
EXPECT_EQ(Position(text_c, 2), it.GetPositionAfter());
EXPECT_EQ(Position(text_c, 1), it.StartPosition());
EXPECT_EQ(Position(text_c, 2), it.EndPosition());
ASSERT_FALSE(it.AtEnd());
it.Advance(1);
EXPECT_EQ(Position(body, 2), it.GetPositionBefore());
EXPECT_EQ(Position(body, 2), it.GetPositionAfter());
EXPECT_EQ(Position(body, 2), it.StartPosition());
EXPECT_EQ(Position(body, 2), it.EndPosition());
EXPECT_TRUE(it.AtEnd());
}
} // namespace blink } // namespace blink
...@@ -885,6 +885,64 @@ void TextIteratorAlgorithm<Strategy>::EnsurePositionContainer() const { ...@@ -885,6 +885,64 @@ void TextIteratorAlgorithm<Strategy>::EnsurePositionContainer() const {
text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node)); text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node));
} }
template <typename Strategy>
PositionTemplate<Strategy> TextIteratorAlgorithm<Strategy>::GetPositionBefore(
int char16_offset) const {
if (AtEnd()) {
DCHECK_EQ(char16_offset, 0);
return PositionTemplate<Strategy>(CurrentContainer(),
StartOffsetInCurrentContainer());
}
DCHECK_GE(char16_offset, 0);
DCHECK_LT(char16_offset, length());
DCHECK_GE(length(), 1);
const Node& node = *text_state_.PositionNode();
if (text_state_.IsInTextNode() || text_state_.IsBeforeCharacter()) {
return PositionTemplate<Strategy>(
node, text_state_.PositionStartOffset() + char16_offset);
}
if (node.IsTextNode()) {
if (text_state_.IsAfterPositionNode())
return PositionTemplate<Strategy>(node, ToText(node).length());
return PositionTemplate<Strategy>(node, 0);
}
if (text_state_.IsAfterPositionNode())
return PositionTemplate<Strategy>::AfterNode(node);
DCHECK(!text_state_.IsBeforeChildren());
return PositionTemplate<Strategy>::BeforeNode(node);
}
template <typename Strategy>
PositionTemplate<Strategy> TextIteratorAlgorithm<Strategy>::GetPositionAfter(
int char16_offset) const {
if (AtEnd()) {
DCHECK_EQ(char16_offset, 0);
return PositionTemplate<Strategy>(CurrentContainer(),
EndOffsetInCurrentContainer());
}
DCHECK_GE(char16_offset, 0);
DCHECK_LT(char16_offset, length());
DCHECK_GE(length(), 1);
const Node& node = *text_state_.PositionNode();
if (text_state_.IsBeforeCharacter()) {
return PositionTemplate<Strategy>(
node, text_state_.PositionStartOffset() + char16_offset);
}
if (text_state_.IsInTextNode()) {
return PositionTemplate<Strategy>(
node, text_state_.PositionStartOffset() + char16_offset + 1);
}
if (node.IsTextNode()) {
if (text_state_.IsBeforePositionNode())
return PositionTemplate<Strategy>(node, 0);
return PositionTemplate<Strategy>(node, ToText(node).length());
}
if (text_state_.IsBeforePositionNode())
return PositionTemplate<Strategy>::BeforeNode(node);
DCHECK(!text_state_.IsBeforeChildren());
return PositionTemplate<Strategy>::AfterNode(node);
}
template <typename Strategy> template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>
TextIteratorAlgorithm<Strategy>::StartPositionInCurrentContainer() const { TextIteratorAlgorithm<Strategy>::StartPositionInCurrentContainer() const {
......
...@@ -81,6 +81,12 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm { ...@@ -81,6 +81,12 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
PositionTemplate<Strategy> StartPositionInCurrentContainer() const; PositionTemplate<Strategy> StartPositionInCurrentContainer() const;
PositionTemplate<Strategy> EndPositionInCurrentContainer() const; PositionTemplate<Strategy> EndPositionInCurrentContainer() const;
// Returns the position before |char16_offset| in current text run.
PositionTemplate<Strategy> GetPositionBefore(int char16_offset) const;
// Returns the position after |char16_offset| in current text run.
PositionTemplate<Strategy> GetPositionAfter(int char16_offset) const;
const TextIteratorTextState& GetText() const { return text_state_; } const TextIteratorTextState& GetText() const { return text_state_; }
int length() const { return text_state_.length(); } int length() const { return text_state_.length(); }
UChar CharacterAt(unsigned index) const { UChar CharacterAt(unsigned index) const {
......
...@@ -144,6 +144,7 @@ void TextIteratorTextState::UpdatePositionOffsets( ...@@ -144,6 +144,7 @@ void TextIteratorTextState::UpdatePositionOffsets(
position_start_offset_ = node_index; position_start_offset_ = node_index;
position_end_offset_ = node_index; position_end_offset_ = node_index;
return; return;
case PositionNodeType::kBeforeCharacter:
case PositionNodeType::kBeforeChildren: case PositionNodeType::kBeforeChildren:
case PositionNodeType::kInText: case PositionNodeType::kInText:
case PositionNodeType::kNone: case PositionNodeType::kNone:
...@@ -191,7 +192,14 @@ void TextIteratorTextState::EmitChar16BeforeNode(UChar code_unit, ...@@ -191,7 +192,14 @@ void TextIteratorTextState::EmitChar16BeforeNode(UChar code_unit,
void TextIteratorTextState::EmitChar16Before(UChar code_unit, void TextIteratorTextState::EmitChar16Before(UChar code_unit,
const Text& text_node, const Text& text_node,
unsigned offset) { unsigned offset) {
SetTextNodePosition(text_node, offset, offset); // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
// "U+00DF" to "SS". See "fast/css/case-transform.html"
// DCHECK_LE(offset, text_node.length());
position_node_type_ = PositionNodeType::kBeforeCharacter;
position_container_node_ = &text_node;
position_node_ = &text_node;
position_start_offset_ = offset;
position_end_offset_ = offset;
PopulateStringBufferFromChar16(code_unit); PopulateStringBufferFromChar16(code_unit);
} }
...@@ -253,7 +261,7 @@ void TextIteratorTextState::PopulateStringBuffer(const String& text, ...@@ -253,7 +261,7 @@ void TextIteratorTextState::PopulateStringBuffer(const String& text,
void TextIteratorTextState::SetTextNodePosition(const Text& text_node, void TextIteratorTextState::SetTextNodePosition(const Text& text_node,
unsigned position_start_offset, unsigned position_start_offset,
unsigned position_end_offset) { unsigned position_end_offset) {
DCHECK_LE(position_start_offset, position_end_offset); DCHECK_LT(position_start_offset, position_end_offset);
// TODO(editing-dev): text-transform:uppercase can make text longer, e.g. // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
// "U+00DF" to "SS". See "fast/css/case-transform.html" // "U+00DF" to "SS". See "fast/css/case-transform.html"
// DCHECK_LE(position_end_offset, text_node.length()); // DCHECK_LE(position_end_offset, text_node.length());
......
...@@ -91,6 +91,21 @@ class CORE_EXPORT TextIteratorTextState { ...@@ -91,6 +91,21 @@ class CORE_EXPORT TextIteratorTextState {
unsigned PositionEndOffset() const; unsigned PositionEndOffset() const;
const Node* PositionNode() const { return position_node_; } const Node* PositionNode() const { return position_node_; }
const Node* PositionContainerNode() const { return position_container_node_; } const Node* PositionContainerNode() const { return position_container_node_; }
bool IsAfterPositionNode() const {
return position_node_type_ == PositionNodeType::kAfterNode;
}
bool IsBeforePositionNode() const {
return position_node_type_ == PositionNodeType::kBeforeNode;
}
bool IsBeforeCharacter() const {
return position_node_type_ == PositionNodeType::kBeforeCharacter;
}
bool IsBeforeChildren() const {
return position_node_type_ == PositionNodeType::kBeforeChildren;
}
bool IsInTextNode() const {
return position_node_type_ == PositionNodeType::kInText;
}
bool HasEmitted() const { return has_emitted_; } bool HasEmitted() const { return has_emitted_; }
UChar LastCharacter() const { return last_character_; } UChar LastCharacter() const { return last_character_; }
...@@ -106,6 +121,7 @@ class CORE_EXPORT TextIteratorTextState { ...@@ -106,6 +121,7 @@ class CORE_EXPORT TextIteratorTextState {
kAfterNode, kAfterNode,
kAltText, kAltText,
kAsNode, kAsNode,
kBeforeCharacter,
kBeforeChildren, kBeforeChildren,
kBeforeNode, kBeforeNode,
kInText, kInText,
......
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