DeleteSelectionCommand.cpp [plain text]
#include "config.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "EditorClient.h"
#include "ElementIterator.h"
#include "Frame.h"
#include "HTMLBRElement.h"
#include "HTMLLinkElement.h"
#include "HTMLNames.h"
#include "HTMLStyleElement.h"
#include "HTMLTableElement.h"
#include "NodeTraversal.h"
#include "RenderTableCell.h"
#include "RenderText.h"
#include "RenderedDocumentMarker.h"
#include "Text.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
namespace WebCore {
using namespace HTMLNames;
static bool isTableRow(const Node* node)
{
return node && node->hasTagName(trTag);
}
static bool isTableCellEmpty(Node* cell)
{
ASSERT(isTableCell(cell));
return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPositionInNode(cell));
}
static bool isTableRowEmpty(Node* row)
{
if (!isTableRow(row))
return false;
for (Node* child = row->firstChild(); child; child = child->nextSibling())
if (isTableCell(child) && !isTableCellEmpty(child))
return false;
return true;
}
DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
: CompositeEditCommand(document, editingAction)
, m_hasSelectionToDelete(false)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
, m_needPlaceholder(false)
, m_replace(replace)
, m_expandForSpecialElements(expandForSpecialElements)
, m_pruneStartBlockIfNecessary(false)
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
{
}
DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
: CompositeEditCommand(selection.start().anchorNode()->document(), editingAction)
, m_hasSelectionToDelete(true)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
, m_needPlaceholder(false)
, m_replace(replace)
, m_expandForSpecialElements(expandForSpecialElements)
, m_pruneStartBlockIfNecessary(false)
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
, m_selectionToDelete(selection)
{
}
void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
{
HTMLElement* startSpecialContainer = nullptr;
HTMLElement* endSpecialContainer = nullptr;
start = m_selectionToDelete.start();
end = m_selectionToDelete.end();
if (start.deprecatedNode()->hasTagName(hrTag))
start = positionBeforeNode(start.deprecatedNode());
else if (end.deprecatedNode()->hasTagName(hrTag))
end = positionAfterNode(end.deprecatedNode());
if (!m_expandForSpecialElements)
return;
while (1) {
startSpecialContainer = nullptr;
endSpecialContainer = nullptr;
Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer);
Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer);
if (!startSpecialContainer && !endSpecialContainer)
break;
m_mergeBlocksAfterDelete = false;
if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || VisiblePosition(end) != m_selectionToDelete.visibleEnd())
break;
if (startSpecialContainer && !endSpecialContainer && comparePositions(positionInParentAfterNode(startSpecialContainer), end) > -1)
break;
if (endSpecialContainer && !startSpecialContainer && comparePositions(start, positionInParentBeforeNode(endSpecialContainer)) > -1)
break;
if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSpecialContainer))
start = s;
else if (endSpecialContainer && endSpecialContainer->isDescendantOf(startSpecialContainer))
end = e;
else {
start = s;
end = e;
}
}
}
void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(const Position& start, const Position& end)
{
VisiblePosition newBase;
VisiblePosition newExtent;
if (startingSelection().isBaseFirst()) {
newBase = start;
newExtent = end;
} else {
newBase = end;
newExtent = start;
}
setStartingSelection(VisibleSelection(newBase, newExtent, startingSelection().isDirectional()));
}
void DeleteSelectionCommand::initializePositionData()
{
Position start, end;
initializeStartEnd(start, end);
if (!isEditablePosition(start, ContentIsEditable))
start = firstEditablePositionAfterPositionInRoot(start, highestEditableRoot(start));
if (!isEditablePosition(end, ContentIsEditable))
end = lastEditablePositionBeforePositionInRoot(end, highestEditableRoot(start));
m_upstreamStart = start.upstream();
m_downstreamStart = start.downstream();
m_upstreamEnd = end.upstream();
m_downstreamEnd = end.downstream();
m_startRoot = editableRootForPosition(start);
m_endRoot = editableRootForPosition(end);
m_startTableRow = enclosingNodeOfType(start, &isTableRow);
m_endTableRow = enclosingNodeOfType(end, &isTableRow);
Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCrossEditingBoundary);
Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, CanCrossEditingBoundary);
if (endCell && endCell != startCell)
m_mergeBlocksAfterDelete = false;
VisiblePosition visibleEnd(m_downstreamEnd);
if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd))
m_endingPosition = m_downstreamEnd;
else
m_endingPosition = m_downstreamStart;
if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end)
&& isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosition(start))
&& endingSelection().isRange()) {
m_mergeBlocksAfterDelete = false;
m_pruneStartBlockIfNecessary = true;
}
m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity());
m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
if (m_smartDelete) {
Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent();
bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
if (!skipSmartDelete)
skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull();
if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) {
VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous();
pos = visiblePos.deepEquivalent();
m_upstreamStart = pos.upstream();
m_downstreamStart = pos.downstream();
m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity());
setStartingSelectionOnSmartDelete(m_upstreamStart, m_upstreamEnd);
}
if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) {
pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent();
m_upstreamEnd = pos.upstream();
m_downstreamEnd = pos.downstream();
m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd);
}
}
m_startBlock = enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary);
m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary);
}
void DeleteSelectionCommand::saveTypingStyleState()
{
if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode()) {
frame().selection().clearTypingStyle();
return;
}
m_typingStyle = EditingStyle::create(m_selectionToDelete.start(), EditingStyle::EditingPropertiesInEffect);
m_typingStyle->removeStyleAddedByNode(enclosingAnchorElement(m_selectionToDelete.start()));
if (enclosingNodeOfType(m_selectionToDelete.start(), isMailBlockquote))
m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end());
else
m_deleteIntoBlockquoteStyle = nullptr;
}
bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
{
Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition();
Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition();
Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition();
if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart)
return false;
bool upstreamStartIsBR = nodeAfterUpstreamStart->hasTagName(brTag);
bool downstreamStartIsBR = nodeAfterDownstreamStart->hasTagName(brTag);
bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && ((nodeAfterDownstreamStart == nodeAfterUpstreamEnd) || (nodeAfterUpstreamEnd && nodeAfterUpstreamEnd->hasTagName(brTag) && nodeAfterUpstreamStart->nextSibling() == nodeAfterUpstreamEnd));
if (isBROnLineByItself) {
removeNode(nodeAfterDownstreamStart);
return true;
}
if (upstreamStartIsBR && downstreamStartIsBR
&& !(isStartOfBlock(positionBeforeNode(nodeAfterUpstreamStart)) && isEndOfBlock(positionAfterNode(nodeAfterDownstreamStart)))
&& (!nodeAfterUpstreamEnd || nodeAfterUpstreamEnd->hasTagName(brTag) || nodeAfterUpstreamEnd->previousSibling() != nodeAfterUpstreamStart)) {
m_startsAtEmptyLine = true;
m_endingPosition = m_downstreamEnd;
}
return false;
}
static Position firstEditablePositionInNode(Node* node)
{
ASSERT(node);
Node* next = node;
while (next && !next->hasEditableStyle())
next = NodeTraversal::next(*next, node);
return next ? firstPositionInOrBeforeNode(next) : Position();
}
void DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded(Element& element)
{
auto* renderer = element.renderer();
if (!is<RenderTableCell>(renderer))
return;
if (downcast<RenderTableCell>(*renderer).contentHeight() > 0)
return;
insertBlockPlaceholder(firstEditablePositionInNode(&element));
}
void DeleteSelectionCommand::removeNodeUpdatingStates(Node& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
if (&node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
m_needPlaceholder = true;
else if (&node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
m_needPlaceholder = true;
updatePositionForNodeRemoval(m_endingPosition, node);
updatePositionForNodeRemoval(m_leadingWhitespace, node);
updatePositionForNodeRemoval(m_trailingWhitespace, node);
CompositeEditCommand::removeNode(&node, shouldAssumeContentIsAlwaysEditable);
}
static inline bool shouldRemoveContentOnly(const Node& node)
{
return isTableStructureNode(&node) || node.isRootEditableElement();
}
void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
if (!node)
return;
if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) {
if (!node->parentNode()->hasEditableStyle()) {
if (!node->firstChild())
return;
RefPtr<Node> child = node->firstChild();
while (child) {
RefPtr<Node> nextChild = child->nextSibling();
removeNode(child.get(), shouldAssumeContentIsAlwaysEditable);
if (nextChild && nextChild->parentNode() != node)
return;
child = nextChild;
}
return;
}
}
if (shouldRemoveContentOnly(*node)) {
auto* child = NodeTraversal::next(*node, node.get());
while (child) {
if (shouldRemoveContentOnly(*child)) {
child = NodeTraversal::next(*child, node.get());
continue;
}
auto* remove = child;
child = NodeTraversal::nextSkippingChildren(*child, node.get());
removeNodeUpdatingStates(*remove, shouldAssumeContentIsAlwaysEditable);
}
ASSERT(is<Element>(*node));
auto& element = downcast<Element>(*node);
document().updateLayoutIgnorePendingStylesheets();
auto* descendant = ElementTraversal::next(element, &element);
while (descendant) {
auto* placeholderCandidate = descendant;
descendant = ElementTraversal::next(*descendant, &element);
insertBlockPlaceholderForTableCellIfNeeded(*placeholderCandidate);
}
insertBlockPlaceholderForTableCellIfNeeded(element);
return;
}
removeNodeUpdatingStates(*node, shouldAssumeContentIsAlwaysEditable);
}
static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
{
if (position.anchorType() != Position::PositionIsOffsetInAnchor || position.containerNode() != node)
return;
if (position.offsetInContainerNode() > offset + count)
position.moveToOffset(position.offsetInContainerNode() - count);
else if (position.offsetInContainerNode() > offset)
position.moveToOffset(offset);
}
void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
{
updatePositionForTextRemoval(node.get(), offset, count, m_endingPosition);
updatePositionForTextRemoval(node.get(), offset, count, m_leadingWhitespace);
updatePositionForTextRemoval(node.get(), offset, count, m_trailingWhitespace);
updatePositionForTextRemoval(node.get(), offset, count, m_downstreamEnd);
CompositeEditCommand::deleteTextFromNode(node, offset, count);
}
void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss()
{
RefPtr<Range> range = m_selectionToDelete.toNormalizedRange();
RefPtr<Node> node = range->firstNode();
while (node && node != range->pastLastNode()) {
RefPtr<Node> nextNode = NodeTraversal::next(*node);
if ((is<HTMLStyleElement>(*node) && !downcast<HTMLStyleElement>(*node).hasAttributeWithoutSynchronization(scopedAttr)) || is<HTMLLinkElement>(*node)) {
nextNode = NodeTraversal::nextSkippingChildren(*node);
RefPtr<ContainerNode> rootEditableElement = node->rootEditableElement();
if (rootEditableElement) {
removeNode(node);
appendNode(node, rootEditableElement);
}
}
node = nextNode;
}
}
void DeleteSelectionCommand::handleGeneralDelete()
{
if (m_upstreamStart.isNull())
return;
int startOffset = m_upstreamStart.deprecatedEditingOffset();
Node* startNode = m_upstreamStart.deprecatedNode();
makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
if (startNode == m_startBlock && !startOffset && canHaveChildrenForEditing(*startNode) && !is<HTMLTableElement>(*startNode)) {
startOffset = 0;
startNode = NodeTraversal::next(*startNode);
if (!startNode)
return;
}
int startNodeCaretMaxOffset = caretMaxOffset(*startNode);
if (startOffset >= startNodeCaretMaxOffset && is<Text>(*startNode)) {
Text& text = downcast<Text>(*startNode);
if (text.length() > static_cast<unsigned>(startNodeCaretMaxOffset))
deleteTextFromNode(&text, startNodeCaretMaxOffset, text.length() - startNodeCaretMaxOffset);
}
if (startOffset >= lastOffsetForEditing(*startNode)) {
startNode = NodeTraversal::nextSkippingChildren(*startNode);
startOffset = 0;
}
if (!startNode)
return;
if (startNode == m_downstreamEnd.deprecatedNode()) {
if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
if (is<Text>(*startNode)) {
Text& text = downcast<Text>(*startNode);
deleteTextFromNode(&text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
} else {
removeChildrenInRange(startNode, startOffset, m_downstreamEnd.deprecatedEditingOffset());
m_endingPosition = m_upstreamStart;
}
}
if (!startNode->renderer() || (!startOffset && m_downstreamEnd.atLastEditingPositionForNode()))
removeNode(startNode);
}
else {
bool startNodeWasDescendantOfEndNode = m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode());
RefPtr<Node> node(startNode);
if (startOffset > 0) {
if (is<Text>(*startNode)) {
Text& text = downcast<Text>(*node);
deleteTextFromNode(&text, startOffset, text.length() - startOffset);
node = NodeTraversal::next(*node);
} else {
node = startNode->traverseToChildAt(startOffset);
}
} else if (startNode == m_upstreamEnd.deprecatedNode() && is<Text>(*startNode)) {
Text& text = downcast<Text>(*m_upstreamEnd.deprecatedNode());
deleteTextFromNode(&text, 0, m_upstreamEnd.deprecatedEditingOffset());
}
while (node && node != m_downstreamEnd.deprecatedNode()) {
if (comparePositions(firstPositionInOrBeforeNode(node.get()), m_downstreamEnd) >= 0) {
node = nullptr;
} else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(*node)) {
RefPtr<Node> nextNode = NodeTraversal::nextSkippingChildren(*node);
updatePositionForNodeRemoval(m_downstreamEnd, *node);
removeNode(node.get());
node = nextNode.get();
} else {
Node* n = node->lastDescendant();
if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(*n)) {
removeNode(node.get());
node = nullptr;
} else
node = NodeTraversal::next(*node);
}
}
if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()) && m_downstreamEnd.anchorNode()->inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(*m_downstreamEnd.deprecatedNode())) {
if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(*m_downstreamEnd.deprecatedNode())) {
removeNode(m_downstreamEnd.deprecatedNode());
} else {
if (is<Text>(*m_downstreamEnd.deprecatedNode())) {
Text& text = downcast<Text>(*m_downstreamEnd.deprecatedNode());
if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
deleteTextFromNode(&text, 0, m_downstreamEnd.deprecatedEditingOffset());
}
} else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->inDocument())) {
unsigned offset = 0;
if (m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode())) {
Node* n = m_upstreamStart.deprecatedNode();
while (n && n->parentNode() != m_downstreamEnd.deprecatedNode())
n = n->parentNode();
if (n)
offset = n->computeNodeIndex() + 1;
}
removeChildrenInRange(m_downstreamEnd.deprecatedNode(), offset, m_downstreamEnd.deprecatedEditingOffset());
m_downstreamEnd = createLegacyEditingPosition(m_downstreamEnd.deprecatedNode(), offset);
}
}
}
}
}
void DeleteSelectionCommand::fixupWhitespace()
{
document().updateLayoutIgnorePendingStylesheets();
if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && is<Text>(*m_leadingWhitespace.deprecatedNode())) {
Text& textNode = downcast<Text>(*m_leadingWhitespace.deprecatedNode());
ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
replaceTextInNodePreservingMarkers(&textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && is<Text>(*m_trailingWhitespace.deprecatedNode())) {
Text& textNode = downcast<Text>(*m_trailingWhitespace.deprecatedNode());
ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
replaceTextInNodePreservingMarkers(&textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
}
void DeleteSelectionCommand::mergeParagraphs()
{
if (!m_mergeBlocksAfterDelete) {
if (m_pruneStartBlockIfNecessary) {
prune(m_startBlock);
m_needPlaceholder = false;
}
return;
}
ASSERT(!m_pruneStartBlockIfNecessary);
if (!m_downstreamEnd.anchorNode()->inDocument() || !m_upstreamStart.anchorNode()->inDocument())
return;
if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0)
return;
if (m_upstreamStart == m_downstreamEnd)
return;
VisiblePosition startOfParagraphToMove(m_downstreamEnd);
VisiblePosition mergeDestination(m_upstreamStart);
Element* endBlock = enclosingBlock(m_downstreamEnd.deprecatedNode());
if (!endBlock || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode()) || !startOfParagraphToMove.deepEquivalent().deprecatedNode()) {
removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode()));
return;
}
if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination.deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStart.containerNode())) || m_startsAtEmptyLine) {
insertNodeAt(HTMLBRElement::create(document()).ptr(), m_upstreamStart);
mergeDestination = VisiblePosition(m_upstreamStart);
}
if (mergeDestination == startOfParagraphToMove)
return;
VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove, CanSkipOverEditingBoundary);
if (mergeDestination == endOfParagraphToMove)
return;
if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfParagraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds().x()) {
if (mergeDestination.deepEquivalent().downstream().deprecatedNode()->hasTagName(brTag)) {
removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().deprecatedNode());
m_endingPosition = startOfParagraphToMove.deepEquivalent();
return;
}
}
if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) {
m_endingPosition = m_upstreamStart;
return;
}
RefPtr<Range> range = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), endOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent());
RefPtr<Range> rangeToBeReplaced = Range::create(document(), mergeDestination.deepEquivalent().parentAnchoredEquivalent(), mergeDestination.deepEquivalent().parentAnchoredEquivalent());
if (!frame().editor().client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get()))
return;
bool needPlaceholder = m_needPlaceholder;
bool paragraphToMergeIsEmpty = (startOfParagraphToMove == endOfParagraphToMove);
moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination, false, !paragraphToMergeIsEmpty);
m_needPlaceholder = needPlaceholder;
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))
CompositeEditCommand::removeNode(row);
row = previousRow.get();
}
}
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())) {
if (!m_endingPosition.deprecatedNode()->isDescendantOf(*m_endTableRow)) {
CompositeEditCommand::removeNode(m_endTableRow.get());
}
}
}
void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
{
if (!m_typingStyle)
return;
if (m_deleteIntoBlockquoteStyle && !enclosingNodeOfType(m_endingPosition, isMailBlockquote, CanCrossEditingBoundary))
m_typingStyle = m_deleteIntoBlockquoteStyle;
m_deleteIntoBlockquoteStyle = nullptr;
m_typingStyle->prepareToApplyAt(m_endingPosition);
if (m_typingStyle->isEmpty())
m_typingStyle = nullptr;
frame().selection().setTypingStyle(m_typingStyle);
}
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();
}
String DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelection()
{
if (!m_selectionToDelete.isRange())
return String();
VisiblePosition startOfSelection = m_selectionToDelete.start();
if (!isStartOfWord(startOfSelection))
return String();
VisiblePosition nextPosition = startOfSelection.next();
if (nextPosition.isNull())
return String();
RefPtr<Range> rangeOfFirstCharacter = Range::create(document(), startOfSelection.deepEquivalent(), nextPosition.deepEquivalent());
Vector<RenderedDocumentMarker*> markers = document().markers().markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
for (auto* marker : markers) {
int startOffset = marker->startOffset();
if (startOffset == startOfSelection.deepEquivalent().offsetInContainerNode())
return marker->description();
}
return String();
}
void DeleteSelectionCommand::removeRedundantBlocks()
{
Node* node = m_endingPosition.containerNode();
Node* rootNode = node->rootEditableElement();
while (node != rootNode) {
if (isRemovableBlock(node)) {
if (node == m_endingPosition.anchorNode())
updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node);
CompositeEditCommand::removeNodePreservingChildren(node);
node = m_endingPosition.anchorNode();
} else
node = node->parentNode();
}
}
void DeleteSelectionCommand::doApply()
{
if (!m_hasSelectionToDelete)
m_selectionToDelete = endingSelection();
if (!m_selectionToDelete.isNonOrphanedRange())
return;
String originalString = originalStringForAutocorrectionAtBeginningOfSelection();
if (!m_replace) {
Element* textControl = enclosingTextFormControl(m_selectionToDelete.start());
if (textControl && textControl->focused())
frame().editor().textWillBeDeletedInTextField(textControl);
}
EAffinity affinity = m_selectionToDelete.affinity();
Position downstreamEnd = m_selectionToDelete.end().downstream();
m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart(), CanCrossEditingBoundary)
&& isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditingBoundary)
&& !lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd());
if (m_needPlaceholder) {
if (auto* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart())) {
if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(*table))
m_needPlaceholder = false;
}
}
initializePositionData();
deleteInsignificantTextDownstream(m_trailingWhitespace);
saveTypingStyleState();
if (handleSpecialCaseBRDelete()) {
calculateTypingStyleAfterDelete();
setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
clearTransientState();
rebalanceWhitespace();
return;
}
handleGeneralDelete();
fixupWhitespace();
mergeParagraphs();
removePreviouslySelectedEmptyTableRows();
if (m_needPlaceholder) {
if (m_sanitizeMarkup)
removeRedundantBlocks();
insertNodeAt(HTMLBRElement::create(document()), m_endingPosition);
}
bool shouldRebalaceWhiteSpace = true;
if (!frame().editor().behavior().shouldRebalanceWhiteSpacesInSecureField()) {
Node* node = m_endingPosition.deprecatedNode();
if (is<Text>(node)) {
Text& textNode = downcast<Text>(*node);
if (textNode.length() && textNode.renderer())
shouldRebalaceWhiteSpace = textNode.renderer()->style().textSecurity() == TSNONE;
}
}
if (shouldRebalaceWhiteSpace)
rebalanceWhitespaceAt(m_endingPosition);
calculateTypingStyleAfterDelete();
if (!originalString.isEmpty())
frame().editor().deletedAutocorrectionAtPosition(m_endingPosition, originalString);
setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
clearTransientState();
}
bool DeleteSelectionCommand::preservesTypingStyle() const
{
return m_typingStyle;
}
}