EditCommand::EditCommand(Document& document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection) : m_document(document) { ASSERT(document.frame()); setStartingSelection(startingSelection); setEndingSelection(endingSelection); }
EditCommand::EditCommand(Document& document) : m_document(&document), m_parent(nullptr) { DCHECK(m_document); DCHECK(m_document->frame()); setStartingSelection(m_document->frame()->selection().selection()); setEndingVisibleSelection(m_startingSelection); }
EditCommand::EditCommand(Document& document, EditAction editingAction) : m_document(document) , m_editingAction(editingAction) { ASSERT(document.frame()); setStartingSelection(m_document->frame()->selection().selection()); setEndingSelection(m_startingSelection); }
EditCommand::EditCommand(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection) : m_document(document) , m_parent(nullptr) { ASSERT(m_document); ASSERT(m_document->frame()); setStartingSelection(startingSelection); setEndingSelection(endingSelection); }
EditCommand::EditCommand(Document& document) : m_document(&document) , m_parent(nullptr) { ASSERT(m_document); ASSERT(m_document->frame()); setStartingSelection(m_document->frame()->selection().selection()); setEndingSelection(m_startingSelection); }
EditCommand::EditCommand(Document* document) : m_document(document) , m_parent(0) { ASSERT(m_document); ASSERT(m_document->frame()); setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), m_document->frame()->editor()->deleteButtonController()->containerElement())); setEndingSelection(m_startingSelection); }
void MoveSelectionCommand::doApply() { ASSERT(endingSelection().isNonOrphanedRange()); Position pos = m_position; if (pos.isNull()) return; // Update the position otherwise it may become invalid after the selection is deleted. Position selectionEnd = endingSelection().end(); if (pos.anchorType() == Position::PositionIsOffsetInAnchor && selectionEnd.anchorType() == Position::PositionIsOffsetInAnchor && selectionEnd.containerNode() == pos.containerNode() && selectionEnd.offsetInContainerNode() < pos.offsetInContainerNode()) { pos.moveToOffset(pos.offsetInContainerNode() - selectionEnd.offsetInContainerNode()); Position selectionStart = endingSelection().start(); if (selectionStart.anchorType() == Position::PositionIsOffsetInAnchor && selectionStart.containerNode() == pos.containerNode()) pos.moveToOffset(pos.offsetInContainerNode() + selectionStart.offsetInContainerNode()); } { auto deleteSelection = DeleteSelectionCommand::create(document(), m_smartDelete, true, false, true, true, EditActionDeleteByDrag); deleteSelection->setParent(this); deleteSelection->apply(); m_commands.append(WTFMove(deleteSelection)); } // If the node for the destination has been removed as a result of the deletion, // set the destination to the ending point after the deletion. // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectionCommand; // selection is empty, leading to null deref if (!pos.anchorNode()->inDocument()) pos = endingSelection().start(); cleanupAfterDeletion(pos); setEndingSelection(VisibleSelection(pos, endingSelection().affinity(), endingSelection().isDirectional())); setStartingSelection(endingSelection()); if (!pos.anchorNode()->inDocument()) { // Document was modified out from under us. return; } ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting; if (m_smartInsert) options |= ReplaceSelectionCommand::SmartReplace; { auto replaceSelection = ReplaceSelectionCommand::create(document(), WTFMove(m_fragment), options, EditActionInsertFromDrop); replaceSelection->setParent(this); replaceSelection->apply(); m_commands.append(WTFMove(replaceSelection)); } }
void EditCommand::setStartingSelection(const VisiblePosition& position) { setStartingSelection(VisibleSelection(position)); }
void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity) { Selection selectionToDelete; Selection selectionAfterUndo; switch (endingSelection().state()) { case Selection::RANGE: selectionToDelete = endingSelection(); selectionAfterUndo = selectionToDelete; break; case Selection::CARET: { m_smartDelete = false; // Handle delete at beginning-of-block case. // Do nothing in the case that the caret is at the start of a // root editable element or at the start of a document. SelectionController selection; selection.setSelection(endingSelection()); selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, granularity); Position downstreamEnd = endingSelection().end().downstream(); VisiblePosition visibleEnd = endingSelection().visibleEnd(); if (visibleEnd == endOfParagraph(visibleEnd)) downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream(); // When deleting tables: Select the table first, then perform the deletion if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.offset() == 0) { setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM)); typingAddedToOpenCommand(); return; } // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any) if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd())) selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity); selectionToDelete = selection.selection(); if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) selectionAfterUndo = selectionToDelete; else { // It's a little tricky to compute what the starting selection would have been in the original document. // We can't let the Selection class's validation kick in or it'll adjust for us based on // the current state of the document and we'll get the wrong result. Position extent = startingSelection().end(); if (extent.node() != selectionToDelete.end().node()) extent = selectionToDelete.extent(); else { int extraCharacters; if (selectionToDelete.start().node() == selectionToDelete.end().node()) extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset(); else extraCharacters = selectionToDelete.end().offset(); extent = Position(extent.node(), extent.offset() + extraCharacters); } selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent); } break; } case Selection::NONE: ASSERT_NOT_REACHED(); break; } if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { // make undo select what was deleted setStartingSelection(selectionAfterUndo); CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); setSmartDelete(false); typingAddedToOpenCommand(); } }
void TypingCommand::deleteKeyPressed(TextGranularity granularity) { Selection selectionToDelete; Selection selectionAfterUndo; switch (endingSelection().state()) { case Selection::RANGE: selectionToDelete = endingSelection(); selectionAfterUndo = selectionToDelete; break; case Selection::CARET: { m_smartDelete = false; SelectionController selection; selection.setSelection(endingSelection()); selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); // When the caret is at the start of the editable area in an empty list item, break out of the list item. if (endingSelection().visibleStart().previous(true).isNull()) { if (breakOutOfEmptyListItem()) { typingAddedToOpenCommand(); return; } } VisiblePosition visibleStart(endingSelection().visibleStart()); // If the caret is at the start of a paragraph after a table, move content into the last table cell. if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) { // Unless the caret is just before a table. We don't want to move a table into the last table cell. if (isLastPositionBeforeTable(visibleStart)) return; // Extend the selection backward into the last cell, then deletion will handle the move. selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); // If the caret is just after a table, select the table and don't delete anything. } else if (Node* table = isFirstPositionAfterTable(visibleStart)) { setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM)); typingAddedToOpenCommand(); return; } selectionToDelete = selection.selection(); if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) selectionAfterUndo = selectionToDelete; else // It's a little tricky to compute what the starting selection would have been in the original document. // We can't let the Selection class's validation kick in or it'll adjust for us based on // the current state of the document and we'll get the wrong result. selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent()); break; } case Selection::NONE: ASSERT_NOT_REACHED(); break; } if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion. // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete // more text than you insert. In that case all of the text that was around originally should be selected. if (m_openedByBackwardDelete) setStartingSelection(selectionAfterUndo); CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); setSmartDelete(false); typingAddedToOpenCommand(); } }