📄 replaceselectioncommand.cpp
字号:
if (!isNodeRendered(node) && !isTableStructureNode(node)) unrendered.append(node); size_t n = unrendered.size(); for (size_t i = 0; i < n; ++i) removeNode(unrendered[i]);}void ReplacementFragment::removeInterchangeNodes(Node* container){ // Interchange newlines at the "start" of the incoming fragment must be // either the first node in the fragment or the first leaf in the fragment. Node* node = container->firstChild(); while (node) { if (isInterchangeNewlineNode(node)) { m_hasInterchangeNewlineAtStart = true; removeNode(node); break; } node = node->firstChild(); } if (!container->hasChildNodes()) return; // Interchange newlines at the "end" of the incoming fragment must be // either the last node in the fragment or the last leaf in the fragment. node = container->lastChild(); while (node) { if (isInterchangeNewlineNode(node)) { m_hasInterchangeNewlineAtEnd = true; removeNode(node); break; } node = node->lastChild(); } node = container->firstChild(); while (node) { Node *next = node->traverseNextNode(); if (isInterchangeConvertedSpaceSpan(node)) { RefPtr<Node> n = 0; while ((n = node->firstChild())) { removeNode(n); insertNodeBefore(n, node); } removeNode(node); if (n) next = n->traverseNextNode(); } node = next; }}ReplaceSelectionCommand::ReplaceSelectionCommand(Document* document, PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph, EditAction editAction) : CompositeEditCommand(document), m_selectReplacement(selectReplacement), m_smartReplace(smartReplace), m_matchStyle(matchStyle), m_documentFragment(fragment), m_preventNesting(preventNesting), m_movingParagraph(movingParagraph), m_editAction(editAction), m_shouldMergeEnd(false){}static bool hasMatchingQuoteLevel(VisiblePosition endOfExistingContent, VisiblePosition endOfInsertedContent){ Position existing = endOfExistingContent.deepEquivalent(); Position inserted = endOfInsertedContent.deepEquivalent(); bool isInsideMailBlockquote = nearestMailBlockquote(inserted.node()); return isInsideMailBlockquote && (numEnclosingMailBlockquotes(existing) == numEnclosingMailBlockquotes(inserted));}bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfParagraph, bool fragmentHasInterchangeNewlineAtStart){ VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); VisiblePosition prev = startOfInsertedContent.previous(true); if (prev.isNull()) return false; if (!m_movingParagraph && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) return true; return !selectionStartWasStartOfParagraph && !fragmentHasInterchangeNewlineAtStart && isStartOfParagraph(startOfInsertedContent) && !startOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && shouldMerge(startOfInsertedContent, prev);}bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph){ VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); VisiblePosition next = endOfInsertedContent.next(true); if (next.isNull()) return false; return !selectionEndWasEndOfParagraph && isEndOfParagraph(endOfInsertedContent) && !endOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && shouldMerge(endOfInsertedContent, next);}static bool isMailPasteAsQuotationNode(const Node* node){ return node && node->hasTagName(blockquoteTag) && node->isElementNode() && static_cast<const Element*>(node)->getAttribute(classAttr) == ApplePasteAsQuotation;}// Wrap CompositeEditCommand::removeNodePreservingChildren() so we can update the nodes we trackvoid ReplaceSelectionCommand::removeNodePreservingChildren(Node* node){ if (m_firstNodeInserted == node) m_firstNodeInserted = node->traverseNextNode(); if (m_lastLeafInserted == node) m_lastLeafInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling(); CompositeEditCommand::removeNodePreservingChildren(node);}// Wrap CompositeEditCommand::removeNodeAndPruneAncestors() so we can update the nodes we trackvoid ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node){ // prepare in case m_firstNodeInserted and/or m_lastLeafInserted get removed // FIXME: shouldn't m_lastLeafInserted be adjusted using traversePreviousNode()? Node* afterFirst = m_firstNodeInserted ? m_firstNodeInserted->traverseNextSibling() : 0; Node* afterLast = m_lastLeafInserted ? m_lastLeafInserted->traverseNextSibling() : 0; CompositeEditCommand::removeNodeAndPruneAncestors(node); // adjust m_firstNodeInserted and m_lastLeafInserted since either or both may have been removed if (m_lastLeafInserted && !m_lastLeafInserted->inDocument()) m_lastLeafInserted = afterLast; if (m_firstNodeInserted && !m_firstNodeInserted->inDocument()) m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0;}static bool isHeaderElement(Node* a){ if (!a) return false; return a->hasTagName(h1Tag) || a->hasTagName(h2Tag) || a->hasTagName(h3Tag) || a->hasTagName(h4Tag) || a->hasTagName(h5Tag);}static bool haveSameTagName(Node* a, Node* b){ return a && b && a->isElementNode() && b->isElementNode() && static_cast<Element*>(a)->tagName() == static_cast<Element*>(b)->tagName();}bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const VisiblePosition& destination){ if (source.isNull() || destination.isNull()) return false; Node* sourceNode = source.deepEquivalent().node(); Node* destinationNode = destination.deepEquivalent().node(); Node* sourceBlock = enclosingBlock(sourceNode); Node* destinationBlock = enclosingBlock(destinationNode); return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode) && sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailBlockquote(sourceBlock)) && enclosingListChild(sourceBlock) == enclosingListChild(destinationNode) && enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(destination.deepEquivalent()) && (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destinationBlock)) && // Don't merge to or from a position before or after a block because it would // be a no-op and cause infinite recursion. !isBlock(sourceNode) && !isBlock(destinationNode);}// Style rules that match just inserted elements could change their appearance, like// a div inserted into a document with div { display:inline; }.void ReplaceSelectionCommand::negateStyleRulesThatAffectAppearance(){ for (RefPtr<Node> node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) { // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance if (isStyleSpan(node.get())) { HTMLElement* e = static_cast<HTMLElement*>(node.get()); // There are other styles that style rules can give to style spans, // but these are the two important ones because they'll prevent // inserted content from appearing in the right paragraph. // FIXME: Hyatt is concerned that selectively using display:inline will give inconsistent // results. We already know one issue because td elements ignore their display property // in quirks mode (which Mail.app is always in). We should look for an alternative. if (isBlock(e)) e->getInlineStyleDecl()->setProperty(CSSPropertyDisplay, CSSValueInline); if (e->renderer() && e->renderer()->style()->floating() != FNONE) e->getInlineStyleDecl()->setProperty(CSSPropertyFloat, CSSValueNone); } if (node == m_lastLeafInserted) break; }}void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(){ document()->updateLayoutIgnorePendingStylesheets(); if (!m_lastLeafInserted->renderer() && m_lastLeafInserted->isTextNode() && !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), selectTag) && !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), scriptTag)) { if (m_firstNodeInserted == m_lastLeafInserted) { removeNode(m_lastLeafInserted.get()); m_lastLeafInserted = 0; m_firstNodeInserted = 0; return; } RefPtr<Node> previous = m_lastLeafInserted->traversePreviousNode(); removeNode(m_lastLeafInserted.get()); m_lastLeafInserted = previous; } // We don't have to make sure that m_firstNodeInserted isn't inside a select or script element, because // it is a top level node in the fragment and the user can't insert into those elements. if (!m_firstNodeInserted->renderer() && m_firstNodeInserted->isTextNode()) { if (m_firstNodeInserted == m_lastLeafInserted) { removeNode(m_firstNodeInserted.get()); m_firstNodeInserted = 0; m_lastLeafInserted = 0; return; } RefPtr<Node> next = m_firstNodeInserted->traverseNextSibling(); removeNode(m_firstNodeInserted.get()); m_firstNodeInserted = next; }}void ReplaceSelectionCommand::handlePasteAsQuotationNode(){ Node* node = m_firstNodeInserted.get(); if (isMailPasteAsQuotationNode(node)) removeNodeAttribute(static_cast<Element*>(node), classAttr);}VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent(){ Node* lastNode = m_lastLeafInserted.get(); Node* enclosingSelect = enclosingNodeWithTag(Position(lastNode, 0), selectTag); if (enclosingSelect) lastNode = enclosingSelect; return VisiblePosition(Position(lastNode, maxDeepOffset(lastNode)));}VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent(){ // Return the inserted content's first VisiblePosition. return VisiblePosition(nextCandidate(positionBeforeNode(m_firstNodeInserted.get())));}// Remove style spans before insertion if they are unnecessary. It's faster because we'll // avoid doing a layout.static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos){ Node* topNode = fragment.firstChild(); // Handling this case is more complicated (see handleStyleSpans) and doesn't receive the optimization. if (isMailPasteAsQuotationNode(topNode)) return false; // Either there are no style spans in the fragment or a WebKit client has added content to the fragment // before inserting it. Look for and handle style spans after insertion. if (!isStyleSpan(topNode)) return false; Node* sourceDocumentStyleSpan = topNode; RefPtr<Node> copiedRangeStyleSpan = sourceDocumentStyleSpan->firstChild();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -