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