📄 deleteselectioncommand.cpp
字号:
{ if (!m_mergeBlocksAfterDelete) { if (m_pruneStartBlockIfNecessary) { // Make sure that the ending position isn't inside the block we're about to prune. m_endingPosition = m_downstreamEnd; // We aren't going to merge into the start block, so remove it if it's empty. prune(m_upstreamStart.node()); // Removing the start block during a deletion is usually an indication that we need // a placeholder, but not in this case. m_needPlaceholder = false; } return; } // It shouldn't have been asked to both try and merge content into the start block and prune it. ASSERT(!m_pruneStartBlockIfNecessary); // FIXME: Deletion should adjust selection endpoints as it removes nodes so that we never get into this state (4099839). if (!m_downstreamEnd.node()->inDocument() || !m_upstreamStart.node()->inDocument()) return; // FIXME: The deletion algorithm shouldn't let this happen. if (Range::compareBoundaryPoints(m_upstreamStart, m_downstreamEnd) > 0) return; // FIXME: Merging will always be unnecessary in this case, but we really bail here because this is a case where // deletion commonly fails to adjust its endpoints, which would cause the visible position comparison below to false negative. if (m_endBlock == m_startBlock) return; VisiblePosition startOfParagraphToMove(m_downstreamEnd); VisiblePosition mergeDestination(m_upstreamStart); // m_downstreamEnd's block has been emptied out by deletion. There is no content inside of it to // move, so just remove it. Element* endBlock = static_cast<Element*>(enclosingBlock(m_downstreamEnd.node())); if (!startOfParagraphToMove.deepEquivalent().node() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().node())) { removeNode(enclosingBlock(m_downstreamEnd.node())); return; } // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion. if (!mergeDestination.deepEquivalent().node() || !mergeDestination.deepEquivalent().node()->isDescendantOf(m_upstreamStart.node()->enclosingBlockFlowElement())) { insertNodeAt(createBreakElement(document()).get(), m_upstreamStart); mergeDestination = VisiblePosition(m_upstreamStart); } if (mergeDestination == startOfParagraphToMove) return; VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove); if (mergeDestination == endOfParagraphToMove) return; // The rule for merging into an empty block is: only do so if its farther to the right. // FIXME: Consider RTL. // FIXME: handleSpecialCaseBRDelete prevents us from getting here in a case like <ul><li>foo<br><br></li></ul>^foo if (isStartOfParagraph(mergeDestination) && startOfParagraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds().x()) { ASSERT(mergeDestination.deepEquivalent().downstream().node()->hasTagName(brTag)); removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().node()); m_endingPosition = startOfParagraphToMove.deepEquivalent(); return; } RefPtr<Range> range = Range::create(document(), rangeCompliantEquivalent(startOfParagraphToMove.deepEquivalent()), rangeCompliantEquivalent(endOfParagraphToMove.deepEquivalent())); RefPtr<Range> rangeToBeReplaced = Range::create(document(), rangeCompliantEquivalent(mergeDestination.deepEquivalent()), rangeCompliantEquivalent(mergeDestination.deepEquivalent())); if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get())) return; // moveParagraphs will insert placeholders if it removes blocks that would require their use, don't let block // removals that it does cause the insertion of *another* placeholder. bool needPlaceholder = m_needPlaceholder; moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination); m_needPlaceholder = needPlaceholder; // The endingPosition was likely clobbered by the move, so recompute it (moveParagraph selects the moved paragraph). m_endingPosition = endingSelection().start();}void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows(){ if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) { Node* row = m_endTableRow->previousSibling(); while (row && row != m_startTableRow) { RefPtr<Node> previousRow = row->previousSibling(); if (isTableRowEmpty(row)) // Use a raw removeNode, instead of DeleteSelectionCommand's, because // that won't remove rows, it only empties them in preparation for this function. CompositeEditCommand::removeNode(row); row = previousRow.get(); } } // Remove empty rows after the start row. if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m_endTableRow) { Node* row = m_startTableRow->nextSibling(); while (row && row != m_endTableRow) { RefPtr<Node> nextRow = row->nextSibling(); if (isTableRowEmpty(row)) CompositeEditCommand::removeNode(row); row = nextRow.get(); } } if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) if (isTableRowEmpty(m_endTableRow.get())) { // Don't remove m_endTableRow if it's where we're putting the ending selection. if (!m_endingPosition.node()->isDescendantOf(m_endTableRow.get())) { // FIXME: We probably shouldn't remove m_endTableRow unless it's fully selected, even if it is empty. // We'll need to start adjusting the selection endpoints during deletion to know whether or not m_endTableRow // was fully selected here. CompositeEditCommand::removeNode(m_endTableRow.get()); } }}void DeleteSelectionCommand::calculateTypingStyleAfterDelete(){ if (!m_typingStyle) return; // Compute the difference between the style before the delete and the style now // after the delete has been done. Set this style on the frame, so other editing // commands being composed with this one will work, and also cache it on the command, // so the Frame::appliedEditing can set it after the whole composite command // has completed. // If we deleted into a blockquote, but are now no longer in a blockquote, use the alternate typing style if (m_deleteIntoBlockquoteStyle && !nearestMailBlockquote(m_endingPosition.node())) m_typingStyle = m_deleteIntoBlockquoteStyle; m_deleteIntoBlockquoteStyle = 0; RefPtr<CSSComputedStyleDeclaration> endingStyle = computedStyle(m_endingPosition.node()); endingStyle->diff(m_typingStyle.get()); if (!m_typingStyle->length()) m_typingStyle = 0; VisiblePosition visibleEnd(m_endingPosition); if (m_typingStyle && isStartOfParagraph(visibleEnd) && isEndOfParagraph(visibleEnd) && lineBreakExistsAtPosition(visibleEnd)) { // Apply style to the placeholder that is now holding open the empty paragraph. // This makes sure that the paragraph has the right height, and that the paragraph // takes on the right style and retains it even if you move the selection away and // then move it back (which will clear typing style). setEndingSelection(visibleEnd); applyStyle(m_typingStyle.get(), EditActionUnspecified); // applyStyle can destroy the placeholder that was at m_endingPosition if it needs to // move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so. m_endingPosition = endingSelection().start(); m_typingStyle = 0; } // This is where we've deleted all traces of a style but not a whole paragraph (that's handled above). // In this case if we start typing, the new characters should have the same style as the just deleted ones, // but, if we change the selection, come back and start typing that style should be lost. Also see // preserveTypingStyle() below. document()->frame()->setTypingStyle(m_typingStyle.get());}void DeleteSelectionCommand::clearTransientState(){ m_selectionToDelete = VisibleSelection(); m_upstreamStart.clear(); m_downstreamStart.clear(); m_upstreamEnd.clear(); m_downstreamEnd.clear(); m_endingPosition.clear(); m_leadingWhitespace.clear(); m_trailingWhitespace.clear();}void DeleteSelectionCommand::doApply(){ // If selection has not been set to a custom selection when the command was created, // use the current ending selection. if (!m_hasSelectionToDelete) m_selectionToDelete = endingSelection(); if (!m_selectionToDelete.isRange()) return; // If the deletion is occurring in a text field, and we're not deleting to replace the selection, then let the frame call across the bridge to notify the form delegate. if (!m_replace) { Node* startNode = m_selectionToDelete.start().node(); Node* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0; if (ancestorNode && ancestorNode->hasTagName(inputTag) && static_cast<HTMLInputElement*>(ancestorNode)->isTextField() && ancestorNode->focused()) document()->frame()->textWillBeDeletedInTextField(static_cast<Element*>(ancestorNode)); } // save this to later make the selection with EAffinity affinity = m_selectionToDelete.affinity(); Position downstreamEnd = m_selectionToDelete.end().downstream(); m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart()) && isEndOfParagraph(m_selectionToDelete.visibleEnd()) && !lineBreakExistsAtPosition(m_selectionToDelete.visibleEnd()); if (m_needPlaceholder) { // Don't need a placeholder when deleting a selection that starts just before a table // and ends inside it (we do need placeholders to hold open empty cells, but that's // handled elsewhere). if (Node* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart())) if (m_selectionToDelete.end().node()->isDescendantOf(table)) m_needPlaceholder = false; } // set up our state initializePositionData(); // Delete any text that may hinder our ability to fixup whitespace after the delete deleteInsignificantTextDownstream(m_trailingWhitespace); saveTypingStyleState(); // deleting just a BR is handled specially, at least because we do not // want to replace it with a placeholder BR! if (handleSpecialCaseBRDelete()) { calculateTypingStyleAfterDelete(); setEndingSelection(VisibleSelection(m_endingPosition, affinity)); clearTransientState(); rebalanceWhitespace(); return; } handleGeneralDelete(); fixupWhitespace(); mergeParagraphs(); removePreviouslySelectedEmptyTableRows(); RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0; if (placeholder) insertNodeAt(placeholder.get(), m_endingPosition); rebalanceWhitespaceAt(m_endingPosition); calculateTypingStyleAfterDelete(); setEndingSelection(VisibleSelection(m_endingPosition, affinity)); clearTransientState();}EditAction DeleteSelectionCommand::editingAction() const{ // Note that DeleteSelectionCommand is also used when the user presses the Delete key, // but in that case there's a TypingCommand that supplies the editingAction(), so // the Undo menu correctly shows "Undo Typing" return EditActionCut;}// Normally deletion doesn't preserve the typing style that was present before it. For example,// type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't// stick around. Deletion should preserve a typing style that *it* sets, however.bool DeleteSelectionCommand::preservesTypingStyle() const{ return m_typingStyle;}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -