DeleteSelectionCommand.cpp [plain text]
#include "config.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "DocumentMarkerController.h"
#include "EditingBoundary.h"
#include "Editor.h"
#include "EditorClient.h"
#include "Element.h"
#include "Frame.h"
#include "htmlediting.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "RenderTableCell.h"
#include "Text.h"
#include "visible_units.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)
: CompositeEditCommand(document)
, 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_startBlock(0)
, m_endBlock(0)
, m_typingStyle(0)
, m_deleteIntoBlockquoteStyle(0)
{
}
DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
: CompositeEditCommand(selection.start().anchorNode()->document())
, 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_selectionToDelete(selection)
, m_startBlock(0)
, m_endBlock(0)
, m_typingStyle(0)
, m_deleteIntoBlockquoteStyle(0)
{
}
void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
{
Node* startSpecialContainer = 0;
Node* endSpecialContainer = 0;
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 = 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)
{
VisiblePosition newBase;
VisiblePosition newExtent;
if (startingSelection().isBaseFirst()) {
newBase = start;
newExtent = end;
} else {
newBase = end;
newExtent = start;
}
setStartingSelection(VisibleSelection(newBase, newExtent));
}
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, &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())
return;
m_typingStyle = EditingStyle::create(m_selectionToDelete.start());
m_typingStyle->removeStyleAddedByNode(enclosingAnchorElement(m_selectionToDelete.start()));
if (enclosingNodeOfType(m_selectionToDelete.start(), isMailBlockquote))
m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end());
else
m_deleteIntoBlockquoteStyle = 0;
}
bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
{
bool upstreamStartIsBR = m_upstreamStart.deprecatedNode()->hasTagName(brTag);
bool downstreamStartIsBR = m_downstreamStart.deprecatedNode()->hasTagName(brTag);
bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.deprecatedNode() == m_upstreamEnd.deprecatedNode();
if (isBROnLineByItself) {
removeNode(m_downstreamStart.deprecatedNode());
return true;
}
if (upstreamStartIsBR && downstreamStartIsBR) {
m_startsAtEmptyLine = true;
m_endingPosition = m_downstreamEnd;
}
return false;
}
static void updatePositionForNodeRemoval(Node* node, Position& position)
{
if (position.isNull())
return;
switch (position.anchorType()) {
case Position::PositionIsOffsetInAnchor:
if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
position.moveToOffset(position.offsetInContainerNode() - 1);
else if (node->contains(position.containerNode()))
position = positionInParentBeforeNode(node);
break;
case Position::PositionIsAfterAnchor:
if (node->contains(position.anchorNode()))
position = positionInParentAfterNode(node);
break;
case Position::PositionIsBeforeAnchor:
if (node->contains(position.anchorNode()))
position = positionInParentBeforeNode(node);
break;
}
}
static Position firstEditablePositionInNode(Node* node)
{
ASSERT(node);
Node* next = node;
while (next && !next->rendererIsEditable())
next = next->traverseNextNode(node);
return next ? firstPositionInOrBeforeNode(next) : Position();
}
void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
{
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());
if (nextChild && nextChild->parentNode() != node)
return;
child = nextChild;
}
return;
}
}
if (isTableStructureNode(node.get()) || node == node->rootEditableElement()) {
Node* child = node->firstChild();
while (child) {
Node* remove = child;
child = child->nextSibling();
removeNode(remove);
}
updateLayout();
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 && !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(node.get(), m_endingPosition);
updatePositionForNodeRemoval(node.get(), m_leadingWhitespace);
updatePositionForNodeRemoval(node.get(), m_trailingWhitespace);
CompositeEditCommand::removeNode(node);
}
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::handleGeneralDelete()
{
int startOffset = m_upstreamStart.deprecatedEditingOffset();
Node* startNode = m_upstreamStart.deprecatedNode();
if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !startNode->hasTagName(tableTag)) {
startOffset = 0;
startNode = startNode->traverseNextNode();
}
if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
Text *text = static_cast<Text *>(startNode);
if (text->length() > (unsigned)caretMaxOffset(startNode))
deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
}
if (startOffset >= lastOffsetForEditing(startNode)) {
startNode = startNode->traverseNextSibling();
startOffset = 0;
}
if (!startNode)
return;
if (startNode == m_downstreamEnd.deprecatedNode()) {
if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
if (startNode->isTextNode()) {
Text* text = static_cast<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 (startNode->isTextNode()) {
Text *text = static_cast<Text *>(node.get());
deleteTextFromNode(text, startOffset, text->length() - startOffset);
node = node->traverseNextNode();
} else {
node = startNode->childNode(startOffset);
}
} else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) {
Text* text = static_cast<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 = 0;
} else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.get())) {
RefPtr<Node> nextNode = node->traverseNextSibling();
if (node->parentNode() == m_downstreamEnd.deprecatedNode()) {
ASSERT(m_downstreamEnd.deprecatedEditingOffset());
ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.deprecatedEditingOffset());
m_downstreamEnd.moveToOffset(m_downstreamEnd.deprecatedEditingOffset() - 1);
}
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 = 0;
} else
node = node->traverseNextNode();
}
}
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 (m_downstreamEnd.deprecatedNode()->isTextNode()) {
Text* text = static_cast<Text*>(m_downstreamEnd.deprecatedNode());
if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset());
}
} else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->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.moveToOffset(offset);
}
}
}
}
}
void DeleteSelectionCommand::fixupWhitespace()
{
updateLayout();
if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = static_cast<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() && m_trailingWhitespace.deprecatedNode()->isTextNode()) {
Text* textNode = static_cast<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 = static_cast<Element*>(enclosingBlock(m_downstreamEnd.deprecatedNode()));
if (!startOfParagraphToMove.deepEquivalent().deprecatedNode() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode())) {
removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode()));
return;
}
if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination.deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStart.containerNode())) || m_startsAtEmptyLine) {
insertNodeAt(createBreakElement(document()).get(), m_upstreamStart);
mergeDestination = VisiblePosition(m_upstreamStart);
}
if (mergeDestination == startOfParagraphToMove)
return;
VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove);
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 (!document()->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.get())) {
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 = 0;
m_typingStyle->prepareToApplyAt(m_endingPosition);
if (m_typingStyle->isEmpty())
m_typingStyle = 0;
VisiblePosition visibleEnd(m_endingPosition);
if (m_typingStyle &&
isStartOfParagraph(visibleEnd) &&
isEndOfParagraph(visibleEnd) &&
lineBreakExistsAtVisiblePosition(visibleEnd)) {
setEndingSelection(visibleEnd);
applyStyle(m_typingStyle.get(), EditActionUnspecified);
m_endingPosition = endingSelection().start();
m_typingStyle = 0;
}
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();
}
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<DocumentMarker> markers = document()->markers()->markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
for (size_t i = 0; i < markers.size(); ++i) {
const DocumentMarker& marker = markers[i];
int startOffset = marker.startOffset;
if (startOffset == startOfSelection.deepEquivalent().offsetInContainerNode())
return marker.description;
}
return String();
}
void DeleteSelectionCommand::doApply()
{
if (!m_hasSelectionToDelete)
m_selectionToDelete = endingSelection();
if (!m_selectionToDelete.isNonOrphanedRange())
return;
String originalString = originalStringForAutocorrectionAtBeginningOfSelection();
if (!m_replace) {
Node* startNode = m_selectionToDelete.start().deprecatedNode();
Node* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0;
if (ancestorNode && ancestorNode->hasTagName(inputTag)
&& static_cast<HTMLInputElement*>(ancestorNode)->isTextField()
&& ancestorNode->focused())
document()->frame()->editor()->textWillBeDeletedInTextField(static_cast<Element*>(ancestorNode));
}
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 (Node* 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));
clearTransientState();
rebalanceWhitespace();
return;
}
handleGeneralDelete();
fixupWhitespace();
mergeParagraphs();
removePreviouslySelectedEmptyTableRows();
RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0;
if (placeholder)
insertNodeAt(placeholder.get(), m_endingPosition);
bool isSecure = NO;
Node* node = m_endingPosition.deprecatedNode();
if (node && node->isTextNode()) {
Text* textNode = static_cast<Text*>(node);
if (textNode->length() > 0) {
RenderObject* renderer = textNode->renderer();
isSecure = renderer->style()->textSecurity() != TSNONE;
}
}
if (!isSecure)
rebalanceWhitespaceAt(m_endingPosition);
calculateTypingStyleAfterDelete();
if (!originalString.isEmpty()) {
if (Frame* frame = document()->frame())
frame->editor()->deletedAutocorrectionAtPosition(m_endingPosition, originalString);
}
setEndingSelection(VisibleSelection(m_endingPosition, affinity));
clearTransientState();
}
EditAction DeleteSelectionCommand::editingAction() const
{
return EditActionCut;
}
bool DeleteSelectionCommand::preservesTypingStyle() const
{
return m_typingStyle;
}
}