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