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 "HTMLNames.h"
     30 #include "core/dom/Document.h"
     31 #include "core/dom/Element.h"
     32 #include "core/editing/BreakBlockquoteCommand.h"
     33 #include "core/editing/Editor.h"
     34 #include "core/editing/FrameSelection.h"
     35 #include "core/editing/InsertLineBreakCommand.h"
     36 #include "core/editing/InsertParagraphSeparatorCommand.h"
     37 #include "core/editing/InsertTextCommand.h"
     38 #include "core/editing/SpellChecker.h"
     39 #include "core/editing/VisiblePosition.h"
     40 #include "core/editing/VisibleUnits.h"
     41 #include "core/editing/htmlediting.h"
     42 #include "core/page/Frame.h"
     43 #include "core/rendering/RenderObject.h"
     44 
     45 namespace WebCore {
     46 
     47 using namespace HTMLNames;
     48 
     49 class TypingCommandLineOperation
     50 {
     51 public:
     52     TypingCommandLineOperation(TypingCommand* typingCommand, bool selectInsertedText, const String& text)
     53     : m_typingCommand(typingCommand)
     54     , m_selectInsertedText(selectInsertedText)
     55     , m_text(text)
     56     { }
     57 
     58     void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const
     59     {
     60         if (isLastLine) {
     61             if (!lineOffset || lineLength > 0)
     62                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), m_selectInsertedText);
     63         } else {
     64             if (lineLength > 0)
     65                 m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), false);
     66             m_typingCommand->insertParagraphSeparator();
     67         }
     68     }
     69 
     70 private:
     71     TypingCommand* m_typingCommand;
     72     bool m_selectInsertedText;
     73     const String& m_text;
     74 };
     75 
     76 TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
     77     : TextInsertionBaseCommand(document)
     78     , m_commandType(commandType)
     79     , m_textToInsert(textToInsert)
     80     , m_openForMoreTyping(true)
     81     , m_selectInsertedText(options & SelectInsertedText)
     82     , m_smartDelete(options & SmartDelete)
     83     , m_granularity(granularity)
     84     , m_compositionType(compositionType)
     85     , m_killRing(options & KillRing)
     86     , m_openedByBackwardDelete(false)
     87     , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
     88     , m_shouldPreventSpellChecking(options & PreventSpellChecking)
     89 {
     90     updatePreservesTypingStyle(m_commandType);
     91 }
     92 
     93 void TypingCommand::deleteSelection(Document* document, Options options)
     94 {
     95     ASSERT(document);
     96 
     97     Frame* frame = document->frame();
     98     ASSERT(frame);
     99 
    100     if (!frame->selection()->isRange())
    101         return;
    102 
    103     if (RefPtr<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     ASSERT(document);
    115     if (granularity == CharacterGranularity) {
    116         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document->frame())) {
    117             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), document->frame());
    118             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    119             lastTypingCommand->deleteKeyPressed(granularity, options & KillRing);
    120             return;
    121         }
    122     }
    123 
    124     TypingCommand::create(document, DeleteKey, "", options, granularity)->apply();
    125 }
    126 
    127 void TypingCommand::forwardDeleteKeyPressed(Document *document, Options options, TextGranularity granularity)
    128 {
    129     // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
    130     ASSERT(document);
    131     Frame* frame = document->frame();
    132     if (granularity == CharacterGranularity) {
    133         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
    134             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
    135             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    136             lastTypingCommand->forwardDeleteKeyPressed(granularity, options & KillRing);
    137             return;
    138         }
    139     }
    140 
    141     TypingCommand::create(document, ForwardDeleteKey, "", options, granularity)->apply();
    142 }
    143 
    144 void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
    145 {
    146     ASSERT(frame);
    147     VisibleSelection currentSelection = frame->selection()->selection();
    148     if (currentSelection == typingCommand->endingSelection())
    149         return;
    150 
    151     typingCommand->setStartingSelection(currentSelection);
    152     typingCommand->setEndingSelection(currentSelection);
    153 }
    154 
    155 void TypingCommand::insertText(Document* document, const String& text, Options options, TextCompositionType composition)
    156 {
    157     ASSERT(document);
    158 
    159     Frame* frame = document->frame();
    160     ASSERT(frame);
    161 
    162     if (!text.isEmpty())
    163         document->frame()->editor()->updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
    164 
    165     insertText(document, text, frame->selection()->selection(), options, composition);
    166 }
    167 
    168 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to FrameSelection's current selection.
    169 void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, Options options, TextCompositionType compositionType)
    170 {
    171     ASSERT(document);
    172 
    173     RefPtr<Frame> frame = document->frame();
    174     ASSERT(frame);
    175 
    176     VisibleSelection currentSelection = frame->selection()->selection();
    177 
    178     String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionUpdate);
    179 
    180     // Set the starting and ending selection appropriately if we are using a selection
    181     // that is different from the current selection.  In the future, we should change EditCommand
    182     // to deal with custom selections in a general way that can be used by all of the commands.
    183     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame.get())) {
    184         if (lastTypingCommand->endingSelection() != selectionForInsertion) {
    185             lastTypingCommand->setStartingSelection(selectionForInsertion);
    186             lastTypingCommand->setEndingSelection(selectionForInsertion);
    187         }
    188 
    189         lastTypingCommand->setCompositionType(compositionType);
    190         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    191         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
    192         lastTypingCommand->insertText(newText, options & SelectInsertedText);
    193         return;
    194     }
    195 
    196     RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
    197     applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
    198 }
    199 
    200 void TypingCommand::insertLineBreak(Document *document, Options options)
    201 {
    202     ASSERT(document);
    203     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document->frame())) {
    204         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    205         lastTypingCommand->insertLineBreak();
    206         return;
    207     }
    208 
    209     applyCommand(TypingCommand::create(document, InsertLineBreak, "", options));
    210 }
    211 
    212 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document)
    213 {
    214     ASSERT(document);
    215     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document->frame())) {
    216         lastTypingCommand->insertParagraphSeparatorInQuotedContent();
    217         return;
    218     }
    219 
    220     applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent));
    221 }
    222 
    223 void TypingCommand::insertParagraphSeparator(Document *document, Options options)
    224 {
    225     ASSERT(document);
    226     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document->frame())) {
    227         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
    228         lastTypingCommand->insertParagraphSeparator();
    229         return;
    230     }
    231 
    232     applyCommand(TypingCommand::create(document, InsertParagraphSeparator, "", options));
    233 }
    234 
    235 PassRefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame* frame)
    236 {
    237     ASSERT(frame);
    238 
    239     RefPtr<CompositeEditCommand> lastEditCommand = frame->editor()->lastEditCommand();
    240     if (!lastEditCommand || !lastEditCommand->isTypingCommand() || !static_cast<TypingCommand*>(lastEditCommand.get())->isOpenForMoreTyping())
    241         return 0;
    242 
    243     return static_cast<TypingCommand*>(lastEditCommand.get());
    244 }
    245 
    246 void TypingCommand::closeTyping(Frame* frame)
    247 {
    248     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame))
    249         lastTypingCommand->closeTyping();
    250 }
    251 
    252 void TypingCommand::doApply()
    253 {
    254     if (!endingSelection().isNonOrphanedCaretOrRange())
    255         return;
    256 
    257     if (m_commandType == DeleteKey)
    258         if (m_commands.isEmpty())
    259             m_openedByBackwardDelete = true;
    260 
    261     switch (m_commandType) {
    262     case DeleteSelection:
    263         deleteSelection(m_smartDelete);
    264         return;
    265     case DeleteKey:
    266         deleteKeyPressed(m_granularity, m_killRing);
    267         return;
    268     case ForwardDeleteKey:
    269         forwardDeleteKeyPressed(m_granularity, m_killRing);
    270         return;
    271     case InsertLineBreak:
    272         insertLineBreak();
    273         return;
    274     case InsertParagraphSeparator:
    275         insertParagraphSeparator();
    276         return;
    277     case InsertParagraphSeparatorInQuotedContent:
    278         insertParagraphSeparatorInQuotedContent();
    279         return;
    280     case InsertText:
    281         insertText(m_textToInsert, m_selectInsertedText);
    282         return;
    283     }
    284 
    285     ASSERT_NOT_REACHED();
    286 }
    287 
    288 EditAction TypingCommand::editingAction() const
    289 {
    290     return EditActionTyping;
    291 }
    292 
    293 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
    294 {
    295     Frame* frame = document()->frame();
    296     if (!frame)
    297         return;
    298 
    299     if (!frame->editor()->isContinuousSpellCheckingEnabled())
    300         return;
    301 
    302     frame->editor()->spellChecker()->cancelCheck();
    303 
    304     // Take a look at the selection that results after typing and determine whether we need to spellcheck.
    305     // Since the word containing the current selection is never marked, this does a check to
    306     // see if typing made a new word that is not in the current selection. Basically, you
    307     // get this by being at the end of a word and typing a space.
    308     VisiblePosition start(endingSelection().start(), endingSelection().affinity());
    309     VisiblePosition previous = start.previous();
    310     if (previous.isNotNull()) {
    311         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
    312         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
    313         if (p1 != p2)
    314             frame->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection());
    315     }
    316 }
    317 
    318 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
    319 {
    320     Frame* frame = document()->frame();
    321     if (!frame)
    322         return;
    323 
    324     updatePreservesTypingStyle(commandTypeForAddedTyping);
    325 
    326     // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
    327     markMisspellingsAfterTyping(commandTypeForAddedTyping);
    328     frame->editor()->appliedEditing(this);
    329 }
    330 
    331 void TypingCommand::insertText(const String &text, bool selectInsertedText)
    332 {
    333     // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
    334     // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
    335     // an existing selection; at the moment they can either put the caret after what's inserted or
    336     // select what's inserted, but there's no way to "extend selection" to include both an old selection
    337     // that ends just before where we want to insert text and the newly inserted text.
    338     TypingCommandLineOperation operation(this, selectInsertedText, text);
    339     forEachLineInString(text, operation);
    340 }
    341 
    342 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
    343 {
    344     RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
    345         m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
    346 
    347     applyCommandToComposite(command, endingSelection());
    348 
    349     typingAddedToOpenCommand(InsertText);
    350 }
    351 
    352 void TypingCommand::insertLineBreak()
    353 {
    354     if (!canAppendNewLineFeedToSelection(endingSelection()))
    355         return;
    356 
    357     applyCommandToComposite(InsertLineBreakCommand::create(document()));
    358     typingAddedToOpenCommand(InsertLineBreak);
    359 }
    360 
    361 void TypingCommand::insertParagraphSeparator()
    362 {
    363     if (!canAppendNewLineFeedToSelection(endingSelection()))
    364         return;
    365 
    366     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
    367     typingAddedToOpenCommand(InsertParagraphSeparator);
    368 }
    369 
    370 void TypingCommand::insertParagraphSeparatorInQuotedContent()
    371 {
    372     // If the selection starts inside a table, just insert the paragraph separator normally
    373     // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
    374     if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
    375         insertParagraphSeparator();
    376         return;
    377     }
    378 
    379     applyCommandToComposite(BreakBlockquoteCommand::create(document()));
    380     typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
    381 }
    382 
    383 bool TypingCommand::makeEditableRootEmpty()
    384 {
    385     Element* root = endingSelection().rootEditableElement();
    386     if (!root || !root->firstChild())
    387         return false;
    388 
    389     if (root->firstChild() == root->lastChild() && root->firstElementChild() && root->firstElementChild()->hasTagName(brTag)) {
    390         // If there is a single child and it could be a placeholder, leave it alone.
    391         if (root->renderer() && root->renderer()->isBlockFlow())
    392             return false;
    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     Frame* frame = document()->frame();
    407     if (!frame)
    408         return;
    409 
    410     frame->editor()->updateMarkersForWordsAffectedByEditing(false);
    411 
    412     VisibleSelection selectionToDelete;
    413     VisibleSelection selectionAfterUndo;
    414 
    415     switch (endingSelection().selectionType()) {
    416     case VisibleSelection::RangeSelection:
    417         selectionToDelete = endingSelection();
    418         selectionAfterUndo = selectionToDelete;
    419         break;
    420     case VisibleSelection::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         FrameSelection selection;
    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         if (endingSelection().visibleStart().previous(CannotCrossEditingBoundary).isNull()) {
    435             // When the caret is at the start of the editable area in an empty list item, break out of the list item.
    436             if (breakOutOfEmptyListItem()) {
    437                 typingAddedToOpenCommand(DeleteKey);
    438                 return;
    439             }
    440             // When there are no visible positions in the editing root, delete its entire contents.
    441             if (endingSelection().visibleStart().next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
    442                 typingAddedToOpenCommand(DeleteKey);
    443                 return;
    444             }
    445         }
    446 
    447         VisiblePosition visibleStart(endingSelection().visibleStart());
    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 == 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 VisibleSelection::NoSelection:
    485         ASSERT_NOT_REACHED();
    486         break;
    487     }
    488 
    489     ASSERT(!selectionToDelete.isNone());
    490     if (selectionToDelete.isNone())
    491         return;
    492 
    493     if (selectionToDelete.isCaret() || !frame->selection()->shouldDeleteSelection(selectionToDelete))
    494         return;
    495 
    496     if (killRing)
    497         frame->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
    498     // 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 (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     Frame* frame = document()->frame();
    511     if (!frame)
    512         return;
    513 
    514     frame->editor()->updateMarkersForWordsAffectedByEditing(false);
    515 
    516     VisibleSelection selectionToDelete;
    517     VisibleSelection selectionAfterUndo;
    518 
    519     switch (endingSelection().selectionType()) {
    520     case VisibleSelection::RangeSelection:
    521         selectionToDelete = endingSelection();
    522         selectionAfterUndo = selectionToDelete;
    523         break;
    524     case VisibleSelection::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         FrameSelection selection;
    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 == 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 (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
    545             && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
    546             setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
    547             typingAddedToOpenCommand(ForwardDeleteKey);
    548             return;
    549         }
    550 
    551         // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
    552         if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
    553             selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
    554 
    555         selectionToDelete = selection.selection();
    556         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
    557             selectionAfterUndo = selectionToDelete;
    558         else {
    559             // It's a little tricky to compute what the starting selection would have been in the original document.
    560             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
    561             // the current state of the document and we'll get the wrong result.
    562             Position extent = startingSelection().end();
    563             if (extent.containerNode() != selectionToDelete.end().containerNode())
    564                 extent = selectionToDelete.extent();
    565             else {
    566                 int extraCharacters;
    567                 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
    568                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
    569                 else
    570                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
    571                 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
    572             }
    573             selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
    574         }
    575         break;
    576     }
    577     case VisibleSelection::NoSelection:
    578         ASSERT_NOT_REACHED();
    579         break;
    580     }
    581 
    582     ASSERT(!selectionToDelete.isNone());
    583     if (selectionToDelete.isNone())
    584         return;
    585 
    586     if (selectionToDelete.isCaret() || !frame->selection()->shouldDeleteSelection(selectionToDelete))
    587         return;
    588 
    589     if (killRing)
    590         frame->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
    591     // make undo select what was deleted
    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