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