📄 textiterator.cpp
字号:
/* * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2005 Alexey Proskuryakov. * * 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. */#include "config.h"#include "TextIterator.h"#include "CharacterNames.h"#include "Document.h"#include "Element.h"#include "HTMLNames.h"#include "htmlediting.h"#include "InlineTextBox.h"#include "Position.h"#include "Range.h"#include "RenderTableCell.h"#include "RenderTableRow.h"#include "RenderTextControl.h"#include "VisiblePosition.h"#include "visible_units.h"#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION#include <unicode/usearch.h>#endifusing namespace WTF::Unicode;using namespace std;namespace WebCore {using namespace HTMLNames;// Buffer that knows how to compare with a search target.// 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 {public: SearchBuffer(const String& target, bool isCaseSensitive); ~SearchBuffer(); // Returns number of characters appended; guaranteed to be in the range [1, length]. size_t append(const UChar*, size_t length); void reachedBreak(); // Result is the size in characters of what was found. // And <startOffset> is the number of characters back to the start of what was found. size_t search(size_t& startOffset); bool atBreak() const;#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATIONprivate: String m_target; Vector<UChar> m_buffer; size_t m_overlap; bool m_atBreak;#elseprivate: void append(UChar, bool isCharacterStart); size_t length() const; String m_target; bool m_isCaseSensitive; Vector<UChar> m_buffer; Vector<bool> m_isCharacterStartBuffer; bool m_isBufferFull; size_t m_cursor;#endif};// --------TextIterator::TextIterator() : m_startContainer(0) , m_startOffset(0) , m_endContainer(0) , m_endOffset(0) , m_positionNode(0) , m_lastCharacter(0) , m_emitCharactersBetweenAllVisiblePositions(false) , m_enterTextControls(false){}TextIterator::TextIterator(const Range* r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls) : m_inShadowContent(false) , m_startContainer(0) , m_startOffset(0) , m_endContainer(0) , m_endOffset(0) , m_positionNode(0) , m_emitCharactersBetweenAllVisiblePositions(emitCharactersBetweenAllVisiblePositions) , m_enterTextControls(enterTextControls){ if (!r) return; // get and validate the range endpoints Node* startContainer = r->startContainer(); if (!startContainer) return; int startOffset = r->startOffset(); Node* endContainer = r->endContainer(); int endOffset = r->endOffset(); // Callers should be handing us well-formed ranges. If we discover that this isn't // the case, we could consider changing this assertion to an early return. ASSERT(r->boundaryPointsValid()); // remember range - this does not change m_startContainer = startContainer; m_startOffset = startOffset; 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) return; 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(); // initialize node processing state m_needAnotherNewline = false; m_textBox = 0; // initialize record of previous node processing m_haveEmitted = false; m_lastTextNode = 0; m_lastTextNodeEndedWithCollapsedSpace = false; m_lastCharacter = 0;#ifndef NDEBUG // need this just because of the assert in advance() m_positionNode = m_node;#endif // identify the first run advance();}void TextIterator::advance(){ // reset the run information m_positionNode = 0; m_textLength = 0; // handle remembered node that needed a newline after the text node's newline if (m_needAnotherNewline) { // Emit the extra newline, and position it *inside* m_node, after m_node's // contents, in case it's a block, in the same way that we position the first // newline. The range for the emitted newline should start where the line // break begins. // FIXME: It would be cleaner if we emitted two newlines during the last // iteration, instead of using m_needAnotherNewline. Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node; emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); m_needAnotherNewline = false; return; } // handle remembered text box if (m_textBox) { handleTextBox(); if (m_positionNode) return; } while (m_node && m_node != m_pastEndNode) { // if the range ends at offset 0 of an element, represent the // position, but not the content, of that element e.g. if the // node is a blockflow element, emit a newline that // precedes the element if (m_node == m_endContainer && m_endOffset == 0) { representNodeOffsetZero(); m_node = 0; return; } RenderObject *renderer = m_node->renderer(); if (!renderer) { m_handledNode = true; m_handledChildren = true; } else { // handle current node according to its type if (!m_handledNode) { if (renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) // FIXME: What about CDATA_SECTION_NODE? m_handledNode = handleTextNode(); else if (renderer && (renderer->isImage() || renderer->isWidget() || (renderer->node() && renderer->node()->isElementNode() && static_cast<Element*>(renderer->node())->isFormControlElement()))) m_handledNode = handleReplacedElement(); else m_handledNode = handleNonTextNode(); if (m_positionNode) return; } } // 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(); 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(); } 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(); } if (haveRenderer) exitNode(); if (m_positionNode) { m_handledNode = true; m_handledChildren = true; return; } next = m_node->nextSibling(); } } } // set the new current node m_node = next; m_handledNode = false; m_handledChildren = false; // how would this ever be? if (m_positionNode) return; }}static inline bool compareBoxStart(const InlineTextBox *first, const InlineTextBox *second){ return first->start() < second->start();}bool TextIterator::handleTextNode(){ RenderText* renderer = toRenderText(m_node->renderer()); if (renderer->style()->visibility() != VISIBLE) return false; m_lastTextNode = m_node; String str = renderer->text(); // handle pre-formatted text if (!renderer->style()->collapseWhiteSpace()) { int runStart = m_offset; if (m_lastTextNodeEndedWithCollapsedSpace) { emitCharacter(' ', m_node, 0, runStart, runStart); return false; } int strLength = str.length(); int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX; int runEnd = min(strLength, end); if (runStart >= runEnd) return true; emitText(m_node, runStart, runEnd); return true; } if (!renderer->firstTextBox() && str.length() > 0) { m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space return true; } // 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()) { m_sortedTextBoxes.append(textBox); } std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), compareBoxStart); m_sortedTextBoxesPosition = 0; } m_textBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox(); handleTextBox(); return true;}void TextIterator::handleTextBox(){ RenderText *renderer = toRenderText(m_node->renderer()); String str = renderer->text(); int start = m_offset; int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX; while (m_textBox) { int textBoxStart = m_textBox->start(); int runStart = max(textBoxStart, start); // Check for collapsed space at the start of this run. 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) { if (m_lastTextNode == m_node && runStart > 0 && str[runStart - 1] == ' ') { unsigned spaceRunStart = runStart - 1; while (spaceRunStart > 0 && str[spaceRunStart - 1] == ' ') --spaceRunStart; emitText(m_node, spaceRunStart, spaceRunStart + 1); } else emitCharacter(' ', m_node, 0, runStart, runStart); return; } int textBoxEnd = textBoxStart + m_textBox->len(); int runEnd = min(textBoxEnd, end); // Determine what the next text box will be, but don't advance yet InlineTextBox *nextTextBox = 0; if (renderer->containsReversedText()) { if (m_sortedTextBoxesPosition + 1 < m_sortedTextBoxes.size()) nextTextBox = m_sortedTextBoxes[m_sortedTextBoxesPosition + 1]; } else nextTextBox = m_textBox->nextTextBox(); if (runStart < runEnd) { // 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[runStart] == '\n') { emitCharacter(' ', m_node, 0, runStart, runStart + 1); m_offset = runStart + 1; } else { int subrunEnd = str.find('\n', runStart); if (subrunEnd == -1 || subrunEnd > runEnd) subrunEnd = runEnd; m_offset = subrunEnd; emitText(m_node, runStart, subrunEnd); } // 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 (m_positionEndOffset < textBoxEnd) return; // Advance and return int nextRunStart = nextTextBox ? nextTextBox->start() : str.length(); if (nextRunStart > runEnd) m_lastTextNodeEndedWithCollapsedSpace = true; // collapsed space between runs or at the end m_textBox = nextTextBox; if (renderer->containsReversedText()) ++m_sortedTextBoxesPosition; return; } // Advance and continue m_textBox = nextTextBox; if (renderer->containsReversedText()) ++m_sortedTextBoxesPosition; }}bool TextIterator::handleReplacedElement(){ RenderObject* renderer = m_node->renderer(); if (renderer->style()->visibility() != VISIBLE) return false; if (m_lastTextNodeEndedWithCollapsedSpace) { emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1); return false; } if (m_enterTextControls && renderer->isTextControl()) { m_node = toRenderTextControl(renderer)->innerTextElement(); m_offset = 0; m_inShadowContent = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -