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