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