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 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 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 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 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 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 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 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 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 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 234 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd) 235 { 236 return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping(); 237 } 238 239 void TypingCommand::closeTyping(EditCommand* cmd) 240 { 241 if (isOpenForMoreTypingCommand(cmd)) 242 static_cast<TypingCommand*>(cmd)->closeTyping(); 243 } 244 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 281 EditAction TypingCommand::editingAction() const 282 { 283 return EditActionTyping; 284 } 285 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 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 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 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 369 void TypingCommand::insertLineBreak() 370 { 371 applyCommandToComposite(InsertLineBreakCommand::create(document())); 372 typingAddedToOpenCommand(InsertLineBreak); 373 } 374 375 void TypingCommand::insertParagraphSeparator() 376 { 377 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document())); 378 typingAddedToOpenCommand(InsertParagraphSeparator); 379 } 380 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 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 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 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 591 void TypingCommand::deleteSelection(bool smartDelete) 592 { 593 CompositeEditCommand::deleteSelection(smartDelete); 594 typingAddedToOpenCommand(DeleteSelection); 595 } 596 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 616 bool TypingCommand::isTypingCommand() const 617 { 618 return true; 619 } 620 621 } // namespace WebCore 622