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<
template <typename Strategy>
bool SimplifiedBackwardsTextIteratorAlgorithm<
Strategy>::HandleReplacedElement() {
unsigned index = Strategy::Index(*node_);
// We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation
// code in moveParagraphs, so we use a comma. Unconditionally emit
// here because this iterator is only used for boundary finding.
EmitCharacter(',', Strategy::Parent(*node_), index, index + 1);
text_state_.EmitChar16AsNode(',', *node_);
return true;
}
......@@ -319,11 +318,10 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::HandleNonTextNode() {
if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
TextIterator::ShouldEmitNewlineAfterNode(*node_) ||
TextIterator::ShouldEmitTabBeforeNode(*node_)) {
unsigned index = Strategy::Index(*node_);
// The start of this emitted range is wrong. Ensuring correctness would
// require VisiblePositions and so would be slow. previousBoundary expects
// this.
EmitCharacter('\n', Strategy::Parent(*node_), index + 1, index + 1);
// 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_.EmitChar16AfterNode('\n', *node_);
}
return true;
}
......@@ -333,22 +331,17 @@ void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::ExitNode() {
if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
TextIterator::ShouldEmitNewlineBeforeNode(*node_) ||
TextIterator::ShouldEmitTabBeforeNode(*node_)) {
// The start of this emitted range is wrong. Ensuring correctness would
// require VisiblePositions and so would be slow. previousBoundary expects
// this.
EmitCharacter('\n', node_, 0, 0);
// TODO(editing-dev): When we want to use |EmitChar16BeforeNode()| when
// test[1] and and test[2] failures are addressed.
// [1] readonly-disabled-text-selection.html
// [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>
bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange(
const Node* next) {
......@@ -361,41 +354,55 @@ bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange(
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>
const Node* SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartContainer()
const {
if (text_state_.PositionNode())
return text_state_.PositionNode();
return start_node_;
if (!text_state_.PositionNode())
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>
int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndOffset() const {
if (text_state_.PositionNode())
return text_state_.PositionEndOffset();
return start_offset_;
if (!text_state_.PositionNode())
return start_offset_;
EnsurePositionContainer();
return text_state_.PositionEndOffset();
}
template <typename Strategy>
PositionTemplate<Strategy>
SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartPosition() const {
if (text_state_.PositionNode()) {
return PositionTemplate<Strategy>::EditingPositionOf(
text_state_.PositionNode(), text_state_.PositionStartOffset());
}
return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
start_offset_);
return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
StartOffset());
}
template <typename Strategy>
PositionTemplate<Strategy>
SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndPosition() const {
if (text_state_.PositionNode()) {
return PositionTemplate<Strategy>::EditingPositionOf(
text_state_.PositionNode(), text_state_.PositionEndOffset());
}
return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
start_offset_);
return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
EndOffset());
}
template <typename Strategy>
......
......@@ -73,6 +73,14 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm {
int min_length) 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;
int EndOffset() const;
PositionTemplate<Strategy> StartPosition() const;
......@@ -88,11 +96,17 @@ class CORE_TEMPLATE_CLASS_EXPORT SimplifiedBackwardsTextIteratorAlgorithm {
LayoutText* HandleFirstLetter(int& start_offset, int& offset_in_node);
bool HandleReplacedElement();
bool HandleNonTextNode();
void EmitCharacter(UChar, const Node*, int start_offset, int end_offset);
bool AdvanceRespectingRange(const Node*);
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_;
// Contains state of emitted text.
......
......@@ -249,7 +249,7 @@ bool TextIteratorAlgorithm<Strategy>::HandleRememberedProgress() {
// iteration, instead of using m_needsAnotherNewline.
Node* last_child = Strategy::LastChild(*node_);
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;
return true;
}
......@@ -508,15 +508,13 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() {
}
if (EmitsObjectReplacementCharacter()) {
SpliceBuffer(kObjectReplacementCharacter, Strategy::Parent(*node_), node_,
0, 1);
EmitChar16AsNode(kObjectReplacementCharacter, *node_);
return;
}
DCHECK_EQ(last_text_node_, text_node_handler_.GetNode());
if (last_text_node_) {
if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement(
Strategy::Parent(*last_text_node_))) {
if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement()) {
needs_handle_replaced_element_ = true;
return;
}
......@@ -531,17 +529,18 @@ void TextIteratorAlgorithm<Strategy>::HandleReplacedElement() {
// We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation
// code in moveParagraphs, so we use a comma.
SpliceBuffer(',', Strategy::Parent(*node_), node_, 0, 1);
EmitChar16AsNode(',', *node_);
return;
}
text_state_.UpdateForReplacedElement(*Strategy::Parent(*node_), *node_);
if (EmitsImageAltText() && TextIterator::SupportsAltText(*node_)) {
text_state_.EmitAltText(node_);
if (text_state_.length())
return;
text_state_.EmitAltText(ToHTMLElement(*node_));
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>
......@@ -727,6 +726,8 @@ template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
// 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,
// shouldRepresentNodeOffsetZero() can create VisiblePositions, which is
// expensive. So, we perform the inexpensive checks on m_node to see if it
......@@ -734,23 +735,23 @@ void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
// encountering shouldRepresentNodeOffsetZero()s worse case behavior.
if (ShouldEmitTabBeforeNode(*node_)) {
if (ShouldRepresentNodeOffsetZero())
SpliceBuffer('\t', Strategy::Parent(*node_), node_, 0, 0);
EmitChar16BeforeNode('\t', *node_);
} else if (ShouldEmitNewlineBeforeNode(*node_)) {
if (ShouldRepresentNodeOffsetZero())
SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 0);
EmitChar16BeforeNode('\n', *node_);
} else if (ShouldEmitSpaceBeforeAndAfterNode(*node_)) {
if (ShouldRepresentNodeOffsetZero())
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 0);
EmitChar16BeforeNode(kSpaceCharacter, *node_);
}
}
template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::HandleNonTextNode() {
if (ShouldEmitNewlineForNode(*node_, EmitsOriginalText()))
SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 1);
EmitChar16AsNode('\n', *node_);
else if (EmitsCharactersBetweenAllVisiblePositions() &&
node_->GetLayoutObject() && node_->GetLayoutObject()->IsHR())
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 1);
EmitChar16AsNode(kSpaceCharacter, *node_);
else
RepresentNodeOffsetZero();
}
......@@ -784,32 +785,39 @@ void TextIteratorAlgorithm<Strategy>::ExitNode() {
// contain a VisiblePosition when doing selection preservation.
if (text_state_.LastCharacter() != '\n') {
// insert a newline with a position following this block's contents.
SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node,
1, 1);
EmitChar16AfterNode(kNewlineCharacter, *base_node);
// remember whether to later add a newline for the current node
DCHECK(!needs_another_newline_);
needs_another_newline_ = add_newline;
} else if (add_newline) {
// insert a newline with a position following this block's contents.
SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node,
1, 1);
EmitChar16AfterNode(kNewlineCharacter, *base_node);
}
}
// If nothing was emitted, see if we need to emit a space.
if (!text_state_.PositionNode() && ShouldEmitSpaceBeforeAndAfterNode(*node_))
SpliceBuffer(kSpaceCharacter, Strategy::Parent(*base_node), base_node, 1,
1);
EmitChar16AfterNode(kSpaceCharacter, *base_node);
}
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>
void TextIteratorAlgorithm<Strategy>::SpliceBuffer(UChar c,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset) {
text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
text_end_offset);
void TextIteratorAlgorithm<Strategy>::EmitChar16AsNode(UChar code_unit,
const Node& node) {
text_state_.EmitChar16AsNode(code_unit, node);
text_node_handler_.ResetCollapsedWhiteSpaceFixup();
}
template <typename Strategy>
void TextIteratorAlgorithm<Strategy>::EmitChar16BeforeNode(UChar code_unit,
const Node& node) {
text_state_.EmitChar16BeforeNode(code_unit, node);
text_node_handler_.ResetCollapsedWhiteSpaceFixup();
}
......@@ -842,30 +850,37 @@ const Node* TextIteratorAlgorithm<Strategy>::GetNode() const {
template <typename Strategy>
int TextIteratorAlgorithm<Strategy>::StartOffsetInCurrentContainer() const {
if (const Node* node = text_state_.PositionNode()) {
if (const Node* base_node = text_state_.PositionOffsetBaseNode())
text_state_.UpdatePositionOffsets(Strategy::Index(*base_node));
return text_state_.PositionStartOffset();
}
return end_offset_;
if (!text_state_.PositionNode())
return end_offset_;
EnsurePositionContainer();
return text_state_.PositionStartOffset();
}
template <typename Strategy>
int TextIteratorAlgorithm<Strategy>::EndOffsetInCurrentContainer() const {
if (const Node* node = text_state_.PositionNode()) {
if (const Node* base_node = text_state_.PositionOffsetBaseNode())
text_state_.UpdatePositionOffsets(Strategy::Index(*base_node));
return text_state_.PositionEndOffset();
}
return end_offset_;
if (!text_state_.PositionNode())
return end_offset_;
EnsurePositionContainer();
return text_state_.PositionEndOffset();
}
template <typename Strategy>
const Node* TextIteratorAlgorithm<Strategy>::CurrentContainer() const {
if (text_state_.PositionNode()) {
return text_state_.PositionNode();
}
return end_container_;
if (!text_state_.PositionNode())
return end_container_;
EnsurePositionContainer();
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>
......
......@@ -130,6 +130,10 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
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();
bool ShouldRepresentNodeOffsetZero();
bool ShouldEmitSpaceBeforeAndAfterNode(const Node&);
......@@ -141,11 +145,6 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
void HandleTextNode();
void HandleReplacedElement();
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
// between every VisiblePosition in the Range used to create the TextIterator.
......@@ -202,6 +201,9 @@ class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
unsigned position,
unsigned copy_length) const;
// Ensure container node of current text run for computing position.
void EnsurePositionContainer() const;
// The range.
const Member<const Node> start_container_;
const unsigned start_offset_;
......
......@@ -181,7 +181,7 @@ void TextIteratorTextNodeHandler::HandlePreFormattedTextNode() {
HasVisibleTextNode(layout_object)) {
if (!behavior_.CollapseTrailingSpace() ||
(offset_ > 0 && str[offset_ - 1] == ' ')) {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, offset_, offset_);
EmitChar16Before(kSpaceCharacter, offset_);
needs_handle_pre_formatted_text_node_ = true;
return;
}
......@@ -377,8 +377,7 @@ void TextIteratorTextNodeHandler::HandleTextBox() {
--space_run_start;
EmitText(layout_object, space_run_start, space_run_start + 1);
} else {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start,
run_start);
EmitChar16Before(kSpaceCharacter, run_start);
}
return;
}
......@@ -416,10 +415,9 @@ void TextIteratorTextNodeHandler::HandleTextBox() {
// We need to preserve new lines in case of PreLine.
// See bug crbug.com/317365.
if (layout_object->Style()->WhiteSpace() == EWhiteSpace::kPreLine) {
SpliceBuffer('\n', text_node_, nullptr, run_start, run_start);
EmitChar16Before('\n', run_start);
} else {
SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start,
run_start + 1);
EmitReplacmentCodeUnit(kSpaceCharacter, run_start);
}
offset_ = text_start_offset + run_start + 1;
} else {
......@@ -537,11 +535,10 @@ bool TextIteratorTextNodeHandler::ShouldFixLeadingWhiteSpaceForReplacedElement()
return offset_ > 0 && str[offset_ - 1] == ' ';
}
bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement(
const Node* parent) {
bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement() {
if (!ShouldFixLeadingWhiteSpaceForReplacedElement())
return false;
text_state_.SpliceBuffer(kSpaceCharacter, parent, text_node_, 1, 1);
text_state_.EmitChar16AfterNode(kSpaceCharacter, *text_node_);
ResetCollapsedWhiteSpaceFixup();
return true;
}
......@@ -552,13 +549,15 @@ void TextIteratorTextNodeHandler::ResetCollapsedWhiteSpaceFixup() {
last_text_node_ended_with_collapsed_space_ = false;
}
void TextIteratorTextNodeHandler::SpliceBuffer(UChar c,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset) {
text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
text_end_offset);
void TextIteratorTextNodeHandler::EmitChar16Before(UChar code_unit,
unsigned offset) {
text_state_.EmitChar16Before(code_unit, *text_node_, offset);
ResetCollapsedWhiteSpaceFixup();
}
void TextIteratorTextNodeHandler::EmitReplacmentCodeUnit(UChar code_unit,
unsigned offset) {
text_state_.EmitReplacmentCodeUnit(code_unit, *text_node_, offset);
ResetCollapsedWhiteSpaceFixup();
}
......
......@@ -32,7 +32,7 @@ class TextIteratorTextNodeHandler {
bool HandleRemainingTextRuns();
// Returns true if a leading white space is emitted before a replaced element.
bool FixLeadingWhiteSpaceForReplacedElement(const Node*);
bool FixLeadingWhiteSpaceForReplacedElement();
void ResetCollapsedWhiteSpaceFixup();
......@@ -65,12 +65,12 @@ class TextIteratorTextNodeHandler {
bool ShouldFixLeadingWhiteSpaceForReplacedElement() const;
// Emit a character before |offset| of characters in |text_node_|.
void SpliceBuffer(UChar,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset);
// Emits |code_unit| before |offset| of characters in |text_node_|.
void EmitChar16Before(UChar code_unit, unsigned offset);
// Emits |code_unit| as replacement of a code unit after |offset| in
// |text_node_|.
void EmitReplacmentCodeUnit(UChar code_unit, unsigned offset);
void EmitText(const LayoutText* layout_object,
unsigned text_start_offset,
unsigned text_end_offset);
......
......@@ -49,6 +49,16 @@ TextIteratorTextState::TextIteratorTextState(
const TextIteratorBehavior& 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 {
SECURITY_DCHECK(index < length());
if (!(index < length()))
......@@ -93,59 +103,116 @@ void TextIteratorTextState::AppendTextToStringBuilder(
}
}
void TextIteratorTextState::UpdateForReplacedElement(const Node& parent_node,
const Node& base_node) {
has_emitted_ = true;
position_node_ = &parent_node;
position_offset_base_node_ = &base_node;
position_start_offset_ = 0;
position_end_offset_ = 1;
single_character_buffer_ = 0;
void TextIteratorTextState::UpdateForReplacedElement(const Node& node) {
ResetPositionContainerNode(PositionNodeType::kAsNode, node);
PopulateStringBuffer("", 0, 0);
}
text_length_ = 0;
text_start_offset_ = 0;
last_character_ = 0;
void TextIteratorTextState::ResetPositionContainerNode(
PositionNodeType node_type,
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) {
text_ = ToHTMLElement(node)->AltText();
text_start_offset_ = 0;
text_length_ = text_.length();
last_character_ = text_length_ ? text_[text_length_ - 1] : 0;
void TextIteratorTextState::UpdatePositionOffsets(
const ContainerNode& container_node,
unsigned node_index) const {
DCHECK(!position_container_node_);
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 {
DCHECK(position_offset_base_node_);
position_start_offset_ += index;
position_end_offset_ += index;
position_offset_base_node_ = nullptr;
void TextIteratorTextState::EmitAltText(const HTMLElement& element) {
ResetPositionContainerNode(PositionNodeType::kAltText, element);
const String text = element.AltText();
PopulateStringBuffer(text, 0, text.length());
}
void TextIteratorTextState::SpliceBuffer(UChar c,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset) {
DCHECK(text_node);
has_emitted_ = true;
void TextIteratorTextState::EmitChar16AfterNode(UChar code_unit,
const Node& node) {
ResetPositionContainerNode(PositionNodeType::kAfterNode, node);
PopulateStringBufferFromChar16(code_unit);
}
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().
// NOTE: textNode is often not a text node, so the range will specify child
// nodes of positionNode
position_node_ = text_node;
position_offset_base_node_ = offset_base_node;
position_start_offset_ = text_start_offset;
position_end_offset_ = text_end_offset;
void TextIteratorTextState::EmitReplacmentCodeUnit(UChar code_unit,
const Text& text_node,
unsigned offset) {
SetTextNodePosition(text_node, offset, offset + 1);
PopulateStringBufferFromChar16(code_unit);
}
void TextIteratorTextState::PopulateStringBufferFromChar16(UChar code_unit) {
has_emitted_ = true;
// remember information with which to construct the TextIterator::characters()
// and length()
single_character_buffer_ = c;
single_character_buffer_ = code_unit;
DCHECK(single_character_buffer_);
text_length_ = 1;
text_start_offset_ = 0;
// remember some iteration state
last_character_ = c;
last_character_ = code_unit;
}
void TextIteratorTextState::EmitText(const Text& text_node,
......@@ -155,31 +222,48 @@ void TextIteratorTextState::EmitText(const Text& text_node,
unsigned text_start_offset,
unsigned text_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());
text_ =
const String text =
behavior_.EmitsSmallXForTextSecurity() && IsTextSecurityNode(text_node)
? RepeatString("x", string.length())
: string,
: string;
DCHECK(!text_.IsEmpty());
DCHECK_LT(text_start_offset, text_.length());
DCHECK_LE(text_end_offset, text_.length());
DCHECK(!text.IsEmpty());
DCHECK_LT(text_start_offset, text.length());
DCHECK_LE(text_end_offset, text.length());
DCHECK_LE(text_start_offset, text_end_offset);
position_node_ = &text_node;
position_offset_base_node_ = nullptr;
position_start_offset_ = position_start_offset;
position_end_offset_ = position_end_offset;
SetTextNodePosition(text_node, position_start_offset, position_end_offset);
PopulateStringBuffer(text, text_start_offset, text_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;
text_start_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;
}
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,
unsigned position,
unsigned length_to_append) const {
......
......@@ -26,6 +26,7 @@
#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_
#include "base/optional.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/text_iterator_behavior.h"
......@@ -35,6 +36,8 @@
namespace blink {
class BackwardsTextBuffer;
class ContainerNode;
class HTMLElement;
class Text;
class CORE_EXPORT TextIteratorTextState {
......@@ -57,28 +60,37 @@ class CORE_EXPORT TextIteratorTextState {
unsigned position,
unsigned length_to_prepend) const;
void SpliceBuffer(UChar,
const Node* text_node,
const Node* offset_base_node,
unsigned text_start_offset,
unsigned text_end_offset);
// Emits code unit relative to |node|.
void EmitChar16AfterNode(UChar code_unit, const Node& node);
void EmitChar16AsNode(UChar code_unit, const Node& node);
void EmitChar16BeforeChildren(UChar code_unit,
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&,
unsigned position_start_offset,
unsigned position_end_offset,
const String&,
unsigned text_start_offset,
unsigned text_end_offset);
void EmitAltText(const Node*);
void UpdateForReplacedElement(const Node& parent_node, const Node& base_node);
void EmitAltText(const HTMLElement&);
void UpdateForReplacedElement(const Node& node);
// Return position of the current text.
void UpdatePositionOffsets(unsigned node_index) const;
unsigned PositionStartOffset() const { return position_start_offset_; }
unsigned PositionEndOffset() const { return position_end_offset_; }
void UpdatePositionOffsets(const ContainerNode& container_node,
unsigned index) const;
unsigned PositionStartOffset() const;
unsigned PositionEndOffset() const;
const Node* PositionNode() const { return position_node_; }
const Node* PositionOffsetBaseNode() const {
return position_offset_base_node_;
}
const Node* PositionContainerNode() const { return position_container_node_; }
bool HasEmitted() const { return has_emitted_; }
UChar LastCharacter() const { return last_character_; }
......@@ -88,9 +100,29 @@ class CORE_EXPORT TextIteratorTextState {
}
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_;
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
// them.
// If non-zero, overrides |text_|.
......@@ -99,13 +131,17 @@ class CORE_EXPORT TextIteratorTextState {
// The current text when |single_character_buffer_| is zero, in which case it
// is |text_.Substring(text_start_offset_, text_length_)|.
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;
// Position of the current text, in the form to be returned from the iterator.
Member<const Node> position_node_;
mutable Member<const Node> position_offset_base_node_;
mutable unsigned position_start_offset_ = 0;
mutable unsigned position_end_offset_ = 0;
// |Text| node when |position_node_type_ == kInText| or |ContainerNode|.
mutable Member<const Node> position_container_node_;
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
// 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