📄 replaceselectioncommand.cpp
字号:
// Paste at start or end of link goes outside of link. insertionPos = positionAvoidingSpecialElementBoundary(insertionPos); // FIXME: Can this wait until after the operation has been performed? There doesn't seem to be // any work performed after this that queries or uses the typing style. if (Frame* frame = document()->frame()) frame->clearTypingStyle(); bool handledStyleSpans = handleStyleSpansBeforeInsertion(fragment, insertionPos); // We're finished if there is nothing to add. if (fragment.isEmpty() || !fragment.firstChild()) return; // 1) Insert the content. // 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>. // 3) Merge the start of the added content with the content before the position being pasted into. // 4) Do one of the following: a) expand the last br if the fragment ends with one and it collapsed, // b) merge the last paragraph of the incoming fragment with the paragraph that contained the // end of the selection that was pasted into, or c) handle an interchange newline at the end of the // incoming fragment. // 5) Add spaces for smart replace. // 6) Select the replacement if requested, and match style if requested. VisiblePosition startOfInsertedContent, endOfInsertedContent; RefPtr<Node> refNode = fragment.firstChild(); RefPtr<Node> node = refNode->nextSibling(); fragment.removeNode(refNode); insertNodeAtAndUpdateNodesInserted(refNode, insertionPos); while (node) { Node* next = node->nextSibling(); fragment.removeNode(node); insertNodeAfterAndUpdateNodesInserted(node, refNode.get()); refNode = node; node = next; } removeUnrenderedTextNodesAtEnds(); negateStyleRulesThatAffectAppearance(); if (!handledStyleSpans) handleStyleSpans(); // Mutation events (bug 20161) may have already removed the inserted content if (!m_firstNodeInserted || !m_firstNodeInserted->inDocument()) return; endOfInsertedContent = positionAtEndOfInsertedContent(); startOfInsertedContent = positionAtStartOfInsertedContent(); // We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and // didn't have a br after it, so the inserted content ended up in the same paragraph. if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.offset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent)) insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent()); Position lastPositionToSelect; bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd(); if (shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR)) removeNodeAndPruneAncestors(endBR); // Determine whether or not we should merge the end of inserted content with what's after it before we do // the start merge so that the start merge doesn't effect our decision. m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph); if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart())) { // Bail to avoid infinite recursion. if (m_movingParagraph) { // setting display:inline does not work for td elements in quirks mode ASSERT(m_firstNodeInserted->hasTagName(tdTag)); return; } VisiblePosition destination = startOfInsertedContent.previous(); VisiblePosition startOfParagraphToMove = startOfInsertedContent; // Merging the the first paragraph of inserted content with the content that came // before the selection that was pasted into would also move content after // the selection that was pasted into if: only one paragraph was being pasted, // and it was not wrapped in a block, the selection that was pasted into ended // at the end of a block and the next paragraph didn't start at the start of a block. // Insert a line break just after the inserted content to separate it from what // comes after and prevent that from happening. VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) insertNodeAt(createBreakElement(document()).get(), endOfInsertedContent.deepEquivalent()); // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are // only ever used to create positions where inserted content starts/ends. moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination); m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().node(); if (!m_lastLeafInserted->inDocument()) m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().node(); } endOfInsertedContent = positionAtEndOfInsertedContent(); startOfInsertedContent = positionAtStartOfInsertedContent(); if (interchangeNewlineAtEnd) { VisiblePosition next = endOfInsertedContent.next(true); if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) { if (!isStartOfParagraph(endOfInsertedContent)) { setEndingSelection(endOfInsertedContent); // Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph // block's style seems to annoy users. insertParagraphSeparator(true); // Select up to the paragraph separator that was added. lastPositionToSelect = endingSelection().visibleStart().deepEquivalent(); updateNodesInserted(lastPositionToSelect.node()); } } else { // Select up to the beginning of the next paragraph. lastPositionToSelect = next.deepEquivalent().downstream(); } } else mergeEndIfNeeded(); handlePasteAsQuotationNode(); endOfInsertedContent = positionAtEndOfInsertedContent(); startOfInsertedContent = positionAtStartOfInsertedContent(); // Add spaces for smart replace. if (m_smartReplace && currentRoot) { // Disable smart replace for password fields. Node* start = currentRoot->shadowAncestorNode(); if (start->hasTagName(inputTag) && static_cast<HTMLInputElement*>(start)->inputType() == HTMLInputElement::PASSWORD) m_smartReplace = false; } if (m_smartReplace) { bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false); if (needsTrailingSpace) { RenderObject* renderer = m_lastLeafInserted->renderer(); bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().node(); if (endNode->isTextNode()) { Text* text = static_cast<Text*>(endNode); insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); } else { RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); insertNodeAfterAndUpdateNodesInserted(node, endNode); } } bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true); if (needsLeadingSpace) { RenderObject* renderer = m_lastLeafInserted->renderer(); bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().node(); if (startNode->isTextNode()) { Text* text = static_cast<Text*>(startNode); insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); } else { RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); // Don't updateNodesInserted. Doing so would set m_lastLeafInserted to be the node containing the // leading space, but m_lastLeafInserted is supposed to mark the end of pasted content. insertNodeBefore(node, startNode); // FIXME: Use positions to track the start/end of inserted content. m_firstNodeInserted = node; } } } completeHTMLReplacement(lastPositionToSelect);}bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR){ if (!endBR || !endBR->inDocument()) return false; VisiblePosition visiblePos(Position(endBR, 0)); // Don't remove the br if nothing was inserted. if (visiblePos.previous() == originalVisPosBeforeEndBR) return false; // Remove the br if it is collapsed away and so is unnecessary. if (!document()->inStrictMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos)) return true; // A br that was originally holding a line open should be displaced by inserted content or turned into a line break. // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder. return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);}void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect){ Position start; Position end; // FIXME: This should never not be the case. if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastLeafInserted && m_lastLeafInserted->inDocument()) { start = positionAtStartOfInsertedContent().deepEquivalent(); end = positionAtEndOfInsertedContent().deepEquivalent(); // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps. rebalanceWhitespaceAt(start); rebalanceWhitespaceAt(end); if (m_matchStyle) { ASSERT(m_insertionStyle); applyStyle(m_insertionStyle.get(), start, end); } if (lastPositionToSelect.isNotNull()) end = lastPositionToSelect; } else if (lastPositionToSelect.isNotNull()) start = end = lastPositionToSelect; else return; if (m_selectReplacement) setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY)); else setEndingSelection(VisibleSelection(end, SEL_DEFAULT_AFFINITY));}EditAction ReplaceSelectionCommand::editingAction() const{ return m_editAction;}void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild){ Node* nodeToUpdate = insertChild.get(); // insertChild will be cleared when passed insertNodeAfter(insertChild, refChild); updateNodesInserted(nodeToUpdate);}void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(PassRefPtr<Node> insertChild, const Position& p){ Node* nodeToUpdate = insertChild.get(); // insertChild will be cleared when passed insertNodeAt(insertChild, p); updateNodesInserted(nodeToUpdate);}void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild){ Node* nodeToUpdate = insertChild.get(); // insertChild will be cleared when passed insertNodeBefore(insertChild, refChild); updateNodesInserted(nodeToUpdate);}void ReplaceSelectionCommand::updateNodesInserted(Node *node){ if (!node) return; if (!m_firstNodeInserted) m_firstNodeInserted = node; if (node == m_lastLeafInserted) return; m_lastLeafInserted = node->lastDescendant();}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -