This source file includes following definitions.
- isTableCellEmpty
- isTableRowEmpty
- m_deleteIntoBlockquoteStyle
- m_deleteIntoBlockquoteStyle
- initializeStartEnd
- setStartingSelectionOnSmartDelete
- initializePositionData
- shouldNotInheritStyleFrom
- saveTypingStyleState
- handleSpecialCaseBRDelete
- firstEditablePositionInNode
- removeNode
- updatePositionForTextRemoval
- deleteTextFromNode
- makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss
- handleGeneralDelete
- fixupWhitespace
- mergeParagraphs
- removePreviouslySelectedEmptyTableRows
- calculateTypingStyleAfterDelete
- clearTransientState
- removeRedundantBlocks
- doApply
- editingAction
- preservesTypingStyle
#include "config.h"
#include "core/editing/DeleteSelectionCommand.h"
#include "HTMLNames.h"
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/Text.h"
#include "core/editing/EditingBoundary.h"
#include "core/editing/Editor.h"
#include "core/editing/VisibleUnits.h"
#include "core/editing/htmlediting.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLInputElement.h"
#include "core/rendering/RenderTableCell.h"
namespace WebCore {
using namespace HTMLNames;
static bool isTableCellEmpty(Node* cell)
{
ASSERT(isTableCell(cell));
return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPositionInNode(cell));
}
static bool isTableRowEmpty(Node* row)
{
if (!isHTMLTableRowElement(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 expandForSpecialElements, bool sanitizeMarkup)
: CompositeEditCommand(document)
, m_hasSelectionToDelete(false)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
, m_needPlaceholder(false)
, m_expandForSpecialElements(expandForSpecialElements)
, m_pruneStartBlockIfNecessary(false)
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
, m_startBlock(nullptr)
, m_endBlock(nullptr)
, m_typingStyle(nullptr)
, m_deleteIntoBlockquoteStyle(nullptr)
{
}
DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup)
: CompositeEditCommand(*selection.start().document())
, m_hasSelectionToDelete(true)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
, m_needPlaceholder(false)
, m_expandForSpecialElements(expandForSpecialElements)
, m_pruneStartBlockIfNecessary(false)
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
, m_selectionToDelete(selection)
, m_startBlock(nullptr)
, m_endBlock(nullptr)
, m_typingStyle(nullptr)
, m_deleteIntoBlockquoteStyle(nullptr)
{
}
void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
{
Node* startSpecialContainer = 0;
Node* endSpecialContainer = 0;
start = m_selectionToDelete.start();
end = m_selectionToDelete.end();
if (isHTMLHRElement(*start.deprecatedNode()))
start = positionBeforeNode(start.deprecatedNode());
else if (isHTMLHRElement(*end.deprecatedNode()))
end = positionAfterNode(end.deprecatedNode());
if (!m_expandForSpecialElements)
return;
while (1) {
startSpecialContainer = 0;
endSpecialContainer = 0;
Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer);
Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer);
if (!startSpecialContainer && !endSpecialContainer)
break;
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)
{
bool isBaseFirst = startingSelection().isBaseFirst();
VisiblePosition newBase(isBaseFirst ? start : end);
VisiblePosition newExtent(isBaseFirst ? end : start);
setStartingSelection(VisibleSelection(newBase, newExtent, startingSelection().isDirectional()));
}
void DeleteSelectionCommand::initializePositionData()
{
Position start, end;
initializeStartEnd(start, end);
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, &isHTMLTableRowElement);
m_endTableRow = enclosingNodeOfType(end, &isHTMLTableRowElement);
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);
}
static bool shouldNotInheritStyleFrom(const Node& node)
{
return !node.canContainRangeEndPoint();
}
void DeleteSelectionCommand::saveTypingStyleState()
{
if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode())
return;
if (shouldNotInheritStyleFrom(*m_selectionToDelete.start().anchorNode()))
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 = isHTMLBRElement(*nodeAfterUpstreamStart);
bool downstreamStartIsBR = isHTMLBRElement(*nodeAfterDownstreamStart);
bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && nodeAfterDownstreamStart == nodeAfterUpstreamEnd;
if (isBROnLineByItself) {
removeNode(nodeAfterDownstreamStart);
return true;
}
if (upstreamStartIsBR && downstreamStartIsBR && !(isStartOfBlock(VisiblePosition(positionBeforeNode(nodeAfterUpstreamStart))) && isEndOfBlock(VisiblePosition(positionAfterNode(nodeAfterUpstreamStart))))) {
m_startsAtEmptyLine = true;
m_endingPosition = m_downstreamEnd;
}
return false;
}
static Position firstEditablePositionInNode(Node* node)
{
ASSERT(node);
Node* next = node;
while (next && !next->rendererIsEditable())
next = NodeTraversal::next(*next, node);
return next ? firstPositionInOrBeforeNode(next) : Position();
}
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()->rendererIsEditable()) {
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 (isTableStructureNode(node.get()) || node->isRootEditableElement()) {
Node* child = node->firstChild();
while (child) {
Node* remove = child;
child = child->nextSibling();
removeNode(remove, shouldAssumeContentIsAlwaysEditable);
}
document().updateLayoutIgnorePendingStylesheets();
RenderObject *r = node->renderer();
if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) {
Position firstEditablePosition = firstEditablePositionInNode(node.get());
if (firstEditablePosition.isNotNull())
insertBlockPlaceholder(firstEditablePosition);
}
return;
}
if (node == m_startBlock) {
VisiblePosition previous = VisiblePosition(firstPositionInNode(m_startBlock.get())).previous();
if (previous.isNotNull() && !isEndOfBlock(previous))
m_needPlaceholder = true;
}
if (node == m_endBlock) {
VisiblePosition next = VisiblePosition(lastPositionInNode(m_endBlock.get())).next();
if (next.isNotNull() && !isStartOfBlock(next))
m_needPlaceholder = true;
}
updatePositionForNodeRemoval(m_endingPosition, *node);
updatePositionForNodeRemoval(m_leadingWhitespace, *node);
updatePositionForNodeRemoval(m_trailingWhitespace, *node);
CompositeEditCommand::removeNode(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()
{
RefPtrWillBeRawPtr<Range> range = m_selectionToDelete.toNormalizedRange();
RefPtr<Node> node = range->firstNode();
while (node && node != range->pastLastNode()) {
RefPtr<Node> nextNode = NodeTraversal::next(*node);
if ((isHTMLStyleElement(*node) && !(toElement(node)->hasAttribute(scopedAttr))) || isHTMLLinkElement(*node)) {
nextNode = NodeTraversal::nextSkippingChildren(*node);
RefPtr<ContainerNode> rootEditableElement = node->rootEditableElement();
if (rootEditableElement.get()) {
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();
ASSERT(startNode);
makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
if (startNode->isSameNode(m_startBlock.get()) && !startOffset && canHaveChildrenForEditing(startNode) && !isHTMLTableElement(*startNode)) {
startOffset = 0;
startNode = NodeTraversal::next(*startNode);
if (!startNode)
return;
}
if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
Text* text = toText(startNode);
if (text->length() > (unsigned)caretMaxOffset(startNode))
deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
}
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 (startNode->isTextNode()) {
Text* text = toText(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 (startNode->isTextNode()) {
Text* text = toText(node);
deleteTextFromNode(text, startOffset, text->length() - startOffset);
node = NodeTraversal::next(*node);
} else {
node = startNode->traverseToChildAt(startOffset);
}
} else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) {
Text* text = toText(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.get())) {
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.inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode())) {
if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.deprecatedNode())) {
removeNode(m_downstreamEnd.deprecatedNode());
} else {
if (m_downstreamEnd.deprecatedNode()->isTextNode()) {
Text* text = toText(m_downstreamEnd.deprecatedNode());
if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset());
}
} else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.inDocument())) {
int 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->nodeIndex() + 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() && m_leadingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = toText(m_leadingWhitespace.deprecatedNode());
ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
replaceTextInNodePreservingMarkers(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = toText(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.inDocument() || !m_upstreamStart.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())) && (!mergeDestination.deepEquivalent().anchorNode()->firstChild() || !m_upstreamStart.containerNode()->firstChild())) || (m_startsAtEmptyLine && mergeDestination != startOfParagraphToMove)) {
insertNodeAt(createBreakElement(document()).get(), m_upstreamStart);
mergeDestination = VisiblePosition(m_upstreamStart);
}
if (mergeDestination == startOfParagraphToMove)
return;
VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove, CanSkipOverEditingBoundary);
if (mergeDestination == endOfParagraphToMove)
return;
Node* listItemInFirstParagraph = enclosingNodeOfType(m_upstreamStart, isListItem);
Node* listItemInSecondParagraph = enclosingNodeOfType(m_downstreamEnd, isListItem);
if (listItemInFirstParagraph && listItemInSecondParagraph
&& canMergeLists(listItemInFirstParagraph->parentElement(), listItemInSecondParagraph->parentElement())) {
mergeIdenticalElements(listItemInFirstParagraph->parentElement(), listItemInSecondParagraph->parentElement());
m_endingPosition = mergeDestination.deepEquivalent();
return;
}
if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfParagraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds().x()) {
if (isHTMLBRElement(*mergeDestination.deepEquivalent().downstream().deprecatedNode())) {
removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().deprecatedNode());
m_endingPosition = startOfParagraphToMove.deepEquivalent();
return;
}
}
if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) {
m_endingPosition = m_upstreamStart;
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.get())) {
CompositeEditCommand::removeNode(m_endTableRow.get());
}
}
}
void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
{
if (!m_typingStyle) {
document().frame()->selection().clearTypingStyle();
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;
document().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();
}
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;
EAffinity affinity = m_selectionToDelete.affinity();
Position downstreamEnd = m_selectionToDelete.end().downstream();
bool rootWillStayOpenWithoutPlaceholder = downstreamEnd.containerNode() == downstreamEnd.containerNode()->rootEditableElement()
|| (downstreamEnd.containerNode()->isTextNode() && downstreamEnd.containerNode()->parentNode() == downstreamEnd.containerNode()->rootEditableElement());
bool lineBreakAtEndOfSelectionToDelete = lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd());
m_needPlaceholder = !rootWillStayOpenWithoutPlaceholder
&& isStartOfParagraph(m_selectionToDelete.visibleStart(), CanCrossEditingBoundary)
&& isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditingBoundary)
&& !lineBreakAtEndOfSelectionToDelete;
if (m_needPlaceholder) {
if (Node* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart()))
if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table))
m_needPlaceholder = false;
}
initializePositionData();
bool lineBreakBeforeStart = lineBreakExistsAtVisiblePosition(VisiblePosition(m_upstreamStart).previous());
deleteInsignificantTextDownstream(m_trailingWhitespace);
saveTypingStyleState();
if (handleSpecialCaseBRDelete()) {
calculateTypingStyleAfterDelete();
setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
clearTransientState();
rebalanceWhitespace();
return;
}
handleGeneralDelete();
fixupWhitespace();
mergeParagraphs();
removePreviouslySelectedEmptyTableRows();
if (!m_needPlaceholder && rootWillStayOpenWithoutPlaceholder) {
VisiblePosition visualEnding(m_endingPosition);
bool hasPlaceholder = lineBreakExistsAtVisiblePosition(visualEnding)
&& visualEnding.next(CannotCrossEditingBoundary).isNull();
m_needPlaceholder = hasPlaceholder && lineBreakBeforeStart && !lineBreakAtEndOfSelectionToDelete;
}
RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0;
if (placeholder) {
if (m_sanitizeMarkup)
removeRedundantBlocks();
insertNodeAt(placeholder.get(), m_endingPosition);
}
rebalanceWhitespaceAt(m_endingPosition);
calculateTypingStyleAfterDelete();
setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
clearTransientState();
}
EditAction DeleteSelectionCommand::editingAction() const
{
return EditActionCut;
}
bool DeleteSelectionCommand::preservesTypingStyle() const
{
return m_typingStyle;
}
}