OSDN Git Service

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