2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "TypingCommand.h"
29 #include "BeforeTextInsertedEvent.h"
30 #include "BreakBlockquoteCommand.h"
31 #include "DeleteSelectionCommand.h"
36 #include "FrameSelection.h"
37 #include "HTMLNames.h"
38 #include "InsertLineBreakCommand.h"
39 #include "InsertParagraphSeparatorCommand.h"
40 #include "InsertTextCommand.h"
41 #include "RenderObject.h"
42 #include "TextIterator.h"
43 #include "VisiblePosition.h"
44 #include "htmlediting.h"
45 #include "visible_units.h"
49 using namespace HTMLNames;
51 static bool canAppendNewLineFeed(const VisibleSelection& selection)
53 Node* node = selection.rootEditableElement();
57 RefPtr<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(String("\n"));
59 node->dispatchEvent(event, ec);
60 return event->text().length();
63 TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
64 : CompositeEditCommand(document)
65 , m_commandType(commandType)
66 , m_textToInsert(textToInsert)
67 , m_openForMoreTyping(true)
68 , m_selectInsertedText(options & SelectInsertedText)
69 , m_smartDelete(options & SmartDelete)
70 , m_granularity(granularity)
71 , m_compositionType(compositionType)
72 , m_killRing(options & KillRing)
73 , m_openedByBackwardDelete(false)
74 , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
75 , m_shouldPreventSpellChecking(options & PreventSpellChecking)
77 updatePreservesTypingStyle(m_commandType);
80 void TypingCommand::deleteSelection(Document* document, Options options)
84 Frame* frame = document->frame();
87 if (!frame->selection()->isRange())
90 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
91 if (isOpenForMoreTypingCommand(lastEditCommand)) {
92 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand);
93 lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
94 lastTypingCommand->deleteSelection(options & SmartDelete);
98 TypingCommand::create(document, DeleteSelection, "", options)->apply();
101 void TypingCommand::deleteKeyPressed(Document *document, Options options, TextGranularity granularity)
105 Frame* frame = document->frame();
108 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
109 if (granularity == CharacterGranularity && isOpenForMoreTypingCommand(lastEditCommand)) {
110 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand);
111 updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand, frame);
112 lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
113 lastTypingCommand->deleteKeyPressed(granularity, options & KillRing);
117 TypingCommand::create(document, DeleteKey, "", options, granularity)->apply();
120 void TypingCommand::forwardDeleteKeyPressed(Document *document, Options options, TextGranularity granularity)
122 // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
125 Frame* frame = document->frame();
128 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
129 if (granularity == CharacterGranularity && isOpenForMoreTypingCommand(lastEditCommand)) {
130 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand);
131 updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand, frame);
132 lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
133 lastTypingCommand->forwardDeleteKeyPressed(granularity, options & KillRing);
137 TypingCommand::create(document, ForwardDeleteKey, "", options, granularity)->apply();
140 void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
143 VisibleSelection currentSelection = frame->selection()->selection();
144 if (currentSelection == typingCommand->endingSelection())
147 typingCommand->setStartingSelection(currentSelection);
148 typingCommand->setEndingSelection(currentSelection);
151 void TypingCommand::insertText(Document* document, const String& text, Options options, TextCompositionType composition)
155 Frame* frame = document->frame();
159 document->frame()->editor()->updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text.characters()[0]));
161 insertText(document, text, frame->selection()->selection(), options, composition);
164 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to FrameSelection's current selection.
165 void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, Options options, TextCompositionType compositionType)
169 RefPtr<Frame> frame = document->frame();
172 VisibleSelection currentSelection = frame->selection()->selection();
173 bool changeSelection = currentSelection != selectionForInsertion;
174 String newText = text;
175 if (Node* startNode = selectionForInsertion.start().containerNode()) {
176 if (startNode->rootEditableElement() && compositionType != TextCompositionUpdate) {
177 // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
178 ExceptionCode ec = 0;
179 RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
180 startNode->rootEditableElement()->dispatchEvent(evt, ec);
181 newText = evt->text();
185 // Set the starting and ending selection appropriately if we are using a selection
186 // that is different from the current selection. In the future, we should change EditCommand
187 // to deal with custom selections in a general way that can be used by all of the commands.
188 RefPtr<EditCommand> lastEditCommand = frame->editor()->lastEditCommand();
189 if (isOpenForMoreTypingCommand(lastEditCommand.get())) {
190 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand.get());
191 if (lastTypingCommand->endingSelection() != selectionForInsertion) {
192 lastTypingCommand->setStartingSelection(selectionForInsertion);
193 lastTypingCommand->setEndingSelection(selectionForInsertion);
196 lastTypingCommand->setCompositionType(compositionType);
197 lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
198 lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
199 lastTypingCommand->insertText(newText, options & SelectInsertedText);
203 RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
204 if (changeSelection) {
205 cmd->setStartingSelection(selectionForInsertion);
206 cmd->setEndingSelection(selectionForInsertion);
209 if (changeSelection) {
210 cmd->setEndingSelection(currentSelection);
211 frame->selection()->setSelection(currentSelection);
215 void TypingCommand::insertLineBreak(Document *document, Options options)
219 Frame* frame = document->frame();
222 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
223 if (isOpenForMoreTypingCommand(lastEditCommand)) {
224 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand);
225 lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
226 lastTypingCommand->insertLineBreak();
230 applyCommand(TypingCommand::create(document, InsertLineBreak, "", options));
233 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document)
237 Frame* frame = document->frame();
240 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
241 if (isOpenForMoreTypingCommand(lastEditCommand)) {
242 static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparatorInQuotedContent();
246 applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent));
249 void TypingCommand::insertParagraphSeparator(Document *document, Options options)
253 Frame* frame = document->frame();
256 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
257 if (isOpenForMoreTypingCommand(lastEditCommand)) {
258 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand);
259 lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
260 lastTypingCommand->insertParagraphSeparator();
264 applyCommand(TypingCommand::create(document, InsertParagraphSeparator, "", options));
267 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd)
269 return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping();
272 void TypingCommand::closeTyping(EditCommand* cmd)
274 if (isOpenForMoreTypingCommand(cmd))
275 static_cast<TypingCommand*>(cmd)->closeTyping();
278 void TypingCommand::doApply()
280 if (!endingSelection().isNonOrphanedCaretOrRange())
283 if (m_commandType == DeleteKey)
284 if (m_commands.isEmpty())
285 m_openedByBackwardDelete = true;
287 switch (m_commandType) {
288 case DeleteSelection:
289 deleteSelection(m_smartDelete);
292 deleteKeyPressed(m_granularity, m_killRing);
294 case ForwardDeleteKey:
295 forwardDeleteKeyPressed(m_granularity, m_killRing);
297 case InsertLineBreak:
300 case InsertParagraphSeparator:
301 insertParagraphSeparator();
303 case InsertParagraphSeparatorInQuotedContent:
304 insertParagraphSeparatorInQuotedContent();
307 insertText(m_textToInsert, m_selectInsertedText);
311 ASSERT_NOT_REACHED();
314 EditAction TypingCommand::editingAction() const
316 return EditActionTyping;
319 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
321 #if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD)
322 if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()
323 && !document()->frame()->editor()->isAutomaticQuoteSubstitutionEnabled()
324 && !document()->frame()->editor()->isAutomaticLinkDetectionEnabled()
325 && !document()->frame()->editor()->isAutomaticDashSubstitutionEnabled()
326 && !document()->frame()->editor()->isAutomaticTextReplacementEnabled())
329 if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
332 // Take a look at the selection that results after typing and determine whether we need to spellcheck.
333 // Since the word containing the current selection is never marked, this does a check to
334 // see if typing made a new word that is not in the current selection. Basically, you
335 // get this by being at the end of a word and typing a space.
336 VisiblePosition start(endingSelection().start(), endingSelection().affinity());
337 VisiblePosition previous = start.previous();
338 if (previous.isNotNull()) {
339 VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
340 VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
342 RefPtr<Range> range = makeRange(p1, p2);
343 String strippedPreviousWord;
344 if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
345 strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
346 document()->frame()->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
347 } else if (commandType == TypingCommand::InsertText)
348 document()->frame()->editor()->startCorrectionPanelTimer();
352 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
354 updatePreservesTypingStyle(commandTypeForAddedTyping);
356 #if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD)
357 document()->frame()->editor()->appliedEditing(this);
358 // Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
359 if (!m_shouldPreventSpellChecking)
360 markMisspellingsAfterTyping(commandTypeForAddedTyping);
362 // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
363 markMisspellingsAfterTyping(commandTypeForAddedTyping);
364 document()->frame()->editor()->appliedEditing(this);
368 void TypingCommand::insertText(const String &text, bool selectInsertedText)
370 // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
371 // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
372 // an existing selection; at the moment they can either put the caret after what's inserted or
373 // select what's inserted, but there's no way to "extend selection" to include both an old selection
374 // that ends just before where we want to insert text and the newly inserted text.
377 while ((newline = text.find('\n', offset)) != notFound) {
378 if (newline != offset)
379 insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false);
380 insertParagraphSeparator();
381 offset = newline + 1;
384 insertTextRunWithoutNewlines(text, selectInsertedText);
386 unsigned length = text.length();
387 if (length != offset)
388 insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText);
392 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
394 RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
395 m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
397 applyCommandToComposite(command, endingSelection());
399 typingAddedToOpenCommand(InsertText);
402 void TypingCommand::insertLineBreak()
404 if (!canAppendNewLineFeed(endingSelection()))
407 applyCommandToComposite(InsertLineBreakCommand::create(document()));
408 typingAddedToOpenCommand(InsertLineBreak);
411 void TypingCommand::insertParagraphSeparator()
413 if (!canAppendNewLineFeed(endingSelection()))
416 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
417 typingAddedToOpenCommand(InsertParagraphSeparator);
420 void TypingCommand::insertParagraphSeparatorInQuotedContent()
422 // If the selection starts inside a table, just insert the paragraph separator normally
423 // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
424 if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
425 insertParagraphSeparator();
429 applyCommandToComposite(BreakBlockquoteCommand::create(document()));
430 typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
433 bool TypingCommand::makeEditableRootEmpty()
435 Element* root = endingSelection().rootEditableElement();
436 if (!root || !root->firstChild())
439 if (root->firstChild() == root->lastChild() && root->firstElementChild() && root->firstElementChild()->hasTagName(brTag)) {
440 // If there is a single child and it could be a placeholder, leave it alone.
441 if (root->renderer() && root->renderer()->isBlockFlow())
445 while (Node* child = root->firstChild())
448 addBlockPlaceholderIfNeeded(root);
449 setEndingSelection(VisibleSelection(firstPositionInNode(root), DOWNSTREAM, endingSelection().isDirectional()));
454 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
456 document()->frame()->editor()->updateMarkersForWordsAffectedByEditing(false);
458 VisibleSelection selectionToDelete;
459 VisibleSelection selectionAfterUndo;
461 switch (endingSelection().selectionType()) {
462 case VisibleSelection::RangeSelection:
463 selectionToDelete = endingSelection();
464 selectionAfterUndo = selectionToDelete;
466 case VisibleSelection::CaretSelection: {
467 // After breaking out of an empty mail blockquote, we still want continue with the deletion
468 // so actual content will get deleted, and not just the quote style.
469 if (breakOutOfEmptyMailBlockquotedParagraph())
470 typingAddedToOpenCommand(DeleteKey);
472 m_smartDelete = false;
474 FrameSelection selection;
475 selection.setSelection(endingSelection());
476 selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
477 if (killRing && selection.isCaret() && granularity != CharacterGranularity)
478 selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
480 if (endingSelection().visibleStart().previous(CannotCrossEditingBoundary).isNull()) {
481 // When the caret is at the start of the editable area in an empty list item, break out of the list item.
482 if (breakOutOfEmptyListItem()) {
483 typingAddedToOpenCommand(DeleteKey);
486 // When there are no visible positions in the editing root, delete its entire contents.
487 if (endingSelection().visibleStart().next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
488 typingAddedToOpenCommand(DeleteKey);
493 VisiblePosition visibleStart(endingSelection().visibleStart());
494 // If we have a caret selection on an empty cell, we have nothing to do.
495 if (isEmptyTableCell(visibleStart.deepEquivalent().containerNode()))
498 // If the caret is at the start of a paragraph after a table, move content into the last table cell.
499 if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(CannotCrossEditingBoundary))) {
500 // Unless the caret is just before a table. We don't want to move a table into the last table cell.
501 if (isLastPositionBeforeTable(visibleStart))
503 // Extend the selection backward into the last cell, then deletion will handle the move.
504 selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
505 // If the caret is just after a table, select the table and don't delete anything.
506 } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
507 setEndingSelection(VisibleSelection(positionBeforeNode(table), endingSelection().start(), DOWNSTREAM, endingSelection().isDirectional()));
508 typingAddedToOpenCommand(DeleteKey);
512 selectionToDelete = selection.selection();
514 if (granularity == CharacterGranularity && selectionToDelete.end().containerNode() == selectionToDelete.start().containerNode()
515 && selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode() > 1) {
516 // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
517 selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
520 if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
521 selectionAfterUndo = selectionToDelete;
523 // It's a little tricky to compute what the starting selection would have been in the original document.
524 // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
525 // the current state of the document and we'll get the wrong result.
526 selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
529 case VisibleSelection::NoSelection:
530 ASSERT_NOT_REACHED();
534 ASSERT(!selectionToDelete.isNone());
535 if (selectionToDelete.isNone())
538 if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
542 document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
543 // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
544 // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
545 // more text than you insert. In that case all of the text that was around originally should be selected.
546 if (m_openedByBackwardDelete)
547 setStartingSelection(selectionAfterUndo);
548 CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
549 setSmartDelete(false);
550 typingAddedToOpenCommand(DeleteKey);
553 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
555 document()->frame()->editor()->updateMarkersForWordsAffectedByEditing(false);
557 VisibleSelection selectionToDelete;
558 VisibleSelection selectionAfterUndo;
560 switch (endingSelection().selectionType()) {
561 case VisibleSelection::RangeSelection:
562 selectionToDelete = endingSelection();
563 selectionAfterUndo = selectionToDelete;
565 case VisibleSelection::CaretSelection: {
566 m_smartDelete = false;
568 // Handle delete at beginning-of-block case.
569 // Do nothing in the case that the caret is at the start of a
570 // root editable element or at the start of a document.
571 FrameSelection selection;
572 selection.setSelection(endingSelection());
573 selection.modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
574 if (killRing && selection.isCaret() && granularity != CharacterGranularity)
575 selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
577 Position downstreamEnd = endingSelection().end().downstream();
578 VisiblePosition visibleEnd = endingSelection().visibleEnd();
579 if (visibleEnd == endOfParagraph(visibleEnd))
580 downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
581 // When deleting tables: Select the table first, then perform the deletion
582 if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
583 && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
584 setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
585 typingAddedToOpenCommand(ForwardDeleteKey);
589 // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
590 if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
591 selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
593 selectionToDelete = selection.selection();
594 if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
595 selectionAfterUndo = selectionToDelete;
597 // It's a little tricky to compute what the starting selection would have been in the original document.
598 // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
599 // the current state of the document and we'll get the wrong result.
600 Position extent = startingSelection().end();
601 if (extent.containerNode() != selectionToDelete.end().containerNode())
602 extent = selectionToDelete.extent();
605 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
606 extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
608 extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
609 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
611 selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
615 case VisibleSelection::NoSelection:
616 ASSERT_NOT_REACHED();
620 ASSERT(!selectionToDelete.isNone());
621 if (selectionToDelete.isNone())
624 if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
628 document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
629 // make undo select what was deleted
630 setStartingSelection(selectionAfterUndo);
631 CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
632 setSmartDelete(false);
633 typingAddedToOpenCommand(ForwardDeleteKey);
636 void TypingCommand::deleteSelection(bool smartDelete)
638 CompositeEditCommand::deleteSelection(smartDelete);
639 typingAddedToOpenCommand(DeleteSelection);
642 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
644 switch (commandType) {
645 case DeleteSelection:
647 case ForwardDeleteKey:
648 case InsertParagraphSeparator:
649 case InsertLineBreak:
650 m_preservesTypingStyle = true;
652 case InsertParagraphSeparatorInQuotedContent:
654 m_preservesTypingStyle = false;
657 ASSERT_NOT_REACHED();
658 m_preservesTypingStyle = false;
661 bool TypingCommand::isTypingCommand() const
666 } // namespace WebCore