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