48b52aeb8ebb80a24de9fdf987bb46d6c611311a
[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 "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
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 static 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
63 TypingCommand::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
80 void 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
101 void 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
120 void 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
140 void 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
151 void 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.
165 void 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     if (Node* startNode = selectionForInsertion.start().containerNode()) {
176         if (startNode->rootEditableElement() && compositionType != TextCompositionUpdate) {
177             // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
178             ExceptionCode ec = 0;
179             RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
180             startNode->rootEditableElement()->dispatchEvent(evt, ec);
181             newText = evt->text();
182         }
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
218 void 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
236 void 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
252 void 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
270 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd)
271 {
272     return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping();
273 }
274
275 void TypingCommand::closeTyping(EditCommand* cmd)
276 {
277     if (isOpenForMoreTypingCommand(cmd))
278         static_cast<TypingCommand*>(cmd)->closeTyping();
279 }
280
281 void 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
317 EditAction TypingCommand::editingAction() const
318 {
319     return EditActionTyping;
320 }
321
322 void 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
355 void 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
371 void 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
395 void 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
416 void TypingCommand::insertLineBreak()
417 {
418     if (!canAppendNewLineFeed(endingSelection()))
419         return;
420
421     applyCommandToComposite(InsertLineBreakCommand::create(document()));
422     typingAddedToOpenCommand(InsertLineBreak);
423 }
424
425 void TypingCommand::insertParagraphSeparator()
426 {
427     if (!canAppendNewLineFeed(endingSelection()))
428         return;
429
430     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
431     typingAddedToOpenCommand(InsertParagraphSeparator);
432 }
433
434 void 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
447 bool 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
468 void 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().containerNode()))
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().containerNode() == selectionToDelete.start().containerNode()
529             && selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode() > 1) {
530             // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
531             selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
532         }
533
534         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
535             selectionAfterUndo = selectionToDelete;
536         else
537             // It's a little tricky to compute what the starting selection would have been in the original document.
538             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
539             // the current state of the document and we'll get the wrong result.
540             selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
541         break;
542     }
543     case VisibleSelection::NoSelection:
544         ASSERT_NOT_REACHED();
545         break;
546     }
547     
548     ASSERT(!selectionToDelete.isNone());
549     if (selectionToDelete.isNone())
550         return;
551     
552     if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
553         return;
554     
555     if (killRing)
556         document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
557     // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
558     // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
559     // more text than you insert.  In that case all of the text that was around originally should be selected.
560     if (m_openedByBackwardDelete)
561         setStartingSelection(selectionAfterUndo);
562     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
563     setSmartDelete(false);
564     typingAddedToOpenCommand(DeleteKey);
565 }
566
567 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
568 {
569     document()->frame()->editor()->updateMarkersForWordsAffectedByEditing(false);
570
571     VisibleSelection selectionToDelete;
572     VisibleSelection selectionAfterUndo;
573
574     switch (endingSelection().selectionType()) {
575     case VisibleSelection::RangeSelection:
576         selectionToDelete = endingSelection();
577         selectionAfterUndo = selectionToDelete;
578         break;
579     case VisibleSelection::CaretSelection: {
580         m_smartDelete = false;
581
582         // Handle delete at beginning-of-block case.
583         // Do nothing in the case that the caret is at the start of a
584         // root editable element or at the start of a document.
585         SelectionController selection;
586         selection.setSelection(endingSelection());
587         selection.modify(SelectionController::AlterationExtend, DirectionForward, granularity);
588         if (killRing && selection.isCaret() && granularity != CharacterGranularity)
589             selection.modify(SelectionController::AlterationExtend, DirectionForward, CharacterGranularity);
590
591         Position downstreamEnd = endingSelection().end().downstream();
592         VisiblePosition visibleEnd = endingSelection().visibleEnd();
593         if (visibleEnd == endOfParagraph(visibleEnd))
594             downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
595         // When deleting tables: Select the table first, then perform the deletion
596         if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
597             && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
598             setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM));
599             typingAddedToOpenCommand(ForwardDeleteKey);
600             return;
601         }
602
603         // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
604         if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
605             selection.modify(SelectionController::AlterationExtend, DirectionForward, CharacterGranularity);
606
607         selectionToDelete = selection.selection();
608         if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
609             selectionAfterUndo = selectionToDelete;
610         else {
611             // It's a little tricky to compute what the starting selection would have been in the original document.
612             // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
613             // the current state of the document and we'll get the wrong result.
614             Position extent = startingSelection().end();
615             if (extent.containerNode() != selectionToDelete.end().containerNode())
616                 extent = selectionToDelete.extent();
617             else {
618                 int extraCharacters;
619                 if (selectionToDelete.start().containerNode() == selectionToDelete.end().containerNode())
620                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode() - selectionToDelete.start().computeOffsetInContainerNode();
621                 else
622                     extraCharacters = selectionToDelete.end().computeOffsetInContainerNode();
623                 extent = Position(extent.containerNode(), extent.computeOffsetInContainerNode() + extraCharacters, Position::PositionIsOffsetInAnchor);
624             }
625             selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
626         }
627         break;
628     }
629     case VisibleSelection::NoSelection:
630         ASSERT_NOT_REACHED();
631         break;
632     }
633     
634     ASSERT(!selectionToDelete.isNone());
635     if (selectionToDelete.isNone())
636         return;
637     
638     if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
639         return;
640         
641     if (killRing)
642         document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
643     // make undo select what was deleted
644     setStartingSelection(selectionAfterUndo);
645     CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
646     setSmartDelete(false);
647     typingAddedToOpenCommand(ForwardDeleteKey);
648 }
649
650 void TypingCommand::deleteSelection(bool smartDelete)
651 {
652     CompositeEditCommand::deleteSelection(smartDelete);
653     typingAddedToOpenCommand(DeleteSelection);
654 }
655
656 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
657 {
658     switch (commandType) {
659     case DeleteSelection:
660     case DeleteKey:
661     case ForwardDeleteKey:
662     case InsertParagraphSeparator:
663     case InsertLineBreak:
664         m_preservesTypingStyle = true;
665         return;
666     case InsertParagraphSeparatorInQuotedContent:
667     case InsertText:
668         m_preservesTypingStyle = false;
669         return;
670     }
671     ASSERT_NOT_REACHED();
672     m_preservesTypingStyle = false;
673 }
674
675 bool TypingCommand::isTypingCommand() const
676 {
677     return true;
678 }
679
680 } // namespace WebCore