Commit 78abf6b3 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Make TextIteratorTextState to represent relative position explicitly

This patch makes |TextIteratorTextState| to represent relative position by
explicit classification |PositionNodeType| with functions to emit code unit for
each |PositionNodeType| instead of start and end offsets and computing parent
node when emitting for improving code health.

Before this patch, relative positions are encoded into start offset and end
offset arguments of |SpliceBufer(code_unit, node, start, end)| as
 start=0, end=0  code_unit is emitted before node.
 start=0, end=1  code_unit is emitted as node.
 start=1, end=1  code_unit is emitted after node.
in |TextIterator|.

|SimpleBackwardTextIterator| passes |start| and |end| offsets of container
node instead of encoded number of above.

This patch is a preparation of the patch[1].

[1] http://crrev.com/c/1023502 Make

CharacterIterator: :GetPosition{Before,After}() not to crash with block content
Change-Id: I4be96f877fbcf1be52d2c4bc876226777353dc9a
Reviewed-on: https://chromium-review.googlesource.com/1029393
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarXiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#556384}
parent f2996c44
...@@ -302,12 +302,11 @@ LayoutText* SimplifiedBackwardsTextIteratorAlgorithm< ...@@ -302,12 +302,11 @@ LayoutText* SimplifiedBackwardsTextIteratorAlgorithm<
template <typename Strategy> template <typename Strategy>
bool SimplifiedBackwardsTextIteratorAlgorithm< bool SimplifiedBackwardsTextIteratorAlgorithm<
Strategy>::HandleReplacedElement() { Strategy>::HandleReplacedElement() {
unsigned index = Strategy::Index(*node_);
// We want replaced elements to behave like punctuation for boundary // We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation // finding, and to simply take up space for the selection preservation
// code in moveParagraphs, so we use a comma. Unconditionally emit // code in moveParagraphs, so we use a comma. Unconditionally emit
// here because this iterator is only used for boundary finding. // here because this iterator is only used for boundary finding.
EmitCharacter(',', Strategy::Parent(*node_), index, index + 1); text_state_.EmitChar16AsNode(',', *node_);
return true; return true;
} }
...@@ -319,11 +318,10 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::HandleNonTextNode() { ...@@ -319,11 +318,10 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::HandleNonTextNode() {
if (TextIterator::ShouldEmitNewlineForNode(*node_, false) || if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
TextIterator::ShouldEmitNewlineAfterNode(*node_) || TextIterator::ShouldEmitNewlineAfterNode(*node_) ||
TextIterator::ShouldEmitTabBeforeNode(*node_)) { TextIterator::ShouldEmitTabBeforeNode(*node_)) {
unsigned index = Strategy::Index(*node_); // TODO(editing-dev):The start of this emitted range is wrong. Ensuring
// The start of this emitted range is wrong. Ensuring correctness would // correctness would require |VisiblePositions| and so would be slow.
// require VisiblePositions and so would be slow. previousBoundary expects // |previousBoundary expects this.
// this. text_state_.EmitChar16AfterNode('\n', *node_);
EmitCharacter('\n', Strategy::Parent(*node_), index + 1, index + 1);
} }
return true; return true;
} }
...@@ -333,22 +331,17 @@ void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::ExitNode() { ...@@ -333,22 +331,17 @@ void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::ExitNode() {
if (TextIterator::ShouldEmitNewlineForNode(*node_, false) || if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
TextIterator::ShouldEmitNewlineBeforeNode(*node_) || TextIterator::ShouldEmitNewlineBeforeNode(*node_) ||
TextIterator::ShouldEmitTabBeforeNode(*node_)) { TextIterator::ShouldEmitTabBeforeNode(*node_)) {
// The start of this emitted range is wrong. Ensuring correctness would // TODO(editing-dev): When we want to use |EmitChar16BeforeNode()| when
// require VisiblePositions and so would be slow. previousBoundary expects // test[1] and and test[2] failures are addressed.
// this. // [1] readonly-disabled-text-selection.html
EmitCharacter('\n', node_, 0, 0); // [2] extend_selection_05_ltr_backward_word.html
// TODO(editing-dev): The start of this emitted range is wrong. Ensuring
// correctness would require |VisiblePositions| and so would be slow.
// previousBoundary expects this.
text_state_.EmitChar16BeforeChildren('\n', ToContainerNode(*node_));
} }
} }
template <typename Strategy>
void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EmitCharacter(
UChar c,
const Node* node,
int start_offset,
int end_offset) {
text_state_.SpliceBuffer(c, node, node, start_offset, end_offset);
}
template <typename Strategy> template <typename Strategy>
bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange( bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange(
const Node* next) { const Node* next) {
...@@ -361,41 +354,55 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange( ...@@ -361,41 +354,55 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange(
return true; return true;
} }
template <typename Strategy>
void SimplifiedBackwardsTextIteratorAlgorithm<
Strategy>::EnsurePositionContainer() const {
DCHECK(text_state_.PositionNode());
if (text_state_.PositionContainerNode())
return;
const Node& node = *text_state_.PositionNode();
const ContainerNode* parent = Strategy::Parent(node);
DCHECK(parent);
text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node));
}
template <typename Strategy> template <typename Strategy>
const Node* SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartContainer() const Node* SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartContainer()
const { const {
if (text_state_.PositionNode()) if (!text_state_.PositionNode())
return text_state_.PositionNode(); return start_node_;
return start_node_; EnsurePositionContainer();
return text_state_.PositionContainerNode();
}
template <typename Strategy>
int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartOffset() const {
if (!text_state_.PositionNode())
return start_offset_;
EnsurePositionContainer();
return text_state_.PositionStartOffset();
} }
template <typename Strategy> template <typename Strategy>
int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndOffset() const { int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndOffset() const {
if (text_state_.PositionNode()) if (!text_state_.PositionNode())
return text_state_.PositionEndOffset(); return start_offset_;
return start_offset_; EnsurePositionContainer();
return text_state_.PositionEndOffset();
} }
template <typename Strategy> template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>
SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartPosition() const { SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartPosition() const {
if (text_state_.PositionNode()) { return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
return PositionTemplate<Strategy>::EditingPositionOf( StartOffset());
text_state_.PositionNode(), text_state_.PositionStartOffset());
}
return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
start_offset_);
} }
template <typename Strategy> template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>
SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndPosition() const { SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndPosition() const {
if (text_state_.PositionNode()) { return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
return PositionTemplate<Strategy>::EditingPositionOf( EndOffset());
text_state_.PositionNode(), text_state_.PositionEndOffset());
}
return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
start_offset_);
} }
template <typename Strategy> template <typename Strategy>
......
...@@ -73,6 +73,14 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm { ...@@ -73,6 +73,14 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm {
int min_length) const; int min_length) const;
int CopyTextTo(BackwardsTextBuffer* output, int position = 0) const; int CopyTextTo(BackwardsTextBuffer* output, int position = 0) const;
// TODO(editing-dev): We should consider code sharing between |TextIterator|
// and |SimplifiedBackwardsTextIterator| for
// - StartContainer()
// - EndOffset()
// - StartContainer()
// - EndPosition()
// TODO(editing-dev): We should rename |StartContainer()| to
// |CurrentContainer()| as |TextIterator|.
const Node* StartContainer() const; const Node* StartContainer() const;
int EndOffset() const; int EndOffset() const;
PositionTemplate<Strategy> StartPosition() const; PositionTemplate<Strategy> StartPosition() const;
...@@ -88,11 +96,17 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm { ...@@ -88,11 +96,17 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm {
LayoutText* HandleFirstLetter(int& start_offset, int& offset_in_node); LayoutText* HandleFirstLetter(int& start_offset, int& offset_in_node);
bool HandleReplacedElement(); bool HandleReplacedElement();
bool HandleNonTextNode(); bool HandleNonTextNode();
void EmitCharacter(UChar, const Node*, int start_offset, int end_offset);
bool AdvanceRespectingRange(const Node*); bool AdvanceRespectingRange(const Node*);
bool IsBetweenSurrogatePair(int position) const; bool IsBetweenSurrogatePair(int position) const;
// TODO(editing-dev): We should consider code sharing between |TextIterator|
// and |SimplifiedBackwardsTextIterator| for
// - EnsurePositionContainer()
// - StartOffset()
void EnsurePositionContainer() const;
int StartOffset() const;
TextIteratorBehavior behavior_; TextIteratorBehavior behavior_;
// Contains state of emitted text. // Contains state of emitted text.
......
...@@ -249,7 +249,7 @@ bool TextIteratorAlgorithm<Strategy>::HandleRememberedProgress() { ...@@ -249,7 +249,7 @@ bool TextIteratorAlgorithm<Strategy>::HandleRememberedProgress() {
// iteration, instead of using m_needsAnotherNewline. // iteration, instead of using m_needsAnotherNewline.
Node* last_child = Strategy::LastChild(*node_); Node* last_child = Strategy::LastChild(*node_);
const Node* base_node = last_child ? last_child : node_.Get(); const Node* base_node = last_child ? last_child : node_.Get();
SpliceBuffer('\n', Strategy::Parent(*base_node), base_node, 1, 1); EmitChar16AfterNode('\n', *base_node);
needs_another_newline_ = false; needs_another_newline_ = false;
return true; return true;
} }
...@@ -508,15 +508,13 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() { ...@@ -508,15 +508,13 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() {
} }
if (EmitsObjectReplacementCharacter()) { if (EmitsObjectReplacementCharacter()) {
SpliceBuffer(kObjectReplacementCharacter, Strategy::Parent(*node_), node_, EmitChar16AsNode(kObjectReplacementCharacter, *node_);
0, 1);
return; return;
} }
DCHECK_EQ(last_text_node_, text_node_handler_.GetNode()); DCHECK_EQ(last_text_node_, text_node_handler_.GetNode());
if (last_text_node_) { if (last_text_node_) {
if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement( if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement()) {
Strategy::Parent(*last_text_node_))) {
needs_handle_replaced_element_ = true; needs_handle_replaced_element_ = true;
return; return;
} }
...@@ -531,17 +529,18 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() { ...@@ -531,17 +529,18 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() {
// We want replaced elements to behave like punctuation for boundary // We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation // finding, and to simply take up space for the selection preservation
// code in moveParagraphs, so we use a comma. // code in moveParagraphs, so we use a comma.
SpliceBuffer(',', Strategy::Parent(*node_), node_, 0, 1); EmitChar16AsNode(',', *node_);
return; return;
} }
text_state_.UpdateForReplacedElement(*Strategy::Parent(*node_), *node_);
if (EmitsImageAltText() && TextIterator::SupportsAltText(*node_)) { if (EmitsImageAltText() && TextIterator::SupportsAltText(*node_)) {
text_state_.EmitAltText(node_); text_state_.EmitAltText(ToHTMLElement(*node_));
if (text_state_.length()) return;
return;
} }
// TODO(editing-dev): We can remove |UpdateForReplacedElement()| call when
// we address layout test failures (text diff by newlines only) and unit
// tests, e.g. TextIteratorTest.IgnoreAltTextInTextControls.
text_state_.UpdateForReplacedElement(*node_);
} }
template <typename Strategy> template <typename Strategy>
...@@ -727,6 +726,8 @@ template <typename Strategy> ...@@ -727,6 +726,8 @@ template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() { void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
// Emit a character to show the positioning of m_node. // Emit a character to show the positioning of m_node.
// TODO(editing-dev): We should rewrite this below code fragment to utilize
// early-return style.
// When we haven't been emitting any characters, // When we haven't been emitting any characters,
// shouldRepresentNodeOffsetZero() can create VisiblePositions, which is // shouldRepresentNodeOffsetZero() can create VisiblePositions, which is
// expensive. So, we perform the inexpensive checks on m_node to see if it // expensive. So, we perform the inexpensive checks on m_node to see if it
...@@ -734,23 +735,23 @@ void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() { ...@@ -734,23 +735,23 @@ void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
// encountering shouldRepresentNodeOffsetZero()s worse case behavior. // encountering shouldRepresentNodeOffsetZero()s worse case behavior.
if (ShouldEmitTabBeforeNode(*node_)) { if (ShouldEmitTabBeforeNode(*node_)) {
if (ShouldRepresentNodeOffsetZero()) if (ShouldRepresentNodeOffsetZero())
SpliceBuffer('\t', Strategy::Parent(*node_), node_, 0, 0); EmitChar16BeforeNode('\t', *node_);
} else if (ShouldEmitNewlineBeforeNode(*node_)) { } else if (ShouldEmitNewlineBeforeNode(*node_)) {
if (ShouldRepresentNodeOffsetZero()) if (ShouldRepresentNodeOffsetZero())
SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 0); EmitChar16BeforeNode('\n', *node_);
} else if (ShouldEmitSpaceBeforeAndAfterNode(*node_)) { } else if (ShouldEmitSpaceBeforeAndAfterNode(*node_)) {
if (ShouldRepresentNodeOffsetZero()) if (ShouldRepresentNodeOffsetZero())
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 0); EmitChar16BeforeNode(kSpaceCharacter, *node_);
} }
} }
template <typename Strategy> template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::HandleNonTextNode() { void TextIteratorAlgorithm<Strategy>::HandleNonTextNode() {
if (ShouldEmitNewlineForNode(*node_, EmitsOriginalText())) if (ShouldEmitNewlineForNode(*node_, EmitsOriginalText()))
SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 1); EmitChar16AsNode('\n', *node_);
else if (EmitsCharactersBetweenAllVisiblePositions() && else if (EmitsCharactersBetweenAllVisiblePositions() &&
node_->GetLayoutObject() && node_->GetLayoutObject()->IsHR()) node_->GetLayoutObject() && node_->GetLayoutObject()->IsHR())
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 1); EmitChar16AsNode(kSpaceCharacter, *node_);
else else
RepresentNodeOffsetZero(); RepresentNodeOffsetZero();
} }
...@@ -784,32 +785,39 @@ void TextIteratorAlgorithm<Strategy>::ExitNode() { ...@@ -784,32 +785,39 @@ void TextIteratorAlgorithm<Strategy>::ExitNode() {
// contain a VisiblePosition when doing selection preservation. // contain a VisiblePosition when doing selection preservation.
if (text_state_.LastCharacter() != '\n') { if (text_state_.LastCharacter() != '\n') {
// insert a newline with a position following this block's contents. // insert a newline with a position following this block's contents.
SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node, EmitChar16AfterNode(kNewlineCharacter, *base_node);
1, 1);
// remember whether to later add a newline for the current node // remember whether to later add a newline for the current node
DCHECK(!needs_another_newline_); DCHECK(!needs_another_newline_);
needs_another_newline_ = add_newline; needs_another_newline_ = add_newline;
} else if (add_newline) { } else if (add_newline) {
// insert a newline with a position following this block's contents. // insert a newline with a position following this block's contents.
SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node, EmitChar16AfterNode(kNewlineCharacter, *base_node);
1, 1);
} }
} }
// If nothing was emitted, see if we need to emit a space. // If nothing was emitted, see if we need to emit a space.
if (!text_state_.PositionNode() && ShouldEmitSpaceBeforeAndAfterNode(*node_)) if (!text_state_.PositionNode() && ShouldEmitSpaceBeforeAndAfterNode(*node_))
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*base_node), base_node, 1, EmitChar16AfterNode(kSpaceCharacter, *base_node);
1); }
template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::EmitChar16AfterNode(UChar code_unit,
const Node& node) {
text_state_.EmitChar16AfterNode(code_unit, node);
text_node_handler_.ResetCollapsedWhiteSpaceFixup();
} }
template <typename Strategy> template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::SpliceBuffer(UChar c, void TextIteratorAlgorithm<Strategy>::EmitChar16AsNode(UChar code_unit,
const Node* text_node, const Node& node) {
const Node* offset_base_node, text_state_.EmitChar16AsNode(code_unit, node);
unsigned text_start_offset, text_node_handler_.ResetCollapsedWhiteSpaceFixup();
unsigned text_end_offset) { }
text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
text_end_offset); template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::EmitChar16BeforeNode(UChar code_unit,
const Node& node) {
text_state_.EmitChar16BeforeNode(code_unit, node);
text_node_handler_.ResetCollapsedWhiteSpaceFixup(); text_node_handler_.ResetCollapsedWhiteSpaceFixup();
} }
...@@ -842,30 +850,37 @@ const Node* TextIteratorAlgorithm<Strategy>::GetNode() const { ...@@ -842,30 +850,37 @@ const Node* TextIteratorAlgorithm<Strategy>::GetNode() const {
template <typename Strategy> template <typename Strategy>
int TextIteratorAlgorithm<Strategy>::StartOffsetInCurrentContainer() const { int TextIteratorAlgorithm<Strategy>::StartOffsetInCurrentContainer() const {
if (const Node* node = text_state_.PositionNode()) { if (!text_state_.PositionNode())
if (const Node* base_node = text_state_.PositionOffsetBaseNode()) return end_offset_;
text_state_.UpdatePositionOffsets(Strategy::Index(*base_node)); EnsurePositionContainer();
return text_state_.PositionStartOffset(); return text_state_.PositionStartOffset();
}
return end_offset_;
} }
template <typename Strategy> template <typename Strategy>
int TextIteratorAlgorithm<Strategy>::EndOffsetInCurrentContainer() const { int TextIteratorAlgorithm<Strategy>::EndOffsetInCurrentContainer() const {
if (const Node* node = text_state_.PositionNode()) { if (!text_state_.PositionNode())
if (const Node* base_node = text_state_.PositionOffsetBaseNode()) return end_offset_;
text_state_.UpdatePositionOffsets(Strategy::Index(*base_node)); EnsurePositionContainer();
return text_state_.PositionEndOffset(); return text_state_.PositionEndOffset();
}
return end_offset_;
} }
template <typename Strategy> template <typename Strategy>
const Node* TextIteratorAlgorithm<Strategy>::CurrentContainer() const { const Node* TextIteratorAlgorithm<Strategy>::CurrentContainer() const {
if (text_state_.PositionNode()) { if (!text_state_.PositionNode())
return text_state_.PositionNode(); return end_container_;
} EnsurePositionContainer();
return end_container_; return text_state_.PositionContainerNode();
}
template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::EnsurePositionContainer() const {
DCHECK(text_state_.PositionNode());
if (text_state_.PositionContainerNode())
return;
const Node& node = *text_state_.PositionNode();
const ContainerNode* parent = Strategy::Parent(node);
DCHECK(parent);
text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node));
} }
template <typename Strategy> template <typename Strategy>
......
...@@ -130,6 +130,10 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm { ...@@ -130,6 +130,10 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
kHandledChildren kHandledChildren
}; };
void EmitChar16AfterNode(UChar code_unit, const Node& node);
void EmitChar16AsNode(UChar code_unit, const Node& node);
void EmitChar16BeforeNode(UChar code_unit, const Node& node);
void ExitNode(); void ExitNode();
bool ShouldRepresentNodeOffsetZero(); bool ShouldRepresentNodeOffsetZero();
bool ShouldEmitSpaceBeforeAndAfterNode(const Node&); bool ShouldEmitSpaceBeforeAndAfterNode(const Node&);
...@@ -141,11 +145,6 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm { ...@@ -141,11 +145,6 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
void HandleTextNode(); void HandleTextNode();
void HandleReplacedElement(); void HandleReplacedElement();
void HandleNonTextNode(); void HandleNonTextNode();
void SpliceBuffer(UChar,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset);
// Used by selection preservation code. There should be one character emitted // Used by selection preservation code. There should be one character emitted
// between every VisiblePosition in the Range used to create the TextIterator. // between every VisiblePosition in the Range used to create the TextIterator.
...@@ -202,6 +201,9 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm { ...@@ -202,6 +201,9 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
unsigned position, unsigned position,
unsigned copy_length) const; unsigned copy_length) const;
// Ensure container node of current text run for computing position.
void EnsurePositionContainer() const;
// The range. // The range.
const Member<const Node> start_container_; const Member<const Node> start_container_;
const unsigned start_offset_; const unsigned start_offset_;
......
...@@ -181,7 +181,7 @@ void TextIteratorTextNodeHandler::HandlePreFormattedTextNode() { ...@@ -181,7 +181,7 @@ void TextIteratorTextNodeHandler::HandlePreFormattedTextNode() {
HasVisibleTextNode(layout_object)) { HasVisibleTextNode(layout_object)) {
if (!behavior_.CollapseTrailingSpace() || if (!behavior_.CollapseTrailingSpace() ||
(offset_ > 0 && str[offset_ - 1] == ' ')) { (offset_ > 0 && str[offset_ - 1] == ' ')) {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, offset_, offset_); EmitChar16Before(kSpaceCharacter, offset_);
needs_handle_pre_formatted_text_node_ = true; needs_handle_pre_formatted_text_node_ = true;
return; return;
} }
...@@ -377,8 +377,7 @@ void TextIteratorTextNodeHandler::HandleTextBox() { ...@@ -377,8 +377,7 @@ void TextIteratorTextNodeHandler::HandleTextBox() {
--space_run_start; --space_run_start;
EmitText(layout_object, space_run_start, space_run_start + 1); EmitText(layout_object, space_run_start, space_run_start + 1);
} else { } else {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start, EmitChar16Before(kSpaceCharacter, run_start);
run_start);
} }
return; return;
} }
...@@ -416,10 +415,9 @@ void TextIteratorTextNodeHandler::HandleTextBox() { ...@@ -416,10 +415,9 @@ void TextIteratorTextNodeHandler::HandleTextBox() {
// We need to preserve new lines in case of PreLine. // We need to preserve new lines in case of PreLine.
// See bug crbug.com/317365. // See bug crbug.com/317365.
if (layout_object->Style()->WhiteSpace() == EWhiteSpace::kPreLine) { if (layout_object->Style()->WhiteSpace() == EWhiteSpace::kPreLine) {
SpliceBuffer('\n', text_node_, nullptr, run_start, run_start); EmitChar16Before('\n', run_start);
} else { } else {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start, EmitReplacmentCodeUnit(kSpaceCharacter, run_start);
run_start + 1);
} }
offset_ = text_start_offset + run_start + 1; offset_ = text_start_offset + run_start + 1;
} else { } else {
...@@ -537,11 +535,10 @@ bool TextIteratorTextNodeHandler::ShouldFixLeadingWhiteSpaceForReplacedElement() ...@@ -537,11 +535,10 @@ bool TextIteratorTextNodeHandler::ShouldFixLeadingWhiteSpaceForReplacedElement()
return offset_ > 0 && str[offset_ - 1] == ' '; return offset_ > 0 && str[offset_ - 1] == ' ';
} }
bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement( bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement() {
const Node* parent) {
if (!ShouldFixLeadingWhiteSpaceForReplacedElement()) if (!ShouldFixLeadingWhiteSpaceForReplacedElement())
return false; return false;
text_state_.SpliceBuffer(kSpaceCharacter, parent, text_node_, 1, 1); text_state_.EmitChar16AfterNode(kSpaceCharacter, *text_node_);
ResetCollapsedWhiteSpaceFixup(); ResetCollapsedWhiteSpaceFixup();
return true; return true;
} }
...@@ -552,13 +549,15 @@ void TextIteratorTextNodeHandler::ResetCollapsedWhiteSpaceFixup() { ...@@ -552,13 +549,15 @@ void TextIteratorTextNodeHandler::ResetCollapsedWhiteSpaceFixup() {
last_text_node_ended_with_collapsed_space_ = false; last_text_node_ended_with_collapsed_space_ = false;
} }
void TextIteratorTextNodeHandler::SpliceBuffer(UChar c, void TextIteratorTextNodeHandler::EmitChar16Before(UChar code_unit,
const Node* text_node, unsigned offset) {
const Node* offset_base_node, text_state_.EmitChar16Before(code_unit, *text_node_, offset);
unsigned text_start_offset, ResetCollapsedWhiteSpaceFixup();
unsigned text_end_offset) { }
text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
text_end_offset); void TextIteratorTextNodeHandler::EmitReplacmentCodeUnit(UChar code_unit,
unsigned offset) {
text_state_.EmitReplacmentCodeUnit(code_unit, *text_node_, offset);
ResetCollapsedWhiteSpaceFixup(); ResetCollapsedWhiteSpaceFixup();
} }
......
...@@ -32,7 +32,7 @@ class TextIteratorTextNodeHandler { ...@@ -32,7 +32,7 @@ class TextIteratorTextNodeHandler {
bool HandleRemainingTextRuns(); bool HandleRemainingTextRuns();
// Returns true if a leading white space is emitted before a replaced element. // Returns true if a leading white space is emitted before a replaced element.
bool FixLeadingWhiteSpaceForReplacedElement(const Node*); bool FixLeadingWhiteSpaceForReplacedElement();
void ResetCollapsedWhiteSpaceFixup(); void ResetCollapsedWhiteSpaceFixup();
...@@ -65,12 +65,12 @@ class TextIteratorTextNodeHandler { ...@@ -65,12 +65,12 @@ class TextIteratorTextNodeHandler {
bool ShouldFixLeadingWhiteSpaceForReplacedElement() const; bool ShouldFixLeadingWhiteSpaceForReplacedElement() const;
// Emit a character before |offset| of characters in |text_node_|. // Emits |code_unit| before |offset| of characters in |text_node_|.
void SpliceBuffer(UChar, void EmitChar16Before(UChar code_unit, unsigned offset);
const Node* text_node, // Emits |code_unit| as replacement of a code unit after |offset| in
const Node* offset_base_node, // |text_node_|.
unsigned text_start_offset, void EmitReplacmentCodeUnit(UChar code_unit, unsigned offset);
unsigned text_end_offset);
void EmitText(const LayoutText* layout_object, void EmitText(const LayoutText* layout_object,
unsigned text_start_offset, unsigned text_start_offset,
unsigned text_end_offset); unsigned text_end_offset);
......
...@@ -49,6 +49,16 @@ TextIteratorTextState::TextIteratorTextState( ...@@ -49,6 +49,16 @@ TextIteratorTextState::TextIteratorTextState(
const TextIteratorBehavior& behavior) const TextIteratorBehavior& behavior)
: behavior_(behavior) {} : behavior_(behavior) {}
unsigned TextIteratorTextState::PositionStartOffset() const {
DCHECK(position_container_node_);
return position_start_offset_.value();
}
unsigned TextIteratorTextState::PositionEndOffset() const {
DCHECK(position_container_node_);
return position_end_offset_.value();
}
UChar TextIteratorTextState::CharacterAt(unsigned index) const { UChar TextIteratorTextState::CharacterAt(unsigned index) const {
SECURITY_DCHECK(index < length()); SECURITY_DCHECK(index < length());
if (!(index < length())) if (!(index < length()))
...@@ -93,59 +103,116 @@ void TextIteratorTextState::AppendTextToStringBuilder( ...@@ -93,59 +103,116 @@ void TextIteratorTextState::AppendTextToStringBuilder(
} }
} }
void TextIteratorTextState::UpdateForReplacedElement(const Node& parent_node, void TextIteratorTextState::UpdateForReplacedElement(const Node& node) {
const Node& base_node) { ResetPositionContainerNode(PositionNodeType::kAsNode, node);
has_emitted_ = true; PopulateStringBuffer("", 0, 0);
position_node_ = &parent_node; }
position_offset_base_node_ = &base_node;
position_start_offset_ = 0;
position_end_offset_ = 1;
single_character_buffer_ = 0;
text_length_ = 0; void TextIteratorTextState::ResetPositionContainerNode(
text_start_offset_ = 0; PositionNodeType node_type,
last_character_ = 0; const Node& node) {
DCHECK_NE(node_type, PositionNodeType::kBeforeChildren);
DCHECK_NE(node_type, PositionNodeType::kInText);
DCHECK_NE(node_type, PositionNodeType::kNone);
position_node_type_ = node_type;
position_container_node_ = nullptr;
position_node_ = node;
position_start_offset_ = base::nullopt;
position_end_offset_ = base::nullopt;
} }
void TextIteratorTextState::EmitAltText(const Node* node) { void TextIteratorTextState::UpdatePositionOffsets(
text_ = ToHTMLElement(node)->AltText(); const ContainerNode& container_node,
text_start_offset_ = 0; unsigned node_index) const {
text_length_ = text_.length(); DCHECK(!position_container_node_);
last_character_ = text_length_ ? text_[text_length_ - 1] : 0; DCHECK(!position_start_offset_.has_value());
DCHECK(!position_end_offset_.has_value());
switch (position_node_type_) {
case PositionNodeType::kAfterNode:
position_container_node_ = &container_node;
position_start_offset_ = node_index + 1;
position_end_offset_ = node_index + 1;
return;
case PositionNodeType::kAltText:
case PositionNodeType::kAsNode:
position_container_node_ = &container_node;
position_start_offset_ = node_index;
position_end_offset_ = node_index + 1;
return;
case PositionNodeType::kBeforeNode:
position_container_node_ = &container_node;
position_start_offset_ = node_index;
position_end_offset_ = node_index;
return;
case PositionNodeType::kBeforeChildren:
case PositionNodeType::kInText:
case PositionNodeType::kNone:
NOTREACHED();
return;
}
NOTREACHED() << static_cast<int>(position_node_type_);
} }
void TextIteratorTextState::UpdatePositionOffsets(unsigned index) const { void TextIteratorTextState::EmitAltText(const HTMLElement& element) {
DCHECK(position_offset_base_node_); ResetPositionContainerNode(PositionNodeType::kAltText, element);
position_start_offset_ += index; const String text = element.AltText();
position_end_offset_ += index; PopulateStringBuffer(text, 0, text.length());
position_offset_base_node_ = nullptr;
} }
void TextIteratorTextState::SpliceBuffer(UChar c, void TextIteratorTextState::EmitChar16AfterNode(UChar code_unit,
const Node* text_node, const Node& node) {
const Node* offset_base_node, ResetPositionContainerNode(PositionNodeType::kAfterNode, node);
unsigned text_start_offset, PopulateStringBufferFromChar16(code_unit);
unsigned text_end_offset) { }
DCHECK(text_node);
has_emitted_ = true; void TextIteratorTextState::EmitChar16AsNode(UChar code_unit,
const Node& node) {
ResetPositionContainerNode(PositionNodeType::kAsNode, node);
PopulateStringBufferFromChar16(code_unit);
}
void TextIteratorTextState::EmitChar16BeforeChildren(
UChar code_unit,
const ContainerNode& container_node) {
position_node_type_ = PositionNodeType::kBeforeChildren;
position_container_node_ = &container_node;
position_node_ = &container_node;
position_start_offset_ = 0;
position_end_offset_ = 0;
PopulateStringBufferFromChar16(code_unit);
}
void TextIteratorTextState::EmitChar16BeforeNode(UChar code_unit,
const Node& node) {
ResetPositionContainerNode(PositionNodeType::kBeforeNode, node);
PopulateStringBufferFromChar16(code_unit);
}
void TextIteratorTextState::EmitChar16Before(UChar code_unit,
const Text& text_node,
unsigned offset) {
SetTextNodePosition(text_node, offset, offset);
PopulateStringBufferFromChar16(code_unit);
}
// Remember information with which to construct the TextIterator::range(). void TextIteratorTextState::EmitReplacmentCodeUnit(UChar code_unit,
// NOTE: textNode is often not a text node, so the range will specify child const Text& text_node,
// nodes of positionNode unsigned offset) {
position_node_ = text_node; SetTextNodePosition(text_node, offset, offset + 1);
position_offset_base_node_ = offset_base_node; PopulateStringBufferFromChar16(code_unit);
position_start_offset_ = text_start_offset; }
position_end_offset_ = text_end_offset;
void TextIteratorTextState::PopulateStringBufferFromChar16(UChar code_unit) {
has_emitted_ = true;
// remember information with which to construct the TextIterator::characters() // remember information with which to construct the TextIterator::characters()
// and length() // and length()
single_character_buffer_ = c; single_character_buffer_ = code_unit;
DCHECK(single_character_buffer_); DCHECK(single_character_buffer_);
text_length_ = 1; text_length_ = 1;
text_start_offset_ = 0; text_start_offset_ = 0;
// remember some iteration state // remember some iteration state
last_character_ = c; last_character_ = code_unit;
} }
void TextIteratorTextState::EmitText(const Text& text_node, void TextIteratorTextState::EmitText(const Text& text_node,
...@@ -155,31 +222,48 @@ void TextIteratorTextState::EmitText(const Text& text_node, ...@@ -155,31 +222,48 @@ void TextIteratorTextState::EmitText(const Text& text_node,
unsigned text_start_offset, unsigned text_start_offset,
unsigned text_end_offset) { unsigned text_end_offset) {
DCHECK_LE(position_start_offset, position_end_offset); DCHECK_LE(position_start_offset, position_end_offset);
// TODO(editing-dev): text-transform:uppercase can make text longer, e.g. const String text =
// "U+00DF" to "SS". See "fast/css/case-transform.html"
// DCHECK_LE(position_end_offset, text_node.length());
text_ =
behavior_.EmitsSmallXForTextSecurity() && IsTextSecurityNode(text_node) behavior_.EmitsSmallXForTextSecurity() && IsTextSecurityNode(text_node)
? RepeatString("x", string.length()) ? RepeatString("x", string.length())
: string, : string;
DCHECK(!text_.IsEmpty()); DCHECK(!text.IsEmpty());
DCHECK_LT(text_start_offset, text_.length()); DCHECK_LT(text_start_offset, text.length());
DCHECK_LE(text_end_offset, text_.length()); DCHECK_LE(text_end_offset, text.length());
DCHECK_LE(text_start_offset, text_end_offset); DCHECK_LE(text_start_offset, text_end_offset);
position_node_ = &text_node; SetTextNodePosition(text_node, position_start_offset, position_end_offset);
position_offset_base_node_ = nullptr; PopulateStringBuffer(text, text_start_offset, text_end_offset);
position_start_offset_ = position_start_offset; }
position_end_offset_ = position_end_offset;
void TextIteratorTextState::PopulateStringBuffer(const String& text,
unsigned text_start_offset,
unsigned text_end_offset) {
DCHECK_LE(text_start_offset, text_end_offset);
DCHECK_LE(text_end_offset, text.length());
text_ = text;
single_character_buffer_ = 0; single_character_buffer_ = 0;
text_start_offset_ = text_start_offset; text_start_offset_ = text_start_offset;
text_length_ = text_end_offset - text_start_offset; text_length_ = text_end_offset - text_start_offset;
last_character_ = text_[text_end_offset - 1]; last_character_ = text_end_offset == 0 ? 0 : text_[text_end_offset - 1];
has_emitted_ = true; has_emitted_ = true;
} }
void TextIteratorTextState::SetTextNodePosition(const Text& text_node,
unsigned position_start_offset,
unsigned position_end_offset) {
DCHECK_LE(position_start_offset, position_end_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(position_end_offset, text_node.length());
position_node_type_ = PositionNodeType::kInText;
position_container_node_ = &text_node;
position_node_ = &text_node;
position_start_offset_ = position_start_offset;
position_end_offset_ = position_end_offset;
}
void TextIteratorTextState::AppendTextTo(ForwardsTextBuffer* output, void TextIteratorTextState::AppendTextTo(ForwardsTextBuffer* output,
unsigned position, unsigned position,
unsigned length_to_append) const { unsigned length_to_append) const {
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/iterators/forwards_text_buffer.h" #include "third_party/blink/renderer/core/editing/iterators/forwards_text_buffer.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator_behavior.h" #include "third_party/blink/renderer/core/editing/iterators/text_iterator_behavior.h"
...@@ -35,6 +36,8 @@ ...@@ -35,6 +36,8 @@
namespace blink { namespace blink {
class BackwardsTextBuffer; class BackwardsTextBuffer;
class ContainerNode;
class HTMLElement;
class Text; class Text;
class CORE_EXPORT TextIteratorTextState { class CORE_EXPORT TextIteratorTextState {
...@@ -57,28 +60,37 @@ class CORE_EXPORT TextIteratorTextState { ...@@ -57,28 +60,37 @@ class CORE_EXPORT TextIteratorTextState {
unsigned position, unsigned position,
unsigned length_to_prepend) const; unsigned length_to_prepend) const;
void SpliceBuffer(UChar, // Emits code unit relative to |node|.
const Node* text_node, void EmitChar16AfterNode(UChar code_unit, const Node& node);
const Node* offset_base_node, void EmitChar16AsNode(UChar code_unit, const Node& node);
unsigned text_start_offset, void EmitChar16BeforeChildren(UChar code_unit,
unsigned text_end_offset); const ContainerNode& container_node);
void EmitChar16BeforeNode(UChar code_unit, const Node& node);
// Emits |code_unit| before |offset| in |text_node|.
void EmitChar16Before(UChar code_unit,
const Text& text_node,
unsigned offset);
// Emits |code_unit| as replacement of code unit at |offset| in |text_node|.
void EmitReplacmentCodeUnit(UChar code_unit,
const Text& text_node,
unsigned offset);
void EmitText(const Text&, void EmitText(const Text&,
unsigned position_start_offset, unsigned position_start_offset,
unsigned position_end_offset, unsigned position_end_offset,
const String&, const String&,
unsigned text_start_offset, unsigned text_start_offset,
unsigned text_end_offset); unsigned text_end_offset);
void EmitAltText(const Node*); void EmitAltText(const HTMLElement&);
void UpdateForReplacedElement(const Node& parent_node, const Node& base_node); void UpdateForReplacedElement(const Node& node);
// Return position of the current text. // Return position of the current text.
void UpdatePositionOffsets(unsigned node_index) const; void UpdatePositionOffsets(const ContainerNode& container_node,
unsigned PositionStartOffset() const { return position_start_offset_; } unsigned index) const;
unsigned PositionEndOffset() const { return position_end_offset_; } unsigned PositionStartOffset() const;
unsigned PositionEndOffset() const;
const Node* PositionNode() const { return position_node_; } const Node* PositionNode() const { return position_node_; }
const Node* PositionOffsetBaseNode() const { const Node* PositionContainerNode() const { return position_container_node_; }
return position_offset_base_node_;
}
bool HasEmitted() const { return has_emitted_; } bool HasEmitted() const { return has_emitted_; }
UChar LastCharacter() const { return last_character_; } UChar LastCharacter() const { return last_character_; }
...@@ -88,9 +100,29 @@ class CORE_EXPORT TextIteratorTextState { ...@@ -88,9 +100,29 @@ class CORE_EXPORT TextIteratorTextState {
} }
private: private:
// Location of text run relative to |position_node_|.
enum class PositionNodeType {
kNone,
kAfterNode,
kAltText,
kAsNode,
kBeforeChildren,
kBeforeNode,
kInText,
};
void ResetPositionContainerNode(PositionNodeType node_type, const Node& node);
void SetTextNodePosition(const Text& text_node,
unsigned start_offset,
unsigned end_offset);
void PopulateStringBuffer(const String& text, unsigned start, unsigned end);
void PopulateStringBufferFromChar16(UChar code_unit);
const TextIteratorBehavior behavior_; const TextIteratorBehavior behavior_;
unsigned text_length_ = 0; unsigned text_length_ = 0;
// TODO(editing-dev): We should integrate single character buffer and
// string buffer.
// Used for whitespace characters that aren't in the DOM, so we can point at // Used for whitespace characters that aren't in the DOM, so we can point at
// them. // them.
// If non-zero, overrides |text_|. // If non-zero, overrides |text_|.
...@@ -99,13 +131,17 @@ class CORE_EXPORT TextIteratorTextState { ...@@ -99,13 +131,17 @@ class CORE_EXPORT TextIteratorTextState {
// The current text when |single_character_buffer_| is zero, in which case it // The current text when |single_character_buffer_| is zero, in which case it
// is |text_.Substring(text_start_offset_, text_length_)|. // is |text_.Substring(text_start_offset_, text_length_)|.
String text_; String text_;
// TODO(editing-dev): We should make |text_| to hold substring instead of
// entire string with |text_start_offset_| and |text_length_|.
unsigned text_start_offset_ = 0; unsigned text_start_offset_ = 0;
// Position of the current text, in the form to be returned from the iterator. // Position of the current text, in the form to be returned from the iterator.
Member<const Node> position_node_; Member<const Node> position_node_;
mutable Member<const Node> position_offset_base_node_; // |Text| node when |position_node_type_ == kInText| or |ContainerNode|.
mutable unsigned position_start_offset_ = 0; mutable Member<const Node> position_container_node_;
mutable unsigned position_end_offset_ = 0; mutable base::Optional<unsigned> position_start_offset_;
mutable base::Optional<unsigned> position_end_offset_;
PositionNodeType position_node_type_ = PositionNodeType::kNone;
// Used when deciding whether to emit a "positioning" (e.g. newline) before // Used when deciding whether to emit a "positioning" (e.g. newline) before
// any other content // any other content
......
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