| Index: third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
|
| diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
|
| index 63417b445365c5aa86ad7e1cfd85c5a76d4ebf6e..5305f47320a0098b4cfb3fdffa46bc0988ceacb9 100644
|
| --- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
|
| @@ -28,12 +28,10 @@
|
| #include "core/editing/iterators/TextIterator.h"
|
|
|
| #include <unicode/utf16.h>
|
| -#include <algorithm>
|
| #include "bindings/core/v8/ExceptionState.h"
|
| #include "core/HTMLNames.h"
|
| #include "core/InputTypeNames.h"
|
| #include "core/dom/Document.h"
|
| -#include "core/dom/FirstLetterPseudoElement.h"
|
| #include "core/dom/shadow/ShadowRoot.h"
|
| #include "core/editing/EditingUtilities.h"
|
| #include "core/editing/EphemeralRange.h"
|
| @@ -50,8 +48,6 @@
|
| #include "core/html/TextControlElement.h"
|
| #include "core/layout/LayoutTableCell.h"
|
| #include "core/layout/LayoutTableRow.h"
|
| -#include "core/layout/LayoutTextFragment.h"
|
| -#include "core/layout/line/InlineTextBox.h"
|
| #include "platform/fonts/Font.h"
|
| #include "platform/wtf/text/CString.h"
|
| #include "platform/wtf/text/StringBuilder.h"
|
| @@ -152,11 +148,6 @@ bool IsRenderedAsTable(const Node* node) {
|
|
|
| } // namespace
|
|
|
| -TextIteratorTextNodeHandler::TextIteratorTextNodeHandler(
|
| - const TextIteratorBehavior& behavior,
|
| - TextIteratorTextState* text_state)
|
| - : behavior_(behavior), text_state_(*text_state) {}
|
| -
|
| template <typename Strategy>
|
| TextIteratorAlgorithm<Strategy>::TextIteratorAlgorithm(
|
| const PositionTemplate<Strategy>& start,
|
| @@ -193,22 +184,6 @@ TextIteratorAlgorithm<Strategy>::TextIteratorAlgorithm(
|
| end.ComputeContainerNode(), end.ComputeOffsetInContainerNode());
|
| }
|
|
|
| -void TextIteratorTextNodeHandler::Initialize(Node* start_container,
|
| - int start_offset,
|
| - Node* end_container,
|
| - int end_offset) {
|
| - // This function should be called only once.
|
| - DCHECK(!start_container_);
|
| - DCHECK_EQ(start_offset_, 0);
|
| - DCHECK(!end_container_);
|
| - DCHECK_EQ(end_offset_, 0);
|
| -
|
| - start_container_ = start_container;
|
| - start_offset_ = start_offset;
|
| - end_container_ = end_container;
|
| - end_offset_ = end_offset;
|
| -}
|
| -
|
| template <typename Strategy>
|
| void TextIteratorAlgorithm<Strategy>::Initialize(Node* start_container,
|
| int start_offset,
|
| @@ -281,21 +256,6 @@ bool TextIteratorAlgorithm<Strategy>::IsInsideAtomicInlineElement() const {
|
| return layout_object && layout_object->IsAtomicInlineLevel();
|
| }
|
|
|
| -bool TextIteratorTextNodeHandler::HandleRemainingTextRuns() {
|
| - if (ShouldProceedToRemainingText())
|
| - ProceedToRemainingText();
|
| - // Handle remembered text box
|
| - if (text_box_) {
|
| - HandleTextBox();
|
| - return text_state_.PositionNode();
|
| - }
|
| - // Handle remembered pre-formatted text node.
|
| - if (!needs_handle_pre_formatted_text_node_)
|
| - return false;
|
| - HandlePreFormattedTextNode();
|
| - return text_state_.PositionNode();
|
| -}
|
| -
|
| template <typename Strategy>
|
| void TextIteratorAlgorithm<Strategy>::Advance() {
|
| if (should_stop_)
|
| @@ -508,91 +468,6 @@ void TextIteratorAlgorithm<Strategy>::Advance() {
|
| }
|
| }
|
|
|
| -static bool HasVisibleTextNode(LayoutText* layout_object) {
|
| - if (layout_object->Style()->Visibility() == EVisibility::kVisible)
|
| - return true;
|
| -
|
| - if (!layout_object->IsTextFragment())
|
| - return false;
|
| -
|
| - LayoutTextFragment* fragment = ToLayoutTextFragment(layout_object);
|
| - if (!fragment->IsRemainingTextLayoutObject())
|
| - return false;
|
| -
|
| - DCHECK(fragment->GetFirstLetterPseudoElement());
|
| - LayoutObject* pseudo_element_layout_object =
|
| - fragment->GetFirstLetterPseudoElement()->GetLayoutObject();
|
| - return pseudo_element_layout_object &&
|
| - pseudo_element_layout_object->Style()->Visibility() ==
|
| - EVisibility::kVisible;
|
| -}
|
| -
|
| -bool TextIteratorTextNodeHandler::ShouldHandleFirstLetter(
|
| - const LayoutText& layout_text) const {
|
| - if (handled_first_letter_)
|
| - return false;
|
| - if (!layout_text.IsTextFragment())
|
| - return false;
|
| - const LayoutTextFragment& text_fragment = ToLayoutTextFragment(layout_text);
|
| - return offset_ < static_cast<int>(text_fragment.TextStartOffset());
|
| -}
|
| -
|
| -void TextIteratorTextNodeHandler::HandlePreFormattedTextNode() {
|
| - // TODO(xiaochengh): Get rid of repeated computation of these fields.
|
| - LayoutText* const layout_object = text_node_->GetLayoutObject();
|
| - const String str = layout_object->GetText();
|
| -
|
| - needs_handle_pre_formatted_text_node_ = false;
|
| -
|
| - if (last_text_node_ended_with_collapsed_space_ &&
|
| - HasVisibleTextNode(layout_object)) {
|
| - if (!behavior_.CollapseTrailingSpace() ||
|
| - (offset_ > 0 && str[offset_ - 1] == ' ')) {
|
| - SpliceBuffer(kSpaceCharacter, text_node_, 0, offset_, offset_);
|
| - needs_handle_pre_formatted_text_node_ = true;
|
| - return;
|
| - }
|
| - }
|
| - if (ShouldHandleFirstLetter(*layout_object)) {
|
| - HandleTextNodeFirstLetter(ToLayoutTextFragment(layout_object));
|
| - if (first_letter_text_) {
|
| - const String first_letter = first_letter_text_->GetText();
|
| - const unsigned run_start = offset_;
|
| - const bool stops_in_first_letter =
|
| - text_node_ == end_container_ &&
|
| - end_offset_ <= static_cast<int>(first_letter.length());
|
| - const unsigned run_end =
|
| - stops_in_first_letter ? end_offset_ : first_letter.length();
|
| - EmitText(text_node_, first_letter_text_, run_start, run_end);
|
| - first_letter_text_ = nullptr;
|
| - text_box_ = 0;
|
| - offset_ = run_end;
|
| - if (!stops_in_first_letter)
|
| - needs_handle_pre_formatted_text_node_ = true;
|
| - return;
|
| - }
|
| - // We are here only if the DOM and/or layout trees are broken.
|
| - // For robustness, we should stop processing this node.
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| - if (layout_object->Style()->Visibility() != EVisibility::kVisible &&
|
| - !IgnoresStyleVisibility())
|
| - return;
|
| - DCHECK_GE(static_cast<unsigned>(offset_), layout_object->TextStartOffset());
|
| - const unsigned run_start = offset_ - layout_object->TextStartOffset();
|
| - const unsigned str_length = str.length();
|
| - const unsigned end = (text_node_ == end_container_)
|
| - ? end_offset_ - layout_object->TextStartOffset()
|
| - : str_length;
|
| - const unsigned run_end = std::min(str_length, end);
|
| -
|
| - if (run_start >= run_end)
|
| - return;
|
| -
|
| - EmitText(text_node_, text_node_->GetLayoutObject(), run_start, run_end);
|
| -}
|
| -
|
| template <typename Strategy>
|
| bool TextIteratorAlgorithm<Strategy>::HandleTextNode() {
|
| if (ExcludesAutofilledValue()) {
|
| @@ -609,267 +484,6 @@ bool TextIteratorAlgorithm<Strategy>::HandleTextNode() {
|
| return text_node_handler_.HandleTextNode(ToText(node_));
|
| }
|
|
|
| -bool TextIteratorTextNodeHandler::HandleTextNode(Text* node) {
|
| - text_node_ = node;
|
| - offset_ = text_node_ == start_container_ ? start_offset_ : 0;
|
| - handled_first_letter_ = false;
|
| - first_letter_text_ = nullptr;
|
| -
|
| - LayoutText* layout_object = text_node_->GetLayoutObject();
|
| - String str = layout_object->GetText();
|
| -
|
| - // handle pre-formatted text
|
| - if (!layout_object->Style()->CollapseWhiteSpace()) {
|
| - HandlePreFormattedTextNode();
|
| - return true;
|
| - }
|
| -
|
| - if (layout_object->FirstTextBox())
|
| - text_box_ = layout_object->FirstTextBox();
|
| -
|
| - const bool should_handle_first_letter =
|
| - ShouldHandleFirstLetter(*layout_object);
|
| - if (should_handle_first_letter)
|
| - HandleTextNodeFirstLetter(ToLayoutTextFragment(layout_object));
|
| -
|
| - if (!layout_object->FirstTextBox() && str.length() > 0 &&
|
| - !should_handle_first_letter) {
|
| - if (layout_object->Style()->Visibility() != EVisibility::kVisible &&
|
| - !IgnoresStyleVisibility())
|
| - return false;
|
| - last_text_node_ended_with_collapsed_space_ =
|
| - true; // entire block is collapsed space
|
| - return true;
|
| - }
|
| -
|
| - if (first_letter_text_)
|
| - layout_object = first_letter_text_;
|
| -
|
| - // Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
|
| - if (layout_object->ContainsReversedText()) {
|
| - sorted_text_boxes_.clear();
|
| - for (InlineTextBox* text_box = layout_object->FirstTextBox(); text_box;
|
| - text_box = text_box->NextTextBox()) {
|
| - sorted_text_boxes_.push_back(text_box);
|
| - }
|
| - std::sort(sorted_text_boxes_.begin(), sorted_text_boxes_.end(),
|
| - InlineTextBox::CompareByStart);
|
| - sorted_text_boxes_position_ = 0;
|
| - text_box_ = sorted_text_boxes_.IsEmpty() ? 0 : sorted_text_boxes_[0];
|
| - }
|
| -
|
| - HandleTextBox();
|
| - return true;
|
| -}
|
| -
|
| -// Restore the collapsed space for copy & paste. See http://crbug.com/318925
|
| -size_t TextIteratorTextNodeHandler::RestoreCollapsedTrailingSpace(
|
| - InlineTextBox* next_text_box,
|
| - size_t subrun_end) {
|
| - if (next_text_box || !text_box_->Root().NextRootBox() ||
|
| - text_box_->Root().LastChild() != text_box_)
|
| - return subrun_end;
|
| -
|
| - const String& text = text_node_->GetLayoutObject()->GetText();
|
| - if (text.EndsWith(' ') == 0 || subrun_end != text.length() - 1 ||
|
| - text[subrun_end - 1] == ' ')
|
| - return subrun_end;
|
| -
|
| - // If there is the leading space in the next line, we don't need to restore
|
| - // the trailing space.
|
| - // Example: <div style="width: 2em;"><b><i>foo </i></b> bar</div>
|
| - InlineBox* first_box_of_next_line =
|
| - text_box_->Root().NextRootBox()->FirstChild();
|
| - if (!first_box_of_next_line)
|
| - return subrun_end + 1;
|
| - Node* first_node_of_next_line =
|
| - first_box_of_next_line->GetLineLayoutItem().GetNode();
|
| - if (!first_node_of_next_line ||
|
| - first_node_of_next_line->nodeValue()[0] != ' ')
|
| - return subrun_end + 1;
|
| -
|
| - return subrun_end;
|
| -}
|
| -
|
| -void TextIteratorTextNodeHandler::HandleTextBox() {
|
| - LayoutText* layout_object =
|
| - first_letter_text_ ? first_letter_text_ : text_node_->GetLayoutObject();
|
| - const unsigned text_start_offset = layout_object->TextStartOffset();
|
| -
|
| - if (layout_object->Style()->Visibility() != EVisibility::kVisible &&
|
| - !IgnoresStyleVisibility()) {
|
| - text_box_ = nullptr;
|
| - } else {
|
| - String str = layout_object->GetText();
|
| - // Start and end offsets in |str|, i.e., str[start..end - 1] should be
|
| - // emitted (after handling whitespace collapsing).
|
| - const unsigned start = offset_ - layout_object->TextStartOffset();
|
| - const unsigned end =
|
| - (text_node_ == end_container_)
|
| - ? static_cast<unsigned>(end_offset_) - text_start_offset
|
| - : INT_MAX;
|
| - while (text_box_) {
|
| - const unsigned text_box_start = text_box_->Start();
|
| - const unsigned run_start = std::max(text_box_start, start);
|
| -
|
| - // Check for collapsed space at the start of this run.
|
| - InlineTextBox* first_text_box =
|
| - layout_object->ContainsReversedText()
|
| - ? (sorted_text_boxes_.IsEmpty() ? 0 : sorted_text_boxes_[0])
|
| - : layout_object->FirstTextBox();
|
| - const bool need_space = last_text_node_ended_with_collapsed_space_ ||
|
| - (text_box_ == first_text_box &&
|
| - text_box_start == run_start && run_start > 0);
|
| - if (need_space &&
|
| - !layout_object->Style()->IsCollapsibleWhiteSpace(
|
| - text_state_.LastCharacter()) &&
|
| - text_state_.LastCharacter()) {
|
| - if (run_start > 0 && str[run_start - 1] == ' ') {
|
| - unsigned space_run_start = run_start - 1;
|
| - while (space_run_start > 0 && str[space_run_start - 1] == ' ')
|
| - --space_run_start;
|
| - EmitText(text_node_, layout_object, space_run_start,
|
| - space_run_start + 1);
|
| - } else {
|
| - SpliceBuffer(kSpaceCharacter, text_node_, 0, run_start, run_start);
|
| - }
|
| - return;
|
| - }
|
| - const unsigned text_box_end = text_box_start + text_box_->Len();
|
| - const unsigned run_end = std::min(text_box_end, end);
|
| -
|
| - // Determine what the next text box will be, but don't advance yet
|
| - InlineTextBox* next_text_box = nullptr;
|
| - if (layout_object->ContainsReversedText()) {
|
| - if (sorted_text_boxes_position_ + 1 < sorted_text_boxes_.size())
|
| - next_text_box = sorted_text_boxes_[sorted_text_boxes_position_ + 1];
|
| - } else {
|
| - next_text_box = text_box_->NextTextBox();
|
| - }
|
| -
|
| - // FIXME: Based on the outcome of crbug.com/446502 it's possible we can
|
| - // remove this block. The reason we new it now is because BIDI and
|
| - // FirstLetter seem to have different ideas of where things can split.
|
| - // FirstLetter takes the punctuation + first letter, and BIDI will
|
| - // split out the punctuation and possibly reorder it.
|
| - if (next_text_box &&
|
| - !(next_text_box->GetLineLayoutItem().IsEqual(layout_object))) {
|
| - text_box_ = 0;
|
| - return;
|
| - }
|
| - DCHECK(!next_text_box ||
|
| - next_text_box->GetLineLayoutItem().IsEqual(layout_object));
|
| -
|
| - if (run_start < run_end) {
|
| - // Handle either a single newline character (which becomes a space),
|
| - // or a run of characters that does not include a newline.
|
| - // This effectively translates newlines to spaces without copying the
|
| - // text.
|
| - if (str[run_start] == '\n') {
|
| - // 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_, 0, run_start, run_start);
|
| - } else {
|
| - SpliceBuffer(kSpaceCharacter, text_node_, 0, run_start,
|
| - run_start + 1);
|
| - }
|
| - offset_ = text_start_offset + run_start + 1;
|
| - } else {
|
| - size_t subrun_end = str.find('\n', run_start);
|
| - if (subrun_end == kNotFound || subrun_end > run_end) {
|
| - subrun_end = run_end;
|
| - subrun_end =
|
| - RestoreCollapsedTrailingSpace(next_text_box, subrun_end);
|
| - }
|
| -
|
| - offset_ = text_start_offset + subrun_end;
|
| - EmitText(text_node_, layout_object, run_start, subrun_end);
|
| - }
|
| -
|
| - // If we are doing a subrun that doesn't go to the end of the text box,
|
| - // come back again to finish handling this text box; don't advance to
|
| - // the next one.
|
| - if (static_cast<unsigned>(text_state_.PositionEndOffset()) <
|
| - text_box_end)
|
| - return;
|
| -
|
| - if (behavior_.DoesNotEmitSpaceBeyondRangeEnd()) {
|
| - // If the subrun went to the text box end and this end is also the end
|
| - // of the range, do not advance to the next text box and do not
|
| - // generate a space, just stop.
|
| - if (text_box_end == end) {
|
| - text_box_ = nullptr;
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // Advance and return
|
| - const unsigned next_run_start =
|
| - next_text_box ? next_text_box->Start() : str.length();
|
| - if (next_run_start > run_end)
|
| - last_text_node_ended_with_collapsed_space_ =
|
| - true; // collapsed space between runs or at the end
|
| -
|
| - text_box_ = next_text_box;
|
| - if (layout_object->ContainsReversedText())
|
| - ++sorted_text_boxes_position_;
|
| - return;
|
| - }
|
| - // Advance and continue
|
| - text_box_ = next_text_box;
|
| - if (layout_object->ContainsReversedText())
|
| - ++sorted_text_boxes_position_;
|
| - }
|
| - }
|
| -
|
| - if (ShouldProceedToRemainingText()) {
|
| - ProceedToRemainingText();
|
| - HandleTextBox();
|
| - }
|
| -}
|
| -
|
| -bool TextIteratorTextNodeHandler::ShouldProceedToRemainingText() const {
|
| - if (text_box_ || !remaining_text_box_)
|
| - return false;
|
| - if (text_node_ != end_container_)
|
| - return true;
|
| - return offset_ < end_offset_;
|
| -}
|
| -
|
| -void TextIteratorTextNodeHandler::ProceedToRemainingText() {
|
| - text_box_ = remaining_text_box_;
|
| - remaining_text_box_ = 0;
|
| - first_letter_text_ = nullptr;
|
| - offset_ = text_node_->GetLayoutObject()->TextStartOffset();
|
| -}
|
| -
|
| -void TextIteratorTextNodeHandler::HandleTextNodeFirstLetter(
|
| - LayoutTextFragment* layout_object) {
|
| - handled_first_letter_ = true;
|
| -
|
| - if (!layout_object->IsRemainingTextLayoutObject())
|
| - return;
|
| -
|
| - FirstLetterPseudoElement* first_letter_element =
|
| - layout_object->GetFirstLetterPseudoElement();
|
| - if (!first_letter_element)
|
| - return;
|
| -
|
| - LayoutObject* pseudo_layout_object = first_letter_element->GetLayoutObject();
|
| - if (pseudo_layout_object->Style()->Visibility() != EVisibility::kVisible &&
|
| - !IgnoresStyleVisibility())
|
| - return;
|
| -
|
| - LayoutObject* first_letter = pseudo_layout_object->SlowFirstChild();
|
| -
|
| - sorted_text_boxes_.clear();
|
| - remaining_text_box_ = text_box_;
|
| - CHECK(first_letter && first_letter->IsText());
|
| - first_letter_text_ = ToLayoutText(first_letter);
|
| - text_box_ = first_letter_text_->FirstTextBox();
|
| -}
|
| -
|
| template <typename Strategy>
|
| bool TextIteratorAlgorithm<Strategy>::SupportsAltText(Node* node) {
|
| if (!node->IsHTMLElement())
|
| @@ -885,28 +499,6 @@ bool TextIteratorAlgorithm<Strategy>::SupportsAltText(Node* node) {
|
| return false;
|
| }
|
|
|
| -bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement(
|
| - Node* parent) {
|
| - // This is a hacky way for white space fixup in legacy layout. With LayoutNG,
|
| - // we can get rid of this function.
|
| -
|
| - if (behavior_.CollapseTrailingSpace()) {
|
| - if (text_node_) {
|
| - String str = text_node_->GetLayoutObject()->GetText();
|
| - if (last_text_node_ended_with_collapsed_space_ && offset_ > 0 &&
|
| - str[offset_ - 1] == ' ') {
|
| - SpliceBuffer(kSpaceCharacter, parent, text_node_, 1, 1);
|
| - return true;
|
| - }
|
| - }
|
| - } else if (last_text_node_ended_with_collapsed_space_) {
|
| - SpliceBuffer(kSpaceCharacter, parent, text_node_, 1, 1);
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| template <typename Strategy>
|
| bool TextIteratorAlgorithm<Strategy>::HandleReplacedElement() {
|
| if (fully_clipped_stack_.Top())
|
| @@ -1227,12 +819,6 @@ void TextIteratorAlgorithm<Strategy>::ExitNode() {
|
| 1);
|
| }
|
|
|
| -void TextIteratorTextNodeHandler::ResetCollapsedWhiteSpaceFixup() {
|
| - // This is a hacky way for white space fixup in legacy layout. With LayoutNG,
|
| - // we can get rid of this function.
|
| - last_text_node_ended_with_collapsed_space_ = false;
|
| -}
|
| -
|
| template <typename Strategy>
|
| void TextIteratorAlgorithm<Strategy>::SpliceBuffer(UChar c,
|
| Node* text_node,
|
| @@ -1244,25 +830,6 @@ void TextIteratorAlgorithm<Strategy>::SpliceBuffer(UChar c,
|
| text_node_handler_.ResetCollapsedWhiteSpaceFixup();
|
| }
|
|
|
| -void TextIteratorTextNodeHandler::SpliceBuffer(UChar c,
|
| - Node* text_node,
|
| - Node* offset_base_node,
|
| - int text_start_offset,
|
| - int text_end_offset) {
|
| - text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
|
| - text_end_offset);
|
| - ResetCollapsedWhiteSpaceFixup();
|
| -}
|
| -
|
| -void TextIteratorTextNodeHandler::EmitText(Node* text_node,
|
| - LayoutText* layout_object,
|
| - int text_start_offset,
|
| - int text_end_offset) {
|
| - text_state_.EmitText(text_node, layout_object, text_start_offset,
|
| - text_end_offset);
|
| - ResetCollapsedWhiteSpaceFixup();
|
| -}
|
| -
|
| template <typename Strategy>
|
| EphemeralRangeTemplate<Strategy> TextIteratorAlgorithm<Strategy>::Range()
|
| const {
|
|
|