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