blob: ec92d405e2f2857fc4a5548666bfacbb3dc29005 [file] [log] [blame]
/*
* Copyright (C) 2004, 2006, 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TextIterator_h
#define TextIterator_h
#include "core/CoreExport.h"
#include "core/dom/Range.h"
#include "core/editing/EphemeralRange.h"
#include "core/editing/FindOptions.h"
#include "core/editing/iterators/FullyClippedStateStack.h"
#include "core/editing/iterators/TextIteratorBehavior.h"
#include "core/editing/iterators/TextIteratorTextState.h"
#include "platform/heap/Handle.h"
#include "platform/wtf/Vector.h"
namespace blink {
class InlineTextBox;
class LayoutText;
class LayoutTextFragment;
CORE_EXPORT String
PlainText(const EphemeralRange&,
const TextIteratorBehavior& = TextIteratorBehavior());
String PlainText(const EphemeralRangeInFlatTree&,
const TextIteratorBehavior& = TextIteratorBehavior());
// Iterates through the DOM range, returning all the text, and 0-length
// boundaries at points where replaced elements break up the text flow. The
// text comes back in chunks so as to optimize for performance of the iteration.
template <typename Strategy>
class CORE_TEMPLATE_CLASS_EXPORT TextIteratorAlgorithm {
STACK_ALLOCATED();
public:
// [start, end] indicates the document range that the iteration should take
// place within (both ends inclusive).
TextIteratorAlgorithm(const PositionTemplate<Strategy>& start,
const PositionTemplate<Strategy>& end,
const TextIteratorBehavior& = TextIteratorBehavior());
~TextIteratorAlgorithm();
bool AtEnd() const { return !text_state_.PositionNode() || should_stop_; }
void Advance();
bool IsInsideAtomicInlineElement() const;
bool IsInTextSecurityMode() const;
EphemeralRangeTemplate<Strategy> Range() const;
Node* GetNode() const;
Document* OwnerDocument() const;
Node* CurrentContainer() const;
int StartOffsetInCurrentContainer() const;
int EndOffsetInCurrentContainer() const;
PositionTemplate<Strategy> StartPositionInCurrentContainer() const;
PositionTemplate<Strategy> EndPositionInCurrentContainer() const;
const TextIteratorTextState& GetText() const { return text_state_; }
int length() const { return text_state_.length(); }
UChar CharacterAt(unsigned index) const {
return text_state_.CharacterAt(index);
}
bool BreaksAtReplacedElement() {
return !behavior_.DoesNotBreakAtReplacedElement();
}
// Calculate the minimum |actualLength >= minLength| such that code units
// with offset range [position, position + actualLength) are whole code
// points. Append these code points to |output| and return |actualLength|.
// TODO(xiaochengh): Use (start, end) instead of (start, length).
int CopyTextTo(ForwardsTextBuffer* output,
int position,
int min_length) const;
// TODO(xiaochengh): Avoid default parameters.
int CopyTextTo(ForwardsTextBuffer* output, int position = 0) const;
// Computes the length of the given range using a text iterator according to
// the specified iteration behavior. The default iteration behavior is to
// always emit object replacement characters for replaced elements.
static int RangeLength(
const PositionTemplate<Strategy>& start,
const PositionTemplate<Strategy>& end,
const TextIteratorBehavior& =
TextIteratorBehavior::DefaultRangeLengthBehavior());
static bool ShouldEmitTabBeforeNode(Node*);
static bool ShouldEmitNewlineBeforeNode(Node&);
static bool ShouldEmitNewlineAfterNode(Node&);
static bool ShouldEmitNewlineForNode(Node*, bool emits_original_text);
static bool SupportsAltText(Node*);
private:
enum IterationProgress {
kHandledNone,
kHandledOpenShadowRoots,
kHandledUserAgentShadowRoot,
kHandledNode,
kHandledChildren
};
void Initialize(Node* start_container,
int start_offset,
Node* end_container,
int end_offset);
void ExitNode();
bool ShouldRepresentNodeOffsetZero();
bool ShouldEmitSpaceBeforeAndAfterNode(Node*);
void RepresentNodeOffsetZero();
bool HandleTextNode();
bool HandleReplacedElement();
bool HandleNonTextNode();
void HandleTextBox();
void HandleTextNodeFirstLetter(LayoutTextFragment*);
bool ShouldHandleFirstLetter(const LayoutText&) const;
bool ShouldProceedToRemainingText() const;
void ProceedToRemainingText();
// Helper function during initialization. Returns true if the start position
// is in a text node with first-letter, in which case it also sets up related
// parameters. Returns false otherwise.
bool PrepareForFirstLetterInitialization();
bool HasNotAdvancedToStartPosition();
int AdjustedStartForFirstLetter(const Node&, const LayoutText&, int, int);
int AdjustedStartForRemainingText(const Node&, const LayoutText&, int, int);
void SpliceBuffer(UChar,
Node* text_node,
Node* offset_base_node,
int text_start_offset,
int text_end_offset);
void EmitText(Node* text_node,
LayoutText* layout_object,
int text_start_offset,
int text_end_offset);
size_t RestoreCollapsedTrailingSpace(InlineTextBox* next_text_box,
size_t subrun_end);
// Used by selection preservation code. There should be one character emitted
// between every VisiblePosition in the Range used to create the TextIterator.
// FIXME <rdar://problem/6028818>: This functionality should eventually be
// phased out when we rewrite moveParagraphs to not clone/destroy moved
// content.
bool EmitsCharactersBetweenAllVisiblePositions() const {
return behavior_.EmitsCharactersBetweenAllVisiblePositions();
}
bool EntersTextControls() const { return behavior_.EntersTextControls(); }
// Used in pasting inside password field.
bool EmitsOriginalText() const { return behavior_.EmitsOriginalText(); }
// Used when the visibility of the style should not affect text gathering.
bool IgnoresStyleVisibility() const {
return behavior_.IgnoresStyleVisibility();
}
// Used when the iteration should stop if form controls are reached.
bool StopsOnFormControls() const { return behavior_.StopsOnFormControls(); }
bool EmitsImageAltText() const { return behavior_.EmitsImageAltText(); }
bool EntersOpenShadowRoots() const {
return behavior_.EntersOpenShadowRoots();
}
bool EmitsObjectReplacementCharacter() const {
return behavior_.EmitsObjectReplacementCharacter();
}
bool ExcludesAutofilledValue() const {
return behavior_.ExcludeAutofilledValue();
}
bool DoesNotBreakAtReplacedElement() const {
return behavior_.DoesNotBreakAtReplacedElement();
}
bool ForInnerText() const { return behavior_.ForInnerText(); }
bool IsBetweenSurrogatePair(int position) const;
// Append code units with offset range [position, position + copyLength)
// to the output buffer.
void CopyCodeUnitsTo(ForwardsTextBuffer* output,
int position,
int copy_length) const;
// Current position, not necessarily of the text being returned, but position
// as we walk through the DOM tree.
Member<Node> node_;
int offset_;
IterationProgress iteration_progress_;
FullyClippedStateStackAlgorithm<Strategy> fully_clipped_stack_;
int shadow_depth_;
// The range.
Member<Node> start_container_;
int start_offset_;
Member<Node> end_container_;
int end_offset_;
// |m_endNode| stores |Strategy::childAt(*m_endContainer, m_endOffset - 1)|,
// if it exists, or |nullptr| otherwise.
Member<Node> end_node_;
Member<Node> past_end_node_;
// Used when there is still some pending text from the current node; when
// these are false and 0, we go back to normal iterating.
bool needs_another_newline_;
InlineTextBox* text_box_;
// Used when iteration over :first-letter text to save pointer to
// remaining text box.
InlineTextBox* remaining_text_box_;
// Used to point to LayoutText object for :first-letter.
LayoutText* first_letter_text_;
// Used to do the whitespace collapsing logic.
Member<Text> last_text_node_;
bool last_text_node_ended_with_collapsed_space_;
// Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
Vector<InlineTextBox*> sorted_text_boxes_;
size_t sorted_text_boxes_position_;
const TextIteratorBehavior behavior_;
// Used when deciding text fragment created by :first-letter should be looked
// into.
bool handled_first_letter_;
// Used when stopsOnFormControls() is true to determine if the iterator should
// keep advancing.
bool should_stop_;
// Used for use counter |InnerTextWithShadowTree| and
// |SelectionToStringWithShadowTree|, we should not use other purpose.
bool handle_shadow_root_;
// Used for adjusting the initialization and the output when the start
// container is a text node with :first-letter.
int first_letter_start_offset_;
int remaining_text_start_offset_;
// Contains state of emitted text.
TextIteratorTextState text_state_;
};
extern template class CORE_EXTERN_TEMPLATE_EXPORT
TextIteratorAlgorithm<EditingStrategy>;
extern template class CORE_EXTERN_TEMPLATE_EXPORT
TextIteratorAlgorithm<EditingInFlatTreeStrategy>;
using TextIterator = TextIteratorAlgorithm<EditingStrategy>;
using TextIteratorInFlatTree = TextIteratorAlgorithm<EditingInFlatTreeStrategy>;
} // namespace blink
#endif // TextIterator_h