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 634