2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "TypingCommand.h"
29 #include "BeforeTextInsertedEvent.h"
30 #include "BreakBlockquoteCommand.h"
31 #include "DeleteSelectionCommand.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"
48 using namespace HTMLNames;
50 TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity, TextCompositionType compositionType,
52 : CompositeEditCommand(document),
53 m_commandType(commandType),
54 m_textToInsert(textToInsert),
55 m_openForMoreTyping(true),
56 m_selectInsertedText(selectInsertedText),
58 m_granularity(granularity),
59 m_compositionType(compositionType),
61 m_openedByBackwardDelete(false)
63 updatePreservesTypingStyle(m_commandType);
66 void TypingCommand::deleteSelection(Document* document, bool smartDelete)
70 Frame* frame = document->frame();
73 if (!frame->selection()->isRange())
76 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
77 if (isOpenForMoreTypingCommand(lastEditCommand)) {
78 static_cast<TypingCommand*>(lastEditCommand)->deleteSelection(smartDelete);
82 RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", false);
83 typingCommand->setSmartDelete(smartDelete);
84 typingCommand->apply();
87 void TypingCommand::deleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity, bool killRing)
91 Frame* frame = document->frame();
94 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
95 if (granularity == CharacterGranularity && isOpenForMoreTypingCommand(lastEditCommand)) {
96 updateSelectionIfDifferentFromCurrentSelection(static_cast<TypingCommand*>(lastEditCommand), frame);
97 static_cast<TypingCommand*>(lastEditCommand)->deleteKeyPressed(granularity, killRing);
101 RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", false, granularity, killRing);
102 typingCommand->setSmartDelete(smartDelete);
103 typingCommand->apply();
106 void TypingCommand::forwardDeleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity, bool killRing)
108 // FIXME: Forward delete in TextEdit appears to open and close a new typing command.
111 Frame* frame = document->frame();
114 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
115 if (granularity == CharacterGranularity && isOpenForMoreTypingCommand(lastEditCommand)) {
116 updateSelectionIfDifferentFromCurrentSelection(static_cast<TypingCommand*>(lastEditCommand), frame);
117 static_cast<TypingCommand*>(lastEditCommand)->forwardDeleteKeyPressed(granularity, killRing);
121 RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", false, granularity, killRing);
122 typingCommand->setSmartDelete(smartDelete);
123 typingCommand->apply();
126 void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
129 VisibleSelection currentSelection = frame->selection()->selection();
130 if (currentSelection == typingCommand->endingSelection())
133 typingCommand->setStartingSelection(currentSelection);
134 typingCommand->setEndingSelection(currentSelection);
138 void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, TextCompositionType composition)
142 Frame* frame = document->frame();
145 insertText(document, text, frame->selection()->selection(), selectInsertedText, composition);
148 // FIXME: We shouldn't need to take selectionForInsertion. It should be identical to SelectionController's current selection.
149 void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, bool selectInsertedText, TextCompositionType compositionType)
151 #if REMOVE_MARKERS_UPON_EDITING
153 document->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(isSpaceOrNewline(text.characters()[0]));
158 RefPtr<Frame> frame = document->frame();
161 VisibleSelection currentSelection = frame->selection()->selection();
162 bool changeSelection = currentSelection != selectionForInsertion;
163 String newText = text;
164 Node* startNode = selectionForInsertion.start().node();
166 if (startNode && startNode->rootEditableElement() && compositionType != TextCompositionUpdate) {
167 // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
168 ExceptionCode ec = 0;
169 RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
170 startNode->rootEditableElement()->dispatchEvent(evt, ec);
171 newText = evt->text();
174 if (newText.isEmpty())
177 // Set the starting and ending selection appropriately if we are using a selection
178 // that is different from the current selection. In the future, we should change EditCommand
179 // to deal with custom selections in a general way that can be used by all of the commands.
180 RefPtr<EditCommand> lastEditCommand = frame->editor()->lastEditCommand();
181 if (isOpenForMoreTypingCommand(lastEditCommand.get())) {
182 TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand.get());
183 if (lastTypingCommand->endingSelection() != selectionForInsertion) {
184 lastTypingCommand->setStartingSelection(selectionForInsertion);
185 lastTypingCommand->setEndingSelection(selectionForInsertion);
188 lastTypingCommand->setCompositionType(compositionType);
189 lastTypingCommand->insertText(newText, selectInsertedText);
193 RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText, compositionType);
194 if (changeSelection) {
195 cmd->setStartingSelection(selectionForInsertion);
196 cmd->setEndingSelection(selectionForInsertion);
199 if (changeSelection) {
200 cmd->setEndingSelection(currentSelection);
201 frame->selection()->setSelection(currentSelection);
205 void TypingCommand::insertLineBreak(Document *document)
209 Frame* frame = document->frame();
212 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
213 if (isOpenForMoreTypingCommand(lastEditCommand)) {
214 static_cast<TypingCommand*>(lastEditCommand)->insertLineBreak();
218 applyCommand(TypingCommand::create(document, InsertLineBreak));
221 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document)
225 Frame* frame = document->frame();
228 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
229 if (isOpenForMoreTypingCommand(lastEditCommand)) {
230 static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparatorInQuotedContent();
234 applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent));
237 void TypingCommand::insertParagraphSeparator(Document *document)
241 Frame* frame = document->frame();
244 EditCommand* lastEditCommand = frame->editor()->lastEditCommand();
245 if (isOpenForMoreTypingCommand(lastEditCommand)) {
246 static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparator();
250 applyCommand(TypingCommand::create(document, InsertParagraphSeparator));
253 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd)
255 return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping();
258 void TypingCommand::closeTyping(EditCommand* cmd)
260 if (isOpenForMoreTypingCommand(cmd))
261 static_cast<TypingCommand*>(cmd)->closeTyping();
264 void TypingCommand::doApply()
266 if (!endingSelection().isNonOrphanedCaretOrRange())
269 if (m_commandType == DeleteKey)
270 if (m_commands.isEmpty())
271 m_openedByBackwardDelete = true;
273 switch (m_commandType) {
274 case DeleteSelection:
275 deleteSelection(m_smartDelete);
278 deleteKeyPressed(m_granularity, m_killRing);
280 case ForwardDeleteKey:
281 forwardDeleteKeyPressed(m_granularity, m_killRing);
283 case InsertLineBreak:
286 case InsertParagraphSeparator:
287 insertParagraphSeparator();
289 case InsertParagraphSeparatorInQuotedContent:
290 insertParagraphSeparatorInQuotedContent();
293 insertText(m_textToInsert, m_selectInsertedText);
297 ASSERT_NOT_REACHED();
300 EditAction TypingCommand::editingAction() const
302 return EditActionTyping;
305 void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
307 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
308 if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()
309 && !document()->frame()->editor()->isAutomaticQuoteSubstitutionEnabled()
310 && !document()->frame()->editor()->isAutomaticLinkDetectionEnabled()
311 && !document()->frame()->editor()->isAutomaticDashSubstitutionEnabled()
312 && !document()->frame()->editor()->isAutomaticTextReplacementEnabled())
315 if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled())
318 // Take a look at the selection that results after typing and determine whether we need to spellcheck.
319 // Since the word containing the current selection is never marked, this does a check to
320 // see if typing made a new word that is not in the current selection. Basically, you
321 // get this by being at the end of a word and typing a space.
322 VisiblePosition start(endingSelection().start(), endingSelection().affinity());
323 VisiblePosition previous = start.previous();
324 if (previous.isNotNull()) {
325 VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
326 VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
328 document()->frame()->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection());
329 #if SUPPORT_AUTOCORRECTION_PANEL
330 else if (commandType == TypingCommand::InsertText)
331 document()->frame()->editor()->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
333 UNUSED_PARAM(commandType);
338 void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
340 updatePreservesTypingStyle(commandTypeForAddedTyping);
342 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
343 document()->frame()->editor()->appliedEditing(this);
344 // Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
345 markMisspellingsAfterTyping(commandTypeForAddedTyping);
347 // The old spellchecking code requires that checking be done first, to prevent issues like that in 6864072, where <doesn't> is marked as misspelled.
348 markMisspellingsAfterTyping(commandTypeForAddedTyping);
349 document()->frame()->editor()->appliedEditing(this);
353 void TypingCommand::insertText(const String &text, bool selectInsertedText)
355 // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved.
356 // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending
357 // an existing selection; at the moment they can either put the caret after what's inserted or
358 // select what's inserted, but there's no way to "extend selection" to include both an old selection
359 // that ends just before where we want to insert text and the newly inserted text.
362 while ((newline = text.find('\n', offset)) != notFound) {
363 if (newline != offset)
364 insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false);
365 insertParagraphSeparator();
366 offset = newline + 1;
369 insertTextRunWithoutNewlines(text, selectInsertedText);
371 unsigned length = text.length();
372 if (length != offset)
373 insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText);
377 void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
379 RefPtr<InsertTextCommand> command;
380 if (!document()->frame()->selection()->typingStyle() && !m_commands.isEmpty()) {
381 EditCommand* lastCommand = m_commands.last().get();
382 if (lastCommand->isInsertTextCommand())
383 command = static_cast<InsertTextCommand*>(lastCommand);
386 command = InsertTextCommand::create(document());
387 applyCommandToComposite(command);
389 if (endingSelection() != command->endingSelection()) {
390 command->setStartingSelection(endingSelection());
391 command->setEndingSelection(endingSelection());
393 command->input(text, selectInsertedText,
394 m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
395 typingAddedToOpenCommand(InsertText);
398 void TypingCommand::insertLineBreak()
400 applyCommandToComposite(InsertLineBreakCommand::create(document()));
401 typingAddedToOpenCommand(InsertLineBreak);
404 void TypingCommand::insertParagraphSeparator()
406 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
407 typingAddedToOpenCommand(InsertParagraphSeparator);
410 void TypingCommand::insertParagraphSeparatorInQuotedContent()
412 // If the selection starts inside a table, just insert the paragraph separator normally
413 // Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
414 if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
415 insertParagraphSeparator();
419 applyCommandToComposite(BreakBlockquoteCommand::create(document()));
420 typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
423 bool TypingCommand::makeEditableRootEmpty()
425 Element* root = endingSelection().rootEditableElement();
426 if (!root->firstChild())
429 if (root->firstChild() == root->lastChild() && root->firstElementChild() && root->firstElementChild()->hasTagName(brTag)) {
430 // If there is a single child and it could be a placeholder, leave it alone.
431 if (root->renderer() && root->renderer()->isBlockFlow())
435 while (Node* child = root->firstChild())
438 addBlockPlaceholderIfNeeded(root);
439 setEndingSelection(VisibleSelection(firstPositionInNode(root), DOWNSTREAM));
444 void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
446 #if REMOVE_MARKERS_UPON_EDITING
447 document()->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
449 VisibleSelection selectionToDelete;
450 VisibleSelection selectionAfterUndo;
452 switch (endingSelection().selectionType()) {
453 case VisibleSelection::RangeSelection:
454 selectionToDelete = endingSelection();
455 selectionAfterUndo = selectionToDelete;
457 case VisibleSelection::CaretSelection: {
458 // After breaking out of an empty mail blockquote, we still want continue with the deletion
459 // so actual content will get deleted, and not just the quote style.
460 if (breakOutOfEmptyMailBlockquotedParagraph())
461 typingAddedToOpenCommand(DeleteKey);
463 m_smartDelete = false;
465 SelectionController selection;
466 selection.setSelection(endingSelection());
467 selection.modify(SelectionController::AlterationExtend, DirectionBackward, granularity);
468 if (killRing && selection.isCaret() && granularity != CharacterGranularity)
469 selection.modify(SelectionController::AlterationExtend, DirectionBackward, CharacterGranularity);
471 if (endingSelection().visibleStart().previous(true).isNull()) {
472 // When the caret is at the start of the editable area in an empty list item, break out of the list item.
473 if (breakOutOfEmptyListItem()) {
474 typingAddedToOpenCommand(DeleteKey);
477 // When there are no visible positions in the editing root, delete its entire contents.
478 if (endingSelection().visibleStart().next(true).isNull() && makeEditableRootEmpty()) {
479 typingAddedToOpenCommand(DeleteKey);
484 VisiblePosition visibleStart(endingSelection().visibleStart());
485 // If we have a caret selection on an empty cell, we have nothing to do.
486 if (isEmptyTableCell(visibleStart.deepEquivalent().node()))
489 // If the caret is at the start of a paragraph after a table, move content into the last table cell.
490 if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) {
491 // Unless the caret is just before a table. We don't want to move a table into the last table cell.
492 if (isLastPositionBeforeTable(visibleStart))
494 // Extend the selection backward into the last cell, then deletion will handle the move.
495 selection.modify(SelectionController::AlterationExtend, DirectionBackward, granularity);
496 // If the caret is just after a table, select the table and don't delete anything.
497 } else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
498 setEndingSelection(VisibleSelection(positionAfterNode(table), endingSelection().start(), DOWNSTREAM));
499 typingAddedToOpenCommand(DeleteKey);
503 selectionToDelete = selection.selection();
505 if (granularity == CharacterGranularity && selectionToDelete.end().node() == selectionToDelete.start().node() && selectionToDelete.end().deprecatedEditingOffset() - selectionToDelete.start().deprecatedEditingOffset() > 1) {
506 // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
507 selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
510 if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
511 selectionAfterUndo = selectionToDelete;
513 // It's a little tricky to compute what the starting selection would have been in the original document.
514 // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
515 // the current state of the document and we'll get the wrong result.
516 selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
519 case VisibleSelection::NoSelection:
520 ASSERT_NOT_REACHED();
524 ASSERT(!selectionToDelete.isNone());
525 if (selectionToDelete.isNone())
528 if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
532 document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
533 // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
534 // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
535 // more text than you insert. In that case all of the text that was around originally should be selected.
536 if (m_openedByBackwardDelete)
537 setStartingSelection(selectionAfterUndo);
538 CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
539 setSmartDelete(false);
540 typingAddedToOpenCommand(DeleteKey);
543 void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
545 #if REMOVE_MARKERS_UPON_EDITING
546 document()->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(false);
548 VisibleSelection selectionToDelete;
549 VisibleSelection selectionAfterUndo;
551 switch (endingSelection().selectionType()) {
552 case VisibleSelection::RangeSelection:
553 selectionToDelete = endingSelection();
554 selectionAfterUndo = selectionToDelete;
556 case VisibleSelection::CaretSelection: {
557 m_smartDelete = false;
559 // Handle delete at beginning-of-block case.
560 // Do nothing in the case that the caret is at the start of a
561 // root editable element or at the start of a document.
562 SelectionController selection;
563 selection.setSelection(endingSelection());
564 selection.modify(SelectionController::AlterationExtend, DirectionForward, granularity);
565 if (killRing && selection.isCaret() && granularity != CharacterGranularity)
566 selection.modify(SelectionController::AlterationExtend, DirectionForward, CharacterGranularity);
568 Position downstreamEnd = endingSelection().end().downstream();
569 VisiblePosition visibleEnd = endingSelection().visibleEnd();
570 if (visibleEnd == endOfParagraph(visibleEnd))
571 downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream();
572 // When deleting tables: Select the table first, then perform the deletion
573 if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && !downstreamEnd.deprecatedEditingOffset()) {
574 setEndingSelection(VisibleSelection(endingSelection().end(), lastDeepEditingPositionForNode(downstreamEnd.node()), DOWNSTREAM));
575 typingAddedToOpenCommand(ForwardDeleteKey);
579 // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any)
580 if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd()))
581 selection.modify(SelectionController::AlterationExtend, DirectionForward, CharacterGranularity);
583 selectionToDelete = selection.selection();
584 if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
585 selectionAfterUndo = selectionToDelete;
587 // It's a little tricky to compute what the starting selection would have been in the original document.
588 // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
589 // the current state of the document and we'll get the wrong result.
590 Position extent = startingSelection().end();
591 if (extent.node() != selectionToDelete.end().node())
592 extent = selectionToDelete.extent();
595 if (selectionToDelete.start().node() == selectionToDelete.end().node())
596 extraCharacters = selectionToDelete.end().deprecatedEditingOffset() - selectionToDelete.start().deprecatedEditingOffset();
598 extraCharacters = selectionToDelete.end().deprecatedEditingOffset();
599 extent = Position(extent.node(), extent.deprecatedEditingOffset() + extraCharacters, Position::PositionIsOffsetInAnchor);
601 selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
605 case VisibleSelection::NoSelection:
606 ASSERT_NOT_REACHED();
610 ASSERT(!selectionToDelete.isNone());
611 if (selectionToDelete.isNone())
614 if (selectionToDelete.isCaret() || !document()->frame()->selection()->shouldDeleteSelection(selectionToDelete))
618 document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
619 // make undo select what was deleted
620 setStartingSelection(selectionAfterUndo);
621 CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
622 setSmartDelete(false);
623 typingAddedToOpenCommand(ForwardDeleteKey);
626 void TypingCommand::deleteSelection(bool smartDelete)
628 CompositeEditCommand::deleteSelection(smartDelete);
629 typingAddedToOpenCommand(DeleteSelection);
632 void TypingCommand::updatePreservesTypingStyle(ETypingCommand commandType)
634 switch (commandType) {
635 case DeleteSelection:
637 case ForwardDeleteKey:
638 case InsertParagraphSeparator:
639 case InsertLineBreak:
640 m_preservesTypingStyle = true;
642 case InsertParagraphSeparatorInQuotedContent:
644 m_preservesTypingStyle = false;
647 ASSERT_NOT_REACHED();
648 m_preservesTypingStyle = false;
651 bool TypingCommand::isTypingCommand() const
656 } // namespace WebCore