📄 textiterator.cpp
字号:
if (renderer->style()->visibility() == VISIBLE && m_offset > 0) m_handledNode = handleTextNode(); } else if (renderer && (renderer->isImage() || renderer->isWidget())) { if (renderer->style()->visibility() == VISIBLE && m_offset > 0) m_handledNode = handleReplacedElement(); } else m_handledNode = handleNonTextNode(); if (m_positionNode) return; } Node* next = m_handledChildren ? 0 : m_node->lastChild(); if (!next) { // Exit empty containers as we pass over them or containers // where [container, 0] is where we started iterating. if (!m_handledNode && canHaveChildrenForEditing(m_node) && m_node->parentNode() && (!m_node->lastChild() || m_node == m_endNode && m_endOffset == 0)) { exitNode(); if (m_positionNode) { m_handledNode = true; m_handledChildren = true; return; } } // Exit all other containers. next = m_node->previousSibling(); while (!next) { if (!m_node->parentNode()) break; m_node = m_node->parentNode(); exitNode(); if (m_positionNode) { m_handledNode = true; m_handledChildren = true; return; } next = m_node->previousSibling(); } } m_node = next; m_offset = m_node ? caretMaxOffset(m_node) : 0; m_handledNode = false; m_handledChildren = false; if (m_positionNode) return; }}bool SimplifiedBackwardsTextIterator::handleTextNode(){ m_lastTextNode = m_node; RenderText *renderer = toRenderText(m_node->renderer()); String str = renderer->text(); if (!renderer->firstTextBox() && str.length() > 0) return true; m_positionEndOffset = m_offset; m_offset = (m_node == m_startNode) ? m_startOffset : 0; m_positionNode = m_node; m_positionStartOffset = m_offset; m_textLength = m_positionEndOffset - m_positionStartOffset; m_textCharacters = str.characters() + m_positionStartOffset; m_lastCharacter = str[m_positionEndOffset - 1]; return true;}bool SimplifiedBackwardsTextIterator::handleReplacedElement(){ unsigned index = m_node->nodeIndex(); // 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(',', m_node->parentNode(), index, index + 1); return true;}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)) { 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. 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. emitCharacter('\n', m_node, 0, 0);}void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node *node, int startOffset, int endOffset){ m_singleCharacterBuffer = c; m_positionNode = node; m_positionStartOffset = startOffset; m_positionEndOffset = endOffset; m_textCharacters = &m_singleCharacterBuffer; m_textLength = 1; m_lastCharacter = c;}PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const{ if (m_positionNode) return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset); return Range::create(m_startNode->document(), m_startNode, m_startOffset, m_startNode, m_startOffset);}// --------CharacterIterator::CharacterIterator() : m_offset(0) , m_runOffset(0) , m_atBreak(true){}CharacterIterator::CharacterIterator(const Range *r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls) : m_offset(0) , m_runOffset(0) , m_atBreak(true) , m_textIterator(r, emitCharactersBetweenAllVisiblePositions, enterTextControls){ while (!atEnd() && m_textIterator.length() == 0) m_textIterator.advance();}PassRefPtr<Range> CharacterIterator::range() const{ RefPtr<Range> r = m_textIterator.range(); if (!m_textIterator.atEnd()) { if (m_textIterator.length() <= 1) { ASSERT(m_runOffset == 0); } else { Node* n = r->startContainer(); ASSERT(n == r->endContainer()); int offset = r->startOffset() + m_runOffset; ExceptionCode ec = 0; r->setStart(n, offset, ec); r->setEnd(n, offset + 1, ec); ASSERT(!ec); } } return r.release();}void CharacterIterator::advance(int count){ if (count <= 0) { ASSERT(count == 0); return; } m_atBreak = false; // easy if there is enough left in the current m_textIterator run int remaining = m_textIterator.length() - m_runOffset; if (count < remaining) { m_runOffset += count; m_offset += count; return; } // exhaust the current m_textIterator run count -= remaining; m_offset += remaining; // move to a subsequent m_textIterator run for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) { int runLength = m_textIterator.length(); if (runLength == 0) m_atBreak = true; else { // see whether this is m_textIterator to use if (count < runLength) { m_runOffset = count; m_offset += count; return; } // exhaust this m_textIterator run count -= runLength; m_offset += runLength; } } // ran to the end of the m_textIterator... no more runs left m_atBreak = true; m_runOffset = 0;}String CharacterIterator::string(int numChars){ Vector<UChar> result; result.reserveInitialCapacity(numChars); while (numChars > 0 && !atEnd()) { int runSize = min(numChars, length()); result.append(characters(), runSize); numChars -= runSize; advance(runSize); } return String::adopt(result);}static PassRefPtr<Range> characterSubrange(CharacterIterator& it, int offset, int length){ it.advance(offset); RefPtr<Range> start = it.range(); if (length > 1) it.advance(length - 1); RefPtr<Range> end = it.range(); return Range::create(start->startContainer()->document(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());}// --------WordAwareIterator::WordAwareIterator(): m_previousText(0), m_didLookAhead(false){}WordAwareIterator::WordAwareIterator(const Range *r): m_previousText(0), m_didLookAhead(false), 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}// We're always in one of these modes:// - The current chunk in the text iterator is our current chunk// (typically its a piece of whitespace, or text that ended with whitespace)// - The previous chunk in the text iterator is our current chunk// (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 boundariesvoid WordAwareIterator::advance(){ m_previousText = 0; m_buffer.clear(); // toss any old buffer we built up // If last time we did a look-ahead, start with that looked-ahead chunk now if (!m_didLookAhead) { ASSERT(!m_textIterator.atEnd()); m_textIterator.advance(); } m_didLookAhead = false; // Go to next non-empty chunk while (!m_textIterator.atEnd() && m_textIterator.length() == 0) m_textIterator.advance(); m_range = m_textIterator.range(); if (m_textIterator.atEnd()) return; while (1) { // If this chunk ends in whitespace we can just use it as our chunk. if (isSpaceOrNewline(m_textIterator.characters()[m_textIterator.length() - 1])) return; // If this is the first chunk that failed, save it in previousText before look ahead if (m_buffer.isEmpty()) { m_previousText = m_textIterator.characters(); m_previousLength = m_textIterator.length(); } // Look ahead to next chunk. If it is whitespace or a break, we can use the previous stuff m_textIterator.advance(); if (m_textIterator.atEnd() || m_textIterator.length() == 0 || isSpaceOrNewline(m_textIterator.characters()[0])) { m_didLookAhead = true; return; } if (m_buffer.isEmpty()) { // Start gobbling chunks until we get to a suitable stopping point m_buffer.append(m_previousText, m_previousLength); m_previousText = 0; } m_buffer.append(m_textIterator.characters(), m_textIterator.length()); int exception = 0; m_range->setEnd(m_textIterator.range()->endContainer(), m_textIterator.range()->endOffset(), exception); }}int WordAwareIterator::length() const{ if (!m_buffer.isEmpty()) return m_buffer.size(); if (m_previousText) return m_previousLength; return m_textIterator.length();}const UChar* WordAwareIterator::characters() const{ if (!m_buffer.isEmpty()) return m_buffer.data(); if (m_previousText) return m_previousText; return m_textIterator.characters();}// --------#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATIONstatic const size_t minimumSearchBufferSize = 8192;#ifndef NDEBUGstatic bool searcherInUse;#endifstatic 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); return searcher;}static UStringSearch* searcher(){ static UStringSearch* searcher = createSearcher(); return searcher;}static inline void lockSearcher(){#ifndef NDEBUG ASSERT(!searcherInUse); searcherInUse = true;#endif}static inline void unlockSearcher(){#ifndef NDEBUG ASSERT(searcherInUse); searcherInUse = false;#endif}inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive) : m_target(target) , m_atBreak(true){ ASSERT(!m_target.isEmpty()); size_t targetLength = target.length(); m_buffer.reserveInitialCapacity(max(targetLength * 8, minimumSearchBufferSize)); m_overlap = m_buffer.capacity() / 4; // Grab the single global searcher. // If we ever have a reason to do more than once search buffer at once, we'll have // to move to multiple searchers. lockSearcher(); UStringSearch* searcher = WebCore::searcher(); UCollator* collator = usearch_getCollator(searcher); UCollationStrength strength = isCaseSensitive ? UCOL_TERTIARY : UCOL_PRIMARY; if (ucol_getStrength(collator) != strength) { ucol_setStrength(collator, strength); usearch_reset(searcher); } UErrorCode status = U_ZERO_ERROR; usearch_setPattern(searcher, m_target.characters(), targetLength, &status); ASSERT(status == U_ZERO_ERROR);}inline SearchBuffer::~SearchBuffer(){ unlockSearcher();}inline size_t SearchBuffer::append(const UChar* characters, size_t length){ ASSERT(length); if (m_atBreak) { m_buffer.shrink(0); m_atBreak = false; } else if (m_buffer.size() == m_buffer.capacity()) { memcpy(m_buffer.data(), m_buffer.data() + m_buffer.size() - m_overlap, m_overlap * sizeof(UChar)); m_buffer.shrink(m_overlap);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -