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