summaryrefslogtreecommitdiffstats
path: root/WebCore/editing/TextIterator.cpp
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2009-08-11 17:01:47 +0100
committerBen Murdoch <benm@google.com>2009-08-11 18:21:02 +0100
commit0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch)
tree2943df35f62d885c89d01063cc528dd73b480fea /WebCore/editing/TextIterator.cpp
parent7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff)
downloadexternal_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.zip
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.gz
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.bz2
Merge in WebKit r47029.
Diffstat (limited to 'WebCore/editing/TextIterator.cpp')
-rw-r--r--WebCore/editing/TextIterator.cpp344
1 files changed, 256 insertions, 88 deletions
diff --git a/WebCore/editing/TextIterator.cpp b/WebCore/editing/TextIterator.cpp
index b311853..08fda16 100644
--- a/WebCore/editing/TextIterator.cpp
+++ b/WebCore/editing/TextIterator.cpp
@@ -42,6 +42,7 @@
#include "visible_units.h"
#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION
+#include "TextBreakIteratorInternalICU.h"
#include <unicode/usearch.h>
#endif
@@ -56,7 +57,7 @@ using namespace HTMLNames;
// Keeps enough of the previous text to be able to search in the future, but no more.
// Non-breaking spaces are always equal to normal spaces.
// Case folding is also done if <isCaseSensitive> is false.
-class SearchBuffer : Noncopyable {
+class SearchBuffer : public Noncopyable {
public:
SearchBuffer(const String& target, bool isCaseSensitive);
~SearchBuffer();
@@ -97,6 +98,148 @@ private:
// --------
+static const unsigned bitsInWord = sizeof(unsigned) * 8;
+static const unsigned bitInWordMask = bitsInWord - 1;
+
+BitStack::BitStack()
+ : m_size(0)
+{
+}
+
+void BitStack::push(bool bit)
+{
+ unsigned index = m_size / bitsInWord;
+ unsigned shift = m_size & bitInWordMask;
+ if (!shift && index == m_words.size()) {
+ m_words.grow(index + 1);
+ m_words[index] = 0;
+ }
+ unsigned& word = m_words[index];
+ unsigned mask = 1U << shift;
+ if (bit)
+ word |= mask;
+ else
+ word &= ~mask;
+ ++m_size;
+}
+
+void BitStack::pop()
+{
+ if (m_size)
+ --m_size;
+}
+
+bool BitStack::top() const
+{
+ if (!m_size)
+ return false;
+ unsigned shift = (m_size - 1) & bitInWordMask;
+ return m_words.last() & (1U << shift);
+}
+
+unsigned BitStack::size() const
+{
+ return m_size;
+}
+
+// --------
+
+static inline Node* parentCrossingShadowBoundaries(Node* node)
+{
+ if (Node* parent = node->parentNode())
+ return parent;
+ return node->shadowParentNode();
+}
+
+#ifndef NDEBUG
+
+static unsigned depthCrossingShadowBoundaries(Node* node)
+{
+ unsigned depth = 0;
+ for (Node* parent = parentCrossingShadowBoundaries(node); parent; parent = parentCrossingShadowBoundaries(parent))
+ ++depth;
+ return depth;
+}
+
+#endif
+
+// This function is like Range::pastLastNode, except for the fact that it can climb up out of shadow trees.
+static Node* nextInPreOrderCrossingShadowBoundaries(Node* rangeEndContainer, int rangeEndOffset)
+{
+ if (!rangeEndContainer)
+ return 0;
+ if (rangeEndOffset >= 0 && !rangeEndContainer->offsetInCharacters()) {
+ if (Node* next = rangeEndContainer->childNode(rangeEndOffset))
+ return next;
+ }
+ for (Node* node = rangeEndContainer; node; node = parentCrossingShadowBoundaries(node)) {
+ if (Node* next = node->nextSibling())
+ return next;
+ }
+ return 0;
+}
+
+static Node* previousInPostOrderCrossingShadowBoundaries(Node* rangeStartContainer, int rangeStartOffset)
+{
+ if (!rangeStartContainer)
+ return 0;
+ if (rangeStartOffset > 0 && !rangeStartContainer->offsetInCharacters()) {
+ if (Node* previous = rangeStartContainer->childNode(rangeStartOffset - 1))
+ return previous;
+ }
+ for (Node* node = rangeStartContainer; node; node = parentCrossingShadowBoundaries(node)) {
+ if (Node* previous = node->previousSibling())
+ return previous;
+ }
+ return 0;
+}
+
+// --------
+
+static inline bool fullyClipsContents(Node* node)
+{
+ RenderObject* renderer = node->renderer();
+ if (!renderer || !renderer->isBox() || !renderer->hasOverflowClip())
+ return false;
+ return toRenderBox(renderer)->size().isEmpty();
+}
+
+static inline bool ignoresContainerClip(Node* node)
+{
+ RenderObject* renderer = node->renderer();
+ if (!renderer || renderer->isText())
+ return false;
+ EPosition position = renderer->style()->position();
+ return position == AbsolutePosition || position == FixedPosition;
+}
+
+static void pushFullyClippedState(BitStack& stack, Node* node)
+{
+ ASSERT(stack.size() == depthCrossingShadowBoundaries(node));
+
+ // Push true if this node full clips its contents, or if a parent already has fully
+ // clipped and this is not a node that ignores its container's clip.
+ stack.push(fullyClipsContents(node) || stack.top() && !ignoresContainerClip(node));
+}
+
+static void setUpFullyClippedStack(BitStack& stack, Node* node)
+{
+ // Put the nodes in a vector so we can iterate in reverse order.
+ Vector<Node*, 100> ancestry;
+ for (Node* parent = parentCrossingShadowBoundaries(node); parent; parent = parentCrossingShadowBoundaries(parent))
+ ancestry.append(parent);
+
+ // Call pushFullyClippedState on each node starting with the earliest ancestor.
+ size_t size = ancestry.size();
+ for (size_t i = 0; i < size; ++i)
+ pushFullyClippedState(stack, ancestry[size - i - 1]);
+ pushFullyClippedState(stack, node);
+
+ ASSERT(stack.size() == 1 + depthCrossingShadowBoundaries(node));
+}
+
+// --------
+
TextIterator::TextIterator()
: m_startContainer(0)
, m_startOffset(0)
@@ -112,8 +255,7 @@ TextIterator::TextIterator()
}
TextIterator::TextIterator(const Range* r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls)
- : m_inShadowContent(false)
- , m_startContainer(0)
+ : m_startContainer(0)
, m_startOffset(0)
, m_endContainer(0)
, m_endOffset(0)
@@ -144,23 +286,17 @@ TextIterator::TextIterator(const Range* r, bool emitCharactersBetweenAllVisibleP
m_endContainer = endContainer;
m_endOffset = endOffset;
- for (Node* n = startContainer; n; n = n->parentNode()) {
- if (n->isShadowNode()) {
- m_inShadowContent = true;
- break;
- }
- }
-
// set up the current node for processing
m_node = r->firstNode();
- if (m_node == 0)
+ if (!m_node)
return;
+ setUpFullyClippedStack(m_fullyClippedStack, m_node);
m_offset = m_node == m_startContainer ? m_startOffset : 0;
m_handledNode = false;
m_handledChildren = false;
// calculate first out of bounds node
- m_pastEndNode = r->pastLastNode();
+ m_pastEndNode = nextInPreOrderCrossingShadowBoundaries(endContainer, endOffset);
// initialize node processing state
m_needAnotherNewline = false;
@@ -219,7 +355,7 @@ void TextIterator::advance()
return;
}
- RenderObject *renderer = m_node->renderer();
+ RenderObject* renderer = m_node->renderer();
if (!renderer) {
m_handledNode = true;
m_handledChildren = true;
@@ -241,27 +377,20 @@ void TextIterator::advance()
// find a new current node to handle in depth-first manner,
// calling exitNode() as we come back thru a parent node
- Node *next = m_handledChildren ? 0 : m_node->firstChild();
+ Node* next = m_handledChildren ? 0 : m_node->firstChild();
m_offset = 0;
if (!next) {
next = m_node->nextSibling();
if (!next) {
bool pastEnd = m_node->traverseNextNode() == m_pastEndNode;
- Node* parentNode = m_node->parentNode();
- if (!parentNode && m_inShadowContent) {
- m_inShadowContent = false;
- parentNode = m_node->shadowParentNode();
- }
+ Node* parentNode = parentCrossingShadowBoundaries(m_node);
while (!next && parentNode) {
if ((pastEnd && parentNode == m_endContainer) || m_endContainer->isDescendantOf(parentNode))
return;
bool haveRenderer = m_node->renderer();
m_node = parentNode;
- parentNode = m_node->parentNode();
- if (!parentNode && m_inShadowContent) {
- m_inShadowContent = false;
- parentNode = m_node->shadowParentNode();
- }
+ m_fullyClippedStack.pop();
+ parentNode = parentCrossingShadowBoundaries(m_node);
if (haveRenderer)
exitNode();
if (m_positionNode) {
@@ -272,10 +401,13 @@ void TextIterator::advance()
next = m_node->nextSibling();
}
}
+ m_fullyClippedStack.pop();
}
// set the new current node
m_node = next;
+ if (m_node)
+ pushFullyClippedState(m_fullyClippedStack, m_node);
m_handledNode = false;
m_handledChildren = false;
@@ -285,13 +417,16 @@ void TextIterator::advance()
}
}
-static inline bool compareBoxStart(const InlineTextBox *first, const InlineTextBox *second)
+static inline bool compareBoxStart(const InlineTextBox* first, const InlineTextBox* second)
{
return first->start() < second->start();
}
bool TextIterator::handleTextNode()
{
+ if (m_fullyClippedStack.top())
+ return false;
+
RenderText* renderer = toRenderText(m_node->renderer());
if (renderer->style()->visibility() != VISIBLE)
return false;
@@ -325,7 +460,7 @@ bool TextIterator::handleTextNode()
// Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
if (renderer->containsReversedText()) {
m_sortedTextBoxes.clear();
- for (InlineTextBox * textBox = renderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
+ for (InlineTextBox* textBox = renderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
m_sortedTextBoxes.append(textBox);
}
std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), compareBoxStart);
@@ -339,7 +474,7 @@ bool TextIterator::handleTextNode()
void TextIterator::handleTextBox()
{
- RenderText *renderer = toRenderText(m_node->renderer());
+ RenderText* renderer = toRenderText(m_node->renderer());
String str = renderer->text();
int start = m_offset;
int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX;
@@ -348,7 +483,7 @@ void TextIterator::handleTextBox()
int runStart = max(textBoxStart, start);
// Check for collapsed space at the start of this run.
- InlineTextBox *firstTextBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox();
+ InlineTextBox* firstTextBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox();
bool needSpace = m_lastTextNodeEndedWithCollapsedSpace
|| (m_textBox == firstTextBox && textBoxStart == runStart && runStart > 0);
if (needSpace && !isCollapsibleWhitespace(m_lastCharacter) && m_lastCharacter) {
@@ -365,7 +500,7 @@ void TextIterator::handleTextBox()
int runEnd = min(textBoxEnd, end);
// Determine what the next text box will be, but don't advance yet
- InlineTextBox *nextTextBox = 0;
+ InlineTextBox* nextTextBox = 0;
if (renderer->containsReversedText()) {
if (m_sortedTextBoxesPosition + 1 < m_sortedTextBoxes.size())
nextTextBox = m_sortedTextBoxes[m_sortedTextBoxesPosition + 1];
@@ -411,6 +546,9 @@ void TextIterator::handleTextBox()
bool TextIterator::handleReplacedElement()
{
+ if (m_fullyClippedStack.top())
+ return false;
+
RenderObject* renderer = m_node->renderer();
if (renderer->style()->visibility() != VISIBLE)
return false;
@@ -421,10 +559,12 @@ bool TextIterator::handleReplacedElement()
}
if (m_enterTextControls && renderer->isTextControl()) {
- m_node = toRenderTextControl(renderer)->innerTextElement();
- m_offset = 0;
- m_inShadowContent = true;
- return false;
+ if (HTMLElement* innerTextElement = toRenderTextControl(renderer)->innerTextElement()) {
+ m_node = innerTextElement->shadowTreeRootNode();
+ pushFullyClippedState(m_fullyClippedStack, m_node);
+ m_offset = 0;
+ return false;
+ }
}
m_haveEmitted = true;
@@ -459,7 +599,7 @@ static bool shouldEmitTabBeforeNode(Node* node)
return false;
// Want a tab before every cell other than the first one
- RenderTableCell* rc = static_cast<RenderTableCell*>(r);
+ RenderTableCell* rc = toRenderTableCell(r);
RenderTable* t = rc->table();
return t && (t->cellBefore(rc) || t->cellAbove(rc));
}
@@ -509,7 +649,7 @@ static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node)
// Need to make an exception for table row elements, because they are neither
// "inline" or "RenderBlock", but we want newlines for them.
if (r->isTableRow()) {
- RenderTable* t = static_cast<RenderTableRow*>(r)->table();
+ RenderTable* t = toRenderTableRow(r)->table();
if (t && !t->isInline())
return true;
}
@@ -698,7 +838,7 @@ void TextIterator::exitNode()
emitCharacter(' ', baseNode->parentNode(), baseNode, 1, 1);
}
-void TextIterator::emitCharacter(UChar c, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset)
+void TextIterator::emitCharacter(UChar c, Node* textNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset)
{
m_haveEmitted = true;
@@ -774,14 +914,14 @@ Node* TextIterator::node() const
// --------
-SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator() : m_positionNode(0)
+SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator()
+ : m_positionNode(0)
{
}
-SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range *r)
+SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r)
+ : m_positionNode(0)
{
- m_positionNode = 0;
-
if (!r)
return;
@@ -806,6 +946,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range *r)
}
m_node = endNode;
+ setUpFullyClippedStack(m_fullyClippedStack, m_node);
m_offset = endOffset;
m_handledNode = false;
m_handledChildren = endOffset == 0;
@@ -822,15 +963,8 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range *r)
m_lastTextNode = 0;
m_lastCharacter = '\n';
-
- if (startOffset == 0 || !startNode->firstChild()) {
- m_pastStartNode = startNode->previousSibling();
- while (!m_pastStartNode && startNode->parentNode()) {
- startNode = startNode->parentNode();
- m_pastStartNode = startNode->previousSibling();
- }
- } else
- m_pastStartNode = startNode->childNode(startOffset - 1);
+
+ m_pastStartNode = previousInPostOrderCrossingShadowBoundaries(startNode, startOffset);
advance();
}
@@ -845,7 +979,7 @@ void SimplifiedBackwardsTextIterator::advance()
while (m_node && m_node != m_pastStartNode) {
// Don't handle node if we start iterating at [node, 0].
if (!m_handledNode && !(m_node == m_endNode && m_endOffset == 0)) {
- RenderObject *renderer = m_node->renderer();
+ RenderObject* renderer = m_node->renderer();
if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
// FIXME: What about CDATA_SECTION_NODE?
if (renderer->style()->visibility() == VISIBLE && m_offset > 0)
@@ -877,9 +1011,11 @@ void SimplifiedBackwardsTextIterator::advance()
// Exit all other containers.
next = m_node->previousSibling();
while (!next) {
- if (!m_node->parentNode())
+ Node* parentNode = parentCrossingShadowBoundaries(m_node);
+ if (!parentNode)
break;
- m_node = m_node->parentNode();
+ m_node = parentNode;
+ m_fullyClippedStack.pop();
exitNode();
if (m_positionNode) {
m_handledNode = true;
@@ -888,9 +1024,12 @@ void SimplifiedBackwardsTextIterator::advance()
}
next = m_node->previousSibling();
}
+ m_fullyClippedStack.pop();
}
m_node = next;
+ if (m_node)
+ pushFullyClippedState(m_fullyClippedStack, m_node);
m_offset = m_node ? caretMaxOffset(m_node) : 0;
m_handledNode = false;
m_handledChildren = false;
@@ -904,7 +1043,7 @@ bool SimplifiedBackwardsTextIterator::handleTextNode()
{
m_lastTextNode = m_node;
- RenderText *renderer = toRenderText(m_node->renderer());
+ RenderText* renderer = toRenderText(m_node->renderer());
String str = renderer->text();
if (!renderer->firstTextBox() && str.length() > 0)
@@ -938,29 +1077,25 @@ bool SimplifiedBackwardsTextIterator::handleNonTextNode()
{
// We can use a linefeed in place of a tab because this simple iterator is only used to
// find boundaries, not actual content. A linefeed breaks words, sentences, and paragraphs.
- if (shouldEmitNewlineForNode(m_node) ||
- shouldEmitNewlineAfterNode(m_node) ||
- shouldEmitTabBeforeNode(m_node)) {
+ if (shouldEmitNewlineForNode(m_node) || shouldEmitNewlineAfterNode(m_node) || shouldEmitTabBeforeNode(m_node)) {
unsigned index = m_node->nodeIndex();
- // The start of this emitted range is wrong, ensuring correctness would require
- // VisiblePositions and so would be slow. previousBoundary expects this.
+ // The start of this emitted range is wrong. Ensuring correctness would require
+ // VisiblePositions and so would be slow. previousBoundary expects this.
emitCharacter('\n', m_node->parentNode(), index + 1, index + 1);
}
-
return true;
}
void SimplifiedBackwardsTextIterator::exitNode()
{
- if (shouldEmitNewlineForNode(m_node) ||
- shouldEmitNewlineBeforeNode(m_node) ||
- shouldEmitTabBeforeNode(m_node))
- // The start of this emitted range is wrong, ensuring correctness would require
- // VisiblePositions and so would be slow. previousBoundary expects this.
+ if (shouldEmitNewlineForNode(m_node) || shouldEmitNewlineBeforeNode(m_node) || shouldEmitTabBeforeNode(m_node)) {
+ // The start of this emitted range is wrong. Ensuring correctness would require
+ // VisiblePositions and so would be slow. previousBoundary expects this.
emitCharacter('\n', m_node, 0, 0);
+ }
}
-void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node *node, int startOffset, int endOffset)
+void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node* node, int startOffset, int endOffset)
{
m_singleCharacterBuffer = c;
m_positionNode = node;
@@ -988,7 +1123,7 @@ CharacterIterator::CharacterIterator()
{
}
-CharacterIterator::CharacterIterator(const Range *r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls)
+CharacterIterator::CharacterIterator(const Range* r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls)
: m_offset(0)
, m_runOffset(0)
, m_atBreak(true)
@@ -1167,15 +1302,17 @@ void BackwardsCharacterIterator::advance(int count)
// --------
WordAwareIterator::WordAwareIterator()
-: m_previousText(0), m_didLookAhead(false)
+ : m_previousText(0)
+ , m_didLookAhead(false)
{
}
-WordAwareIterator::WordAwareIterator(const Range *r)
-: m_previousText(0), m_didLookAhead(false), m_textIterator(r)
+WordAwareIterator::WordAwareIterator(const Range* r)
+ : m_previousText(0)
+ , m_didLookAhead(true) // so we consider the first chunk from the text iterator
+ , m_textIterator(r)
{
- m_didLookAhead = true; // so we consider the first chunk from the text iterator
- advance(); // get in position over the first chunk of text
+ advance(); // get in position over the first chunk of text
}
// We're always in one of these modes:
@@ -1185,7 +1322,7 @@ WordAwareIterator::WordAwareIterator(const Range *r)
// (we looked ahead to the next chunk and found a word boundary)
// - We built up our own chunk of text from many chunks from the text iterator
-// FIXME: Perf could be bad for huge spans next to each other that don't fall on word boundaries
+// FIXME: Performance could be bad for huge spans next to each other that don't fall on word boundaries.
void WordAwareIterator::advance()
{
@@ -1256,8 +1393,40 @@ const UChar* WordAwareIterator::characters() const
// --------
+static inline UChar foldQuoteMark(UChar c)
+{
+ switch (c) {
+ case hebrewPunctuationGershayim:
+ case leftDoubleQuotationMark:
+ case rightDoubleQuotationMark:
+ return '"';
+ case hebrewPunctuationGeresh:
+ case leftSingleQuotationMark:
+ case rightSingleQuotationMark:
+ return '\'';
+ default:
+ return c;
+ }
+}
+
+static inline void foldQuoteMarks(String& s)
+{
+ s.replace(hebrewPunctuationGeresh, '\'');
+ s.replace(hebrewPunctuationGershayim, '"');
+ s.replace(leftDoubleQuotationMark, '"');
+ s.replace(leftSingleQuotationMark, '\'');
+ s.replace(rightDoubleQuotationMark, '"');
+ s.replace(rightSingleQuotationMark, '\'');
+}
+
#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION
+static inline void foldQuoteMarks(UChar* data, size_t length)
+{
+ for (size_t i = 0; i < length; ++i)
+ data[i] = foldQuoteMark(data[i]);
+}
+
static const size_t minimumSearchBufferSize = 8192;
#ifndef NDEBUG
@@ -1269,13 +1438,9 @@ static UStringSearch* createSearcher()
// Provide a non-empty pattern and non-empty text so usearch_open will not fail,
// but it doesn't matter exactly what it is, since we don't perform any searches
// without setting both the pattern and the text.
-
- // Pass empty string for the locale for now to get the Unicode Collation Algorithm,
- // rather than something locale-specific.
-
UErrorCode status = U_ZERO_ERROR;
- UStringSearch* searcher = usearch_open(&newlineCharacter, 1, &newlineCharacter, 1, "", 0, &status);
- ASSERT(status == U_ZERO_ERROR);
+ UStringSearch* searcher = usearch_open(&newlineCharacter, 1, &newlineCharacter, 1, currentSearchLocaleID(), 0, &status);
+ ASSERT(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING || status == U_USING_DEFAULT_WARNING);
return searcher;
}
@@ -1307,7 +1472,12 @@ inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
{
ASSERT(!m_target.isEmpty());
- size_t targetLength = target.length();
+ // FIXME: We'd like to tailor the searcher to fold quote marks for us instead
+ // of doing it in a separate replacement pass here, but ICU doesn't offer a way
+ // to add tailoring on top of the locale-specific tailoring as of this writing.
+ foldQuoteMarks(m_target);
+
+ size_t targetLength = m_target.length();
m_buffer.reserveInitialCapacity(max(targetLength * 8, minimumSearchBufferSize));
m_overlap = m_buffer.capacity() / 4;
@@ -1347,9 +1517,11 @@ inline size_t SearchBuffer::append(const UChar* characters, size_t length)
m_buffer.shrink(m_overlap);
}
- size_t usableLength = min(m_buffer.capacity() - m_buffer.size(), length);
+ size_t oldLength = m_buffer.size();
+ size_t usableLength = min(m_buffer.capacity() - oldLength, length);
ASSERT(usableLength);
m_buffer.append(characters, usableLength);
+ foldQuoteMarks(m_buffer.data() + oldLength, usableLength);
return usableLength;
}
@@ -1416,6 +1588,7 @@ inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
{
ASSERT(!m_target.isEmpty());
m_target.replace(noBreakSpace, ' ');
+ foldQuoteMarks(m_target);
}
inline SearchBuffer::~SearchBuffer()
@@ -1435,7 +1608,7 @@ inline bool SearchBuffer::atBreak() const
inline void SearchBuffer::append(UChar c, bool isStart)
{
- m_buffer[m_cursor] = c == noBreakSpace ? ' ' : c;
+ m_buffer[m_cursor] = c == noBreakSpace ? ' ' : foldQuoteMark(c);
m_isCharacterStartBuffer[m_cursor] = isStart;
if (++m_cursor == m_target.length()) {
m_cursor = 0;
@@ -1507,7 +1680,7 @@ size_t SearchBuffer::length() const
// --------
-int TextIterator::rangeLength(const Range *r, bool forSelectionPreservation)
+int TextIterator::rangeLength(const Range* r, bool forSelectionPreservation)
{
int length = 0;
for (TextIterator it(r, forSelectionPreservation); !it.atEnd(); it.advance())
@@ -1522,7 +1695,7 @@ PassRefPtr<Range> TextIterator::subrange(Range* entireRange, int characterOffset
return characterSubrange(entireRangeIterator, characterOffset, characterCount);
}
-PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(Element *scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
+PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(Element* scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
{
RefPtr<Range> resultRange = scope->document()->createRange();
@@ -1742,11 +1915,6 @@ tryAgain:
PassRefPtr<Range> findPlainText(const Range* range, const String& target, bool forward, bool caseSensitive)
{
- // We can't search effectively for a string that's entirely made of collapsible
- // whitespace, so we won't even try. This also takes care of the empty string case.
- if (isAllCollapsibleWhitespace(target))
- return collapsedToBoundary(range, forward);
-
// First, find the text.
size_t matchStart;
size_t matchLength;