Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      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.
     12  *
     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.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/editing/TypingCommand.h"
     28 
     29 #include "core/HTMLNames.h"
     30 #include "core/dom/Document.h"
     31 #include "core/dom/Element.h"
     32 #include "core/dom/ElementTraversal.h"
     33 #include "core/editing/BreakBlockquoteCommand.h"
     34 #include "core/editing/Editor.h"
     35 #include "core/editing/FrameSelection.h"
     36 #include "core/editing/InsertLineBreakCommand.h"
     37 #include "core/editing/InsertParagraphSeparatorCommand.h"
     38 #include "core/editing/InsertTextCommand.h"
     39 #include "core/editing/SpellChecker.h"
     40 #include "core/editing/VisiblePosition.h"
     41 #include "core/editing/VisibleUnits.h"
     42 #include "core/editing/htmlediting.h"
     43 #include "core/frame/LocalFrame.h"
     44 #include "core/html/HTMLBRElement.h"
     45 #include "core/rendering/RenderObject.h"
     46 
     47 namespace WebCore {
     48 
     49 using namespace HTMLNames;
     50 
     51 class TypingCommandLineOperation
     52 {
     53 public:
     54     TypingCommandLineOperation(TypingCommand* typingCommand, bool selectInsertedText, const String& text)
     55     : m_typingCommand(typingCommand)
     56     , m_selectInsertedText(selectInsertedText)
     57     , m_text(text)
     58     { }
     59 
     60     void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const
     61     {
     62         if (isLastLine) {
     63             if (!lineOffset || lineLength > 0)
     64                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), m_selectInsertedText);
     65         } else {
     66             if (lineLength > 0)
     67                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), false);
     68             m_typingCommand->insertParagraphSeparator();
     69         }
     70     }
     71 
     72 private:
     73     TypingCommand* m_typingCommand;
     74     bool m_selectInsertedText;
     75     const String& m_text;
     76 };
     77 
     78 TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
     79     : TextInsertionBaseCommand(document)
     80     , m_commandType(commandType)
     81     , m_textToInsert(textToInsert)
     82     , m_openForMoreTyping(true)
     83     , m_selectInsertedText(options & SelectInsertedText)
     84     , m_smartDelete(options & SmartDelete)
     85     , m_granularity(granularity)
     86     , m_compositionType(compositionType)
     87     , m_killRing(options & KillRing)
     88     , m_openedByBackwardDelete(false)
     89     , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
     90     , m_shouldPreventSpellChecking(options & PreventSpellChecking)
     91 {
     92     updatePreservesTypingStyle(m_commandType);
     93 }
     94 
     95 void TypingCommand::deleteSelection(Document& document, Options options)
     96 {
     97     LocalFrame* frame = document.frame();
     98     ASSERT(frame);
     99 
    100     if (!frame->selection().isRange())
    101         return;
    102 
    103     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
    104         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    105         lastTypingCommand->deleteSelection(options & SmartDelete);
    106         return;
    107     }
    108 
    109     TypingCommand::create(document, DeleteSelection, "", options)->apply();
    110 }
    111 
    112 void TypingCommand::deleteKeyPressed(Document& document, Options options, TextGranularity granularity)
    113 {
    114     if (granularity == CharacterGranularity) {
    115         LocalFrame* frame = document.frame();
    116         if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
    117             // If the last typing command is not Delete, open a new typing command.
    118             // We need to group continuous delete commands alone in a single typing command.
    119             if (lastTypingCommand->commandTypeOfOpenCommand() == DeleteKey) {
    120                 updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
    121                 lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    122                 lastTypingCommand->deleteKeyPressed(granularity, options & KillRing);
    123                 return;
    124             }
    125         }
    126     }
    127 
    128     TypingCommand::create(document, DeleteKey, "", options, granularity)->apply();
    129 }
    130 
    131 void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options, TextGranularity granularity)
    132 {
    133     // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
    134     if (granularity == CharacterGranularity) {
    135         LocalFrame* frame = document.frame();
    136         if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
    137             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
    138             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    139             lastTypingCommand->forwardDeleteKeyPressed(granularity, options & KillRing);
    140             return;
    141         }
    142     }
    143 
    144     TypingCommand::create(document, ForwardDeleteKey, "", options, granularity)->apply();
    145 }
    146 
    147 void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, LocalFrame* frame)
    148 {
    149     ASSERT(frame);
    150     VisibleSelection currentSelection = frame->selection().selection();
    151     if (currentSelection == typingCommand->endingSelection())
    152         return;
    153 
    154     typingCommand->setStartingSelection(currentSelection);
    155     typingCommand->setEndingSelection(currentSelection);
    156 }
    157 
    158 void TypingCommand::insertText(Document& document, const String& text, Options options, TextCompositionType composition)
    159 {
    160     LocalFrame* frame = document.frame();
    161     ASSERT(frame);
    162 
    163     if (!text.isEmpty())
    164         document.frame()->spellChecker().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
    165 
    166     insertText(document, text, frame->selection().selection(), options, composition);
    167 }
    168 
    169 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to FrameSelection's current selection.
    170 void TypingCommand::insertText(Document& document, const String& text, const VisibleSelection& selectionForInsertion, Options options, TextCompositionType compositionType)
    171 {
    172     RefPtr<LocalFrame> frame = document.frame();
    173     ASSERT(frame);
    174 
    175     VisibleSelection currentSelection = frame->selection().selection();
    176 
    177     String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionUpdate);
    178 
    179     // Set the starting and ending selection appropriately if we are using a selection
    180     // that is different from the current selection.  In the future, we should change EditCommand
    181     // to deal with custom selections in a general way that can be used by all of the commands.
    182     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame.get())) {
    183         if (lastTypingCommand->endingSelection() != selectionForInsertion) {
    184             lastTypingCommand->setStartingSelection(selectionForInsertion);
    185             lastTypingCommand->setEndingSelection(selectionForInsertion);
    186         }
    187 
    188         lastTypingCommand->setCompositionType(compositionType);
    189         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    190         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    191         lastTypingCommand->insertText(newText, options & SelectInsertedText);
    192         return;
    193     }
    194 
    195     RefPtrWillBeRawPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
    196     applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
    197 }
    198 
    199 void TypingCommand::insertLineBreak(Document& document, Options options)
    200 {
    201     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
    202         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    203         lastTypingCommand->insertLineBreak();
    204         return;
    205     }
    206 
    207     TypingCommand::create(document, InsertLineBreak, "", options)->apply();
    208 }
    209 
    210 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
    211 {
    212     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
    213         lastTypingCommand->insertParagraphSeparatorInQuotedContent();
    214         return;
    215     }
    216 
    217     TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)->apply();
    218 }
    219 
    220 void TypingCommand::insertParagraphSeparator(Document& document, Options options)
    221 {
    222     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
    223         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    224         lastTypingCommand->insertParagraphSeparator();
    225         return;
    226     }
    227 
    228     TypingCommand::create(document, InsertParagraphSeparator, "", options)->apply();
    229 }
    230 
    231 PassRefPtrWillBeRawPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(LocalFrame* frame)
    232 {
    233     ASSERT(frame);
    234 
    235     RefPtrWillBeRawPtr<CompositeEditCommand> lastEditCommand = frame->editor().lastEditCommand();
    236     if (!lastEditCommand || !lastEditCommand->isTypingCommand() || !static_cast<TypingCommand*>(lastEditCommand.get())->isOpenForMoreTyping())
    237         return nullptr;
    238 
    239     return static_cast<TypingCommand*>(lastEditCommand.get());
    240 }
    241 
    242 void TypingCommand::closeTyping(LocalFrame* frame)
    243 {
    244     if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame))
    245         lastTypingCommand->closeTyping();
    246 }
    247 
    248 void TypingCommand::doApply()
    249 {
    250     if (!endingSelection().isNonOrphanedCaretOrRange())
    251         return;
    252 
    253     if (m_commandType == DeleteKey)
    254         if (m_commands.isEmpty())
    255             m_openedByBackwardDelete = true;
    256 
    257     switch (m_commandType) {
    258     case DeleteSelection:
    259         deleteSelection(m_smartDelete);
    260         return;
    261     case DeleteKey:
    262         deleteKeyPressed(m_granularity, m_killRing);
    263         return;
    264     case ForwardDeleteKey:
    265         forwardDeleteKeyPressed(m_granularity, m_killRing);
    266         return;
    267     case InsertLineBreak:
    268         insertLineBreak();
    269         return;
    270     case InsertParagraphSeparator:
    271         insertParagraphSeparator();
    272         return;
    273     case InsertParagraphSeparatorInQuotedContent:
    274         insertParagraphSeparatorInQuotedContent();
    275         return;
    276     case InsertText:
    277         insertText(m_textToInsert, m_selectInsertedText);
    278         return;
    279     }
    280 
    281     ASSERT_NOT_REACHED();
    282 }
    283 
    284 EditAction TypingCommand::editingAction() const
    285 {
    286     return EditActionTyping;
    287 }
    288 
    289 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
    290 {
    291     LocalFrame* frame = document().frame();
    292     if (!frame)
    293         return;
    294 
    295     if (!frame->spellChecker().isContinuousSpellCheckingEnabled())
    296         return;
    297 
    298     frame->spellChecker().cancelCheck();
    299 
    300     // Take a look at the selection that results after typing and determine whether we need to spellcheck.
    301     // Since the word containing the current selection is never marked, this does a check to
    302     // see if typing made a new word that is not in the current selection. Basically, you
    303     // get this by being at the end of a word and typing a space.
    304     VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    305     VisiblePosition previous = start.previous();
    306     if (previous.isNotNull()) {
    307         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
    308         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
    309         if (p1 != p2)
    310             frame->spellChecker().markMisspellingsAfterTypingToWord(p1, endingSelection());
    311     }
    312 }
    313 
    314 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
    315 {
    316     LocalFrame* frame = document().frame();
    317     if (!frame)
    318         return;
    319 
    320     updatePreservesTypingStyle(commandTypeForAddedTyping);
    321     updateCommandTypeOfOpenCommand(commandTypeForAddedTyping);
    322 
    323     // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
    324     markMisspellingsAfterTyping(commandTypeForAddedTyping);
    325     frame->editor().appliedEditing(this);
    326 }
    327 
    328 void TypingCommand::insertText(const String &text, bool selectInsertedText)
    329 {
    330     // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
    331     // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
    332     // an existing selection; at the moment they can either put the caret after what's inserted or
    333     // select what's inserted, but there's no way to "extend selection" to include both an old selection
    334     // that ends just before where we want to insert text and the newly inserted text.
    335     TypingCommandLineOperation operation(this, selectInsertedText, text);
    336     forEachLineInString(text, operation);
    337 }
    338 
    339 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
    340 {
    341     RefPtrWillBeRawPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
    342         m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
    343 
    344     applyCommandToComposite(command, endingSelection());
    345 
    346     typingAddedToOpenCommand(InsertText);
    347 }
    348 
    349 void TypingCommand::insertLineBreak()
    350 {
    351     if (!canAppendNewLineFeedToSelection(endingSelection()))
    352         return;
    353 
    354     applyCommandToComposite(InsertLineBreakCommand::create(document()));
    355     typingAddedToOpenCommand(InsertLineBreak);
    356 }
    357 
    358 void TypingCommand::insertParagraphSeparator()
    359 {
    360     if (!canAppendNewLineFeedToSelection(endingSelection()))
    361         return;
    362 
    363     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
    364     typingAddedToOpenCommand(InsertParagraphSeparator);
    365 }
    366 
    367 void TypingCommand::insertParagraphSeparatorInQuotedContent()
    368 {
    369     // If the selection starts inside a table, just insert the paragraph separator normally
    370     // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
    371     if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
    372         insertParagraphSeparator();
    373         return;
    374     }
    375 
    376     applyCommandToComposite(BreakBlockquoteCommand::create(document()));
    377     typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
    378 }
    379 
    380 bool TypingCommand::makeEditableRootEmpty()
    381 {
    382     Element* root = endingSelection().rootEditableElement();
    383     if (!root || !root->firstChild())
    384         return false;
    385 
    386     if (root->firstChild() == root->lastChild()) {
    387         Element* firstElementChild = ElementTraversal::firstWithin(*root);
    388         if (isHTMLBRElement(firstElementChild)) {
    389             // If there is a single child and it could be a placeholder, leave it alone.
    390             if (root->renderer() && root->renderer()->isRenderBlockFlow())
    391                 return false;
    392         }
    393     }
    394 
    395     while (Node* child = root->firstChild())
    396         removeNode(child);
    397 
    398     addBlockPlaceholderIfNeeded(root);
    399     setEndingSelection(VisibleSelection(firstPositionInNode(root), DOWNSTREAM, endingSelection().isDirectional()));
    400 
    401     return true;
    402 }
    403 
    404 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
    405 {
    406     LocalFrame* frame = document().frame();
    407     if (!frame)
    408         return;
    409 
    410     frame->spellChecker().updateMarkersForWordsAffectedByEditing(false);
    411 
    412     VisibleSelection selectionToDelete;
    413     VisibleSelection selectionAfterUndo;
    414 
    415     switch (endingSelection().selectionType()) {
    416     case RangeSelection:
    417         selectionToDelete = endingSelection();
    418         selectionAfterUndo = selectionToDelete;
    419         break;
    420     case CaretSelection: {
    421         // After breaking out of an empty mail blockquote, we still want continue with the deletion
    422         // so actual content will get deleted, and not just the quote style.
    423         if (breakOutOfEmptyMailBlockquotedParagraph())
    424             typingAddedToOpenCommand(DeleteKey);
    425 
    426         m_smartDelete = false;
    427 
    428         OwnPtrWillBeRawPtr<FrameSelection> selection = FrameSelection::create();
    429         selection->setSelection(endingSelection());
    430         selection->modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
    431         if (killRing && selection->isCaret() && granularity != CharacterGranularity)
    432             selection->modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
    433 
    434         VisiblePosition visibleStart(endingSelection().visibleStart());
    435         if (visibleStart.previous(CannotCrossEditingBoundary).isNull()) {
    436             // When the caret is at the start of the editable area in an empty list item, break out of the list item.
    437             if (breakOutOfEmptyListItem()) {
    438                 typingAddedToOpenCommand(DeleteKey);
    439                 return;
    440             }
    441             // When there are no visible positions in the editing root, delete its entire contents.
    442             if (visibleStart.next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
    443                 typingAddedToOpenCommand(DeleteKey);
    444                 return;
    445             }
    446         }
    447 
    448         // If we have a caret selection at the beginning of a cell, we have nothing to do.
    449         Node* enclosingTableCell = enclosingNodeOfType(visibleStart.deepEquivalent(), &isTableCell);
    450         if (enclosingTableCell && visibleStart == VisiblePosition(firstPositionInNode(enclosingTableCell)))
    451             return;
    452 
    453         // If the caret is at the start of a paragraph after a table, move content into the last table cell.
    454         if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(CannotCrossEditingBoundary))) {
    455             // Unless the caret is just before a table.  We don't want to move a table into the last table cell.
    456             if (isLastPositionBeforeTable(visibleStart))
    457                 return;
    458             // Extend the selection backward into the last cell, then deletion will handle the move.
    459             selection->modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
    460         // If the caret is just after a table, select the table and don't delete anything.
    461         } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
    462             setEndingSelection(VisibleSelection(positionBeforeNode(table), endingSelection().start(), DOWNSTREAM, endingSelection().isDirectional()));
    463             typingAddedToOpenCommand(DeleteKey);
    464             return;
    465         }
    466 
    467         selectionToDelete = selection->selection();
    468 
    469         if (granularity == CharacterGranularity && selectionToDelete.end().containerNode() == selectionToDelete.start().containerNode()
    470             && selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode() > 1) {
    471             // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
    472             selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
    473         }
    474 
    475         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
    476             selectionAfterUndo = selectionToDelete;
    477         else
    478             // It's a little tricky to compute what the starting selection would have been in the original document.
    479             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
    480             // the current state of the document and we'll get the wrong result.
    481             selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
    482         break;
    483     }
    484     case NoSelection:
    485         ASSERT_NOT_REACHED();
    486         break;
    487     }
    488 
    489     ASSERT(!selectionToDelete.isNone());
    490     if (selectionToDelete.isNone())
    491         return;
    492 
    493     if (selectionToDelete.isCaret())
    494         return;
    495 
    496     if (killRing)
    497         frame->editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
    498     // On Mac, make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
    499     // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
    500     // more text than you insert.  In that case all of the text that was around originally should be selected.
    501     if (frame->editor().behavior().shouldUndoOfDeleteSelectText() && m_openedByBackwardDelete)
    502         setStartingSelection(selectionAfterUndo);
    503     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
    504     setSmartDelete(false);
    505     typingAddedToOpenCommand(DeleteKey);
    506 }
    507 
    508 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
    509 {
    510     LocalFrame* frame = document().frame();
    511     if (!frame)
    512         return;
    513 
    514     frame->spellChecker().updateMarkersForWordsAffectedByEditing(false);
    515 
    516     VisibleSelection selectionToDelete;
    517     VisibleSelection selectionAfterUndo;
    518 
    519     switch (endingSelection().selectionType()) {
    520     case RangeSelection:
    521         selectionToDelete = endingSelection();
    522         selectionAfterUndo = selectionToDelete;
    523         break;
    524     case CaretSelection: {
    525         m_smartDelete = false;
    526 
    527         // Handle delete at beginning-of-block case.
    528         // Do nothing in the case that the caret is at the start of a
    529         // root editable element or at the start of a document.
    530         OwnPtrWillBeRawPtr<FrameSelection> selection = FrameSelection::create();
    531         selection->setSelection(endingSelection());
    532         selection->modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
    533         if (killRing && selection->isCaret() && granularity != CharacterGranularity)
    534             selection->modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
    535 
    536         Position downstreamEnd = endingSelection().end().downstream();
    537         VisiblePosition visibleEnd = endingSelection().visibleEnd();
    538         Node* enclosingTableCell = enclosingNodeOfType(visibleEnd.deepEquivalent(), &isTableCell);
    539         if (enclosingTableCell && visibleEnd == VisiblePosition(lastPositionInNode(enclosingTableCell)))
    540             return;
    541         if (visibleEnd == endOfParagraph(visibleEnd))
    542             downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
    543         // When deleting tables: Select the table first, then perform the deletion
    544         if (isRenderedTable(downstreamEnd.containerNode()) && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
    545             setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
    546             typingAddedToOpenCommand(ForwardDeleteKey);
    547             return;
    548         }
    549 
    550         // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
    551         if (granularity == ParagraphBoundary && selection->selection().isCaret() && isEndOfParagraph(selection->selection().visibleEnd()))
    552             selection->modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
    553 
    554         selectionToDelete = selection->selection();
    555         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
    556             selectionAfterUndo = selectionToDelete;
    557         else {
    558             // It's a little tricky to compute what the starting selection would have been in the original document.
    559             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
    560             // the current state of the document and we'll get the wrong result.
    561             Position extent = startingSelection().end();
    562             if (extent.containerNode() != selectionToDelete.end().containerNode())
    563                 extent = selectionToDelete.extent();
    564             else {
    565                 int extraCharacters;
    566                 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
    567                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
    568                 else
    569                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
    570                 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
    571             }
    572             selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
    573         }
    574         break;
    575     }
    576     case NoSelection:
    577         ASSERT_NOT_REACHED();
    578         break;
    579     }
    580 
    581     ASSERT(!selectionToDelete.isNone());
    582     if (selectionToDelete.isNone())
    583         return;
    584 
    585     if (selectionToDelete.isCaret())
    586         return;
    587 
    588     if (killRing)
    589         frame->editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
    590     // Make undo select what was deleted on Mac alone
    591     if (frame->editor().behavior().shouldUndoOfDeleteSelectText())
    592         setStartingSelection(selectionAfterUndo);
    593     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
    594     setSmartDelete(false);
    595     typingAddedToOpenCommand(ForwardDeleteKey);
    596 }
    597 
    598 void TypingCommand::deleteSelection(bool smartDelete)
    599 {
    600     CompositeEditCommand::deleteSelection(smartDelete);
    601     typingAddedToOpenCommand(DeleteSelection);
    602 }
    603 
    604 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
    605 {
    606     switch (commandType) {
    607     case DeleteSelection:
    608     case DeleteKey:
    609     case ForwardDeleteKey:
    610     case InsertParagraphSeparator:
    611     case InsertLineBreak:
    612         m_preservesTypingStyle = true;
    613         return;
    614     case InsertParagraphSeparatorInQuotedContent:
    615     case InsertText:
    616         m_preservesTypingStyle = false;
    617         return;
    618     }
    619     ASSERT_NOT_REACHED();
    620     m_preservesTypingStyle = false;
    621 }
    622 
    623 bool TypingCommand::isTypingCommand() const
    624 {
    625     return true;
    626 }
    627 
    628 } // namespace WebCore
    629