📄 textiterator.cpp
字号:
return false; } m_haveEmitted = true; if (m_emitCharactersBetweenAllVisiblePositions) { // 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. emitCharacter(',', m_node->parentNode(), m_node, 0, 1); return true; } m_positionNode = m_node->parentNode(); m_positionOffsetBaseNode = m_node; m_positionStartOffset = 0; m_positionEndOffset = 1; m_textCharacters = 0; m_textLength = 0; m_lastCharacter = 0; return true;}static bool shouldEmitTabBeforeNode(Node* node){ RenderObject* r = node->renderer(); // Table cells are delimited by tabs. if (!r || !isTableCell(node)) return false; // Want a tab before every cell other than the first one RenderTableCell* rc = static_cast<RenderTableCell*>(r); RenderTable* t = rc->table(); return t && (t->cellBefore(rc) || t->cellAbove(rc));}static bool shouldEmitNewlineForNode(Node* node){ // br elements are represented by a single newline. RenderObject* r = node->renderer(); if (!r) return node->hasTagName(brTag); return r->isBR();}static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node){ // Block flow (versus inline flow) is represented by having // a newline both before and after the element. RenderObject* r = node->renderer(); if (!r) { return (node->hasTagName(blockquoteTag) || node->hasTagName(ddTag) || node->hasTagName(divTag) || node->hasTagName(dlTag) || node->hasTagName(dtTag) || node->hasTagName(h1Tag) || node->hasTagName(h2Tag) || node->hasTagName(h3Tag) || node->hasTagName(h4Tag) || node->hasTagName(h5Tag) || node->hasTagName(h6Tag) || node->hasTagName(hrTag) || node->hasTagName(liTag) || node->hasTagName(listingTag) || node->hasTagName(olTag) || node->hasTagName(pTag) || node->hasTagName(preTag) || node->hasTagName(trTag) || node->hasTagName(ulTag)); } // Need to make an exception for table cells, because they are blocks, but we // want them tab-delimited rather than having newlines before and after. if (isTableCell(node)) return false; // 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(); if (t && !t->isInline()) return true; } return !r->isInline() && r->isRenderBlock() && !r->isFloatingOrPositioned() && !r->isBody();}static bool shouldEmitNewlineAfterNode(Node* node){ // FIXME: It should be better but slower to create a VisiblePosition here. if (!shouldEmitNewlinesBeforeAndAfterNode(node)) return false; // Check if this is the very last renderer in the document. // If so, then we should not emit a newline. while ((node = node->traverseNextSibling())) if (node->renderer()) return true; return false;}static bool shouldEmitNewlineBeforeNode(Node* node){ return shouldEmitNewlinesBeforeAndAfterNode(node); }static bool shouldEmitExtraNewlineForNode(Node* node){ // When there is a significant collapsed bottom margin, emit an extra // newline for a more realistic result. We end up getting the right // result even without margin collapsing. For example: <div><p>text</p></div> // will work right even if both the <div> and the <p> have bottom margins. RenderObject* r = node->renderer(); if (!r || !r->isBox()) return false; // NOTE: We only do this for a select set of nodes, and fwiw WinIE appears // not to do this at all if (node->hasTagName(h1Tag) || node->hasTagName(h2Tag) || node->hasTagName(h3Tag) || node->hasTagName(h4Tag) || node->hasTagName(h5Tag) || node->hasTagName(h6Tag) || node->hasTagName(pTag)) { RenderStyle* style = r->style(); if (style) { int bottomMargin = toRenderBox(r)->collapsedMarginBottom(); int fontSize = style->fontDescription().computedPixelSize(); if (bottomMargin * 2 >= fontSize) return true; } } return false;}// Whether or not we should emit a character as we enter m_node (if it's a container) or as we hit it (if it's atomic).bool TextIterator::shouldRepresentNodeOffsetZero(){ if (m_emitCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isTable()) return true; // Leave element positioned flush with start of a paragraph // (e.g. do not insert tab before a table cell at the start of a paragraph) if (m_lastCharacter == '\n') return false; // Otherwise, show the position if we have emitted any characters if (m_haveEmitted) return true; // We've not emitted anything yet. Generally, there is no need for any positioning then. // The only exception is when the element is visually not in the same line as // the start of the range (e.g. the range starts at the end of the previous paragraph). // NOTE: Creating VisiblePositions and comparing them is relatively expensive, so we // make quicker checks to possibly avoid that. Another check that we could make is // is whether the inline vs block flow changed since the previous visible element. // I think we're already in a special enough case that that won't be needed, tho. // No character needed if this is the first node in the range. if (m_node == m_startContainer) return false; // If we are outside the start container's subtree, assume we need to emit. // FIXME: m_startContainer could be an inline block if (!m_node->isDescendantOf(m_startContainer)) return true; // If we started as m_startContainer offset 0 and the current node is a descendant of // the start container, we already had enough context to correctly decide whether to // emit after a preceding block. We chose not to emit (m_haveEmitted is false), // so don't second guess that now. // NOTE: Is this really correct when m_node is not a leftmost descendant? Probably // immaterial since we likely would have already emitted something by now. if (m_startOffset == 0) return false; // If this node is unrendered or invisible the VisiblePosition checks below won't have much meaning. // Additionally, if the range we are iterating over contains huge sections of unrendered content, // we would create VisiblePositions on every call to this function without this check. if (!m_node->renderer() || m_node->renderer()->style()->visibility() != VISIBLE) return false; // The currPos.isNotNull() check is needed because positions in non-html content // (like svg) do not have visible positions, and we don't want to emit for them either. VisiblePosition startPos = VisiblePosition(m_startContainer, m_startOffset, DOWNSTREAM); VisiblePosition currPos = VisiblePosition(m_node, 0, DOWNSTREAM); return currPos.isNotNull() && !inSameLine(startPos, currPos);}bool TextIterator::shouldEmitSpaceBeforeAndAfterNode(Node* node){ return node->renderer() && node->renderer()->isTable() && (node->renderer()->isInline() || m_emitCharactersBetweenAllVisiblePositions);}void TextIterator::representNodeOffsetZero(){ // Emit a character to show the positioning of m_node. // When we haven't been emitting any characters, shouldRepresentNodeOffsetZero() can // create VisiblePositions, which is expensive. So, we perform the inexpensive checks // on m_node to see if it necessitates emitting a character first and will early return // before encountering shouldRepresentNodeOffsetZero()s worse case behavior. if (shouldEmitTabBeforeNode(m_node)) { if (shouldRepresentNodeOffsetZero()) emitCharacter('\t', m_node->parentNode(), m_node, 0, 0); } else if (shouldEmitNewlineBeforeNode(m_node)) { if (shouldRepresentNodeOffsetZero()) emitCharacter('\n', m_node->parentNode(), m_node, 0, 0); } else if (shouldEmitSpaceBeforeAndAfterNode(m_node)) { if (shouldRepresentNodeOffsetZero()) emitCharacter(' ', m_node->parentNode(), m_node, 0, 0); }}bool TextIterator::handleNonTextNode(){ if (shouldEmitNewlineForNode(m_node)) emitCharacter('\n', m_node->parentNode(), m_node, 0, 1); else if (m_emitCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isHR()) emitCharacter(' ', m_node->parentNode(), m_node, 0, 1); else representNodeOffsetZero(); return true;}void TextIterator::exitNode(){ // prevent emitting a newline when exiting a collapsed block at beginning of the range // FIXME: !m_haveEmitted does not necessarily mean there was a collapsed block... it could // have been an hr (e.g.). Also, a collapsed block could have height (e.g. a table) and // therefore look like a blank line. if (!m_haveEmitted) return; // Emit with a position *inside* m_node, after m_node's contents, in // case it is a block, because the run should start where the // emitted character is positioned visually. Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node; // FIXME: This shouldn't require the m_lastTextNode to be true, but we can't change that without making // the logic in _web_attributedStringFromRange match. We'll get that for free when we switch to use // TextIterator in _web_attributedStringFromRange. // See <rdar://problem/5428427> for an example of how this mismatch will cause problems. if (m_lastTextNode && shouldEmitNewlineAfterNode(m_node)) { // use extra newline to represent margin bottom, as needed bool addNewline = shouldEmitExtraNewlineForNode(m_node); // FIXME: We need to emit a '\n' as we leave an empty block(s) that // contain a VisiblePosition when doing selection preservation. if (m_lastCharacter != '\n') { // insert a newline with a position following this block's contents. emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); // remember whether to later add a newline for the current node ASSERT(!m_needAnotherNewline); m_needAnotherNewline = addNewline; } else if (addNewline) // insert a newline with a position following this block's contents. emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); } // If nothing was emitted, see if we need to emit a space. if (!m_positionNode && shouldEmitSpaceBeforeAndAfterNode(m_node)) emitCharacter(' ', baseNode->parentNode(), baseNode, 1, 1);}void TextIterator::emitCharacter(UChar c, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset){ m_haveEmitted = true; // remember information with which to construct the TextIterator::range() // NOTE: textNode is often not a text node, so the range will specify child nodes of positionNode m_positionNode = textNode; m_positionOffsetBaseNode = offsetBaseNode; m_positionStartOffset = textStartOffset; m_positionEndOffset = textEndOffset; // remember information with which to construct the TextIterator::characters() and length() m_singleCharacterBuffer = c; m_textCharacters = &m_singleCharacterBuffer; m_textLength = 1; // remember some iteration state m_lastTextNodeEndedWithCollapsedSpace = false; m_lastCharacter = c;}void TextIterator::emitText(Node* textNode, int textStartOffset, int textEndOffset){ RenderText* renderer = toRenderText(m_node->renderer()); String str = renderer->text(); ASSERT(str.characters()); m_positionNode = textNode; m_positionOffsetBaseNode = 0; m_positionStartOffset = textStartOffset; m_positionEndOffset = textEndOffset; m_textCharacters = str.characters() + textStartOffset; m_textLength = textEndOffset - textStartOffset; m_lastCharacter = str[textEndOffset - 1]; m_lastTextNodeEndedWithCollapsedSpace = false; m_haveEmitted = true;}PassRefPtr<Range> TextIterator::range() const{ // use the current run information, if we have it if (m_positionNode) { if (m_positionOffsetBaseNode) { int index = m_positionOffsetBaseNode->nodeIndex(); m_positionStartOffset += index; m_positionEndOffset += index; m_positionOffsetBaseNode = 0; } return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset); } // otherwise, return the end of the overall range we were given if (m_endContainer) return Range::create(m_endContainer->document(), m_endContainer, m_endOffset, m_endContainer, m_endOffset); return 0;} Node* TextIterator::node() const{ RefPtr<Range> textRange = range(); if (!textRange) return 0; Node* node = textRange->startContainer(); if (!node) return 0; if (node->offsetInCharacters()) return node; return node->childNode(textRange->startOffset());}// --------SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator() : m_positionNode(0){}SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range *r){ m_positionNode = 0; if (!r) return; Node* startNode = r->startContainer(); if (!startNode) return; Node* endNode = r->endContainer(); int startOffset = r->startOffset(); int endOffset = r->endOffset(); if (!startNode->offsetInCharacters()) { if (startOffset >= 0 && startOffset < static_cast<int>(startNode->childNodeCount())) { startNode = startNode->childNode(startOffset); startOffset = 0; } } if (!endNode->offsetInCharacters()) { if (endOffset > 0 && endOffset <= static_cast<int>(endNode->childNodeCount())) { endNode = endNode->childNode(endOffset - 1); endOffset = endNode->offsetInCharacters() ? endNode->maxCharacterOffset() : endNode->childNodeCount(); } } m_node = endNode; m_offset = endOffset; m_handledNode = false; m_handledChildren = endOffset == 0; m_startNode = startNode; m_startOffset = startOffset; m_endNode = endNode; m_endOffset = endOffset; #ifndef NDEBUG // Need this just because of the assert. m_positionNode = endNode;#endif 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); advance();}void SimplifiedBackwardsTextIterator::advance(){ ASSERT(m_positionNode); m_positionNode = 0; m_textLength = 0; 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(); if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) { // FIXME: What about CDATA_SECTION_NODE?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -