1/* 2 * Copyright (C) 2006, 2007, 2008, 2011, 2013, 2014 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "Editor.h" 29 30#include "AXObjectCache.h" 31#include "AlternativeTextController.h" 32#include "ApplyStyleCommand.h" 33#include "CSSComputedStyleDeclaration.h" 34#include "CSSPropertyNames.h" 35#include "CachedResourceLoader.h" 36#include "ClipboardEvent.h" 37#include "CompositionEvent.h" 38#include "CreateLinkCommand.h" 39#include "DataTransfer.h" 40#include "DeleteSelectionCommand.h" 41#include "DictationAlternative.h" 42#include "DictationCommand.h" 43#include "DocumentFragment.h" 44#include "DocumentMarkerController.h" 45#include "EditorClient.h" 46#include "EventHandler.h" 47#include "EventNames.h" 48#include "ExceptionCodePlaceholder.h" 49#include "FocusController.h" 50#include "Frame.h" 51#include "FrameTree.h" 52#include "FrameView.h" 53#include "GraphicsContext.h" 54#include "HTMLFormControlElement.h" 55#include "HTMLFrameOwnerElement.h" 56#include "HTMLImageElement.h" 57#include "HTMLNames.h" 58#include "HTMLTextAreaElement.h" 59#include "HitTestResult.h" 60#include "IndentOutdentCommand.h" 61#include "InsertListCommand.h" 62#include "KeyboardEvent.h" 63#include "KillRing.h" 64#include "ModifySelectionListLevel.h" 65#include "NodeList.h" 66#include "NodeTraversal.h" 67#include "Page.h" 68#include "Pasteboard.h" 69#include "Range.h" 70#include "RemoveFormatCommand.h" 71#include "RenderBlock.h" 72#include "RenderTextControl.h" 73#include "RenderedPosition.h" 74#include "ReplaceSelectionCommand.h" 75#include "Settings.h" 76#include "ShadowRoot.h" 77#include "SimplifyMarkupCommand.h" 78#include "Sound.h" 79#include "SpellChecker.h" 80#include "SpellingCorrectionCommand.h" 81#include "StyleProperties.h" 82#include "TelephoneNumberDetector.h" 83#include "Text.h" 84#include "TextCheckerClient.h" 85#include "TextCheckingHelper.h" 86#include "TextEvent.h" 87#include "TextIterator.h" 88#include "TypingCommand.h" 89#include "UserTypingGestureIndicator.h" 90#include "VisibleUnits.h" 91#include "htmlediting.h" 92#include "markup.h" 93#include <wtf/unicode/CharacterNames.h> 94 95#if ENABLE(DELETION_UI) 96#include "DeleteButtonController.h" 97#endif 98 99#if PLATFORM(IOS) 100#include "DictationCommandIOS.h" 101#include <wtf/text/StringBuilder.h> 102#include <wtf/text/WTFString.h> 103#endif 104 105namespace WebCore { 106 107#if PLATFORM(IOS) 108class ClearTextCommand : public DeleteSelectionCommand { 109public: 110 ClearTextCommand(Document& document); 111 static void CreateAndApply(const RefPtr<Frame> frame); 112 113private: 114 virtual EditAction editingAction() const; 115}; 116 117ClearTextCommand::ClearTextCommand(Document& document) 118 : DeleteSelectionCommand(document, false, true, false, false, true) 119{ 120} 121 122EditAction ClearTextCommand::editingAction() const 123{ 124 return EditActionDelete; 125} 126 127void ClearTextCommand::CreateAndApply(const RefPtr<Frame> frame) 128{ 129 if (frame->selection().isNone()) 130 return; 131 132 // Don't leave around stale composition state. 133 frame->editor().clear(); 134 135 const VisibleSelection oldSelection = frame->selection().selection(); 136 frame->selection().selectAll(); 137 RefPtr<ClearTextCommand> clearCommand = adoptRef(new ClearTextCommand(*frame->document())); 138 clearCommand->setStartingSelection(oldSelection); 139 applyCommand(clearCommand.release()); 140} 141#endif 142 143using namespace HTMLNames; 144using namespace WTF; 145using namespace Unicode; 146 147#if ENABLE(DELETION_UI) 148 149PassRefPtr<Range> Editor::avoidIntersectionWithDeleteButtonController(const Range* range) const 150{ 151 if (!range) 152 return 0; 153 154 Node* startContainer = range->startContainer(); 155 int startOffset = range->startOffset(); 156 Node* endContainer = range->endContainer(); 157 int endOffset = range->endOffset(); 158 159 if (!startContainer) 160 return 0; 161 162 ASSERT(endContainer); 163 164 Element* element = m_deleteButtonController->containerElement(); 165 if (startContainer == element || startContainer->isDescendantOf(element)) { 166 ASSERT(element->parentNode()); 167 startContainer = element->parentNode(); 168 startOffset = element->nodeIndex(); 169 } 170 if (endContainer == element || endContainer->isDescendantOf(element)) { 171 ASSERT(element->parentNode()); 172 endContainer = element->parentNode(); 173 endOffset = element->nodeIndex(); 174 } 175 176 return Range::create(range->ownerDocument(), startContainer, startOffset, endContainer, endOffset); 177} 178 179VisibleSelection Editor::avoidIntersectionWithDeleteButtonController(const VisibleSelection& selection) const 180{ 181 if (selection.isNone()) 182 return selection; 183 184 Element* element = m_deleteButtonController->containerElement(); 185 if (!element) 186 return selection; 187 VisibleSelection updatedSelection = selection; 188 189 Position updatedBase = selection.base(); 190 updatePositionForNodeRemoval(updatedBase, element); 191 if (updatedBase != selection.base()) 192 updatedSelection.setBase(updatedBase); 193 194 Position updatedExtent = selection.extent(); 195 updatePositionForNodeRemoval(updatedExtent, element); 196 if (updatedExtent != selection.extent()) 197 updatedSelection.setExtent(updatedExtent); 198 199 return updatedSelection; 200} 201 202#endif 203 204// When an event handler has moved the selection outside of a text control 205// we should use the target control's selection for this editing operation. 206VisibleSelection Editor::selectionForCommand(Event* event) 207{ 208 VisibleSelection selection = m_frame.selection().selection(); 209 if (!event) 210 return selection; 211 // If the target is a text control, and the current selection is outside of its shadow tree, 212 // then use the saved selection for that text control. 213 HTMLTextFormControlElement* textFormControlOfSelectionStart = enclosingTextFormControl(selection.start()); 214 HTMLTextFormControlElement* textFromControlOfTarget = isHTMLTextFormControlElement(*event->target()->toNode()) ? toHTMLTextFormControlElement(event->target()->toNode()) : nullptr; 215 if (textFromControlOfTarget && (selection.start().isNull() || textFromControlOfTarget != textFormControlOfSelectionStart)) { 216 if (RefPtr<Range> range = textFromControlOfTarget->selection()) 217 return VisibleSelection(range.get(), DOWNSTREAM, selection.isDirectional()); 218 } 219 return selection; 220} 221 222// Function considers Mac editing behavior a fallback when Page or Settings is not available. 223EditingBehavior Editor::behavior() const 224{ 225 return EditingBehavior(m_frame.settings().editingBehaviorType()); 226} 227 228EditorClient* Editor::client() const 229{ 230 if (Page* page = m_frame.page()) 231 return page->editorClient(); 232 return 0; 233} 234 235 236TextCheckerClient* Editor::textChecker() const 237{ 238 if (EditorClient* owner = client()) 239 return owner->textChecker(); 240 return 0; 241} 242 243void Editor::handleKeyboardEvent(KeyboardEvent* event) 244{ 245 if (EditorClient* c = client()) 246 c->handleKeyboardEvent(event); 247} 248 249void Editor::handleInputMethodKeydown(KeyboardEvent* event) 250{ 251 if (EditorClient* c = client()) 252 c->handleInputMethodKeydown(event); 253} 254 255bool Editor::handleTextEvent(TextEvent* event) 256{ 257 // Default event handling for Drag and Drop will be handled by DragController 258 // so we leave the event for it. 259 if (event->isDrop()) 260 return false; 261 262 if (event->isPaste()) { 263 if (event->pastingFragment()) 264#if PLATFORM(IOS) 265 { 266 if (client()->performsTwoStepPaste(event->pastingFragment())) 267 return true; 268#endif 269 replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle(), event->mailBlockquoteHandling()); 270#if PLATFORM(IOS) 271 } 272#endif 273 else 274 replaceSelectionWithText(event->data(), false, event->shouldSmartReplace()); 275 return true; 276 } 277 278 String data = event->data(); 279 if (data == "\n") { 280 if (event->isLineBreak()) 281 return insertLineBreak(); 282 return insertParagraphSeparator(); 283 } 284 285 return insertTextWithoutSendingTextEvent(data, false, event); 286} 287 288bool Editor::canEdit() const 289{ 290 return m_frame.selection().selection().rootEditableElement(); 291} 292 293bool Editor::canEditRichly() const 294{ 295 return m_frame.selection().selection().isContentRichlyEditable(); 296} 297 298// WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They 299// also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items. 300// We need to use onbeforecopy as a real menu enabler because we allow elements that are not 301// normally selectable to implement copy/paste (like divs, or a document body). 302 303bool Editor::canDHTMLCut() 304{ 305 return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, DataTransferAccessPolicy::Numb); 306} 307 308bool Editor::canDHTMLCopy() 309{ 310 return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, DataTransferAccessPolicy::Numb); 311} 312 313bool Editor::canDHTMLPaste() 314{ 315 return !dispatchCPPEvent(eventNames().beforepasteEvent, DataTransferAccessPolicy::Numb); 316} 317 318bool Editor::canCut() const 319{ 320 return canCopy() && canDelete(); 321} 322 323static HTMLImageElement* imageElementFromImageDocument(Document& document) 324{ 325 if (!document.isImageDocument()) 326 return 0; 327 328 HTMLElement* body = document.body(); 329 if (!body) 330 return 0; 331 332 Node* node = body->firstChild(); 333 if (!node) 334 return 0; 335 if (!isHTMLImageElement(node)) 336 return 0; 337 return toHTMLImageElement(node); 338} 339 340bool Editor::canCopy() const 341{ 342 if (imageElementFromImageDocument(document())) 343 return true; 344 const VisibleSelection& selection = m_frame.selection().selection(); 345 return selection.isRange() && !selection.isInPasswordField(); 346} 347 348bool Editor::canPaste() const 349{ 350 return canEdit(); 351} 352 353bool Editor::canDelete() const 354{ 355 const VisibleSelection& selection = m_frame.selection().selection(); 356 return selection.isRange() && selection.rootEditableElement(); 357} 358 359bool Editor::canDeleteRange(Range* range) const 360{ 361 Node* startContainer = range->startContainer(); 362 Node* endContainer = range->endContainer(); 363 if (!startContainer || !endContainer) 364 return false; 365 366 if (!startContainer->hasEditableStyle() || !endContainer->hasEditableStyle()) 367 return false; 368 369 if (range->collapsed(IGNORE_EXCEPTION)) { 370 VisiblePosition start(range->startPosition(), DOWNSTREAM); 371 VisiblePosition previous = start.previous(); 372 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item. 373 if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement()) 374 return false; 375 } 376 return true; 377} 378 379bool Editor::smartInsertDeleteEnabled() 380{ 381 return client() && client()->smartInsertDeleteEnabled(); 382} 383 384bool Editor::canSmartCopyOrDelete() 385{ 386 return client() && client()->smartInsertDeleteEnabled() && m_frame.selection().granularity() == WordGranularity; 387} 388 389bool Editor::isSelectTrailingWhitespaceEnabled() 390{ 391 return client() && client()->isSelectTrailingWhitespaceEnabled(); 392} 393 394bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction) 395{ 396 if (!canEdit()) 397 return false; 398 399 if (m_frame.selection().isRange()) { 400 if (isTypingAction) { 401 TypingCommand::deleteKeyPressed(document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity); 402 revealSelectionAfterEditingOperation(); 403 } else { 404 if (killRing) 405 addToKillRing(selectedRange().get(), false); 406 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 407 // Implicitly calls revealSelectionAfterEditingOperation(). 408 } 409 } else { 410 TypingCommand::Options options = 0; 411 if (canSmartCopyOrDelete()) 412 options |= TypingCommand::SmartDelete; 413 if (killRing) 414 options |= TypingCommand::KillRing; 415 switch (direction) { 416 case DirectionForward: 417 case DirectionRight: 418 TypingCommand::forwardDeleteKeyPressed(document(), options, granularity); 419 break; 420 case DirectionBackward: 421 case DirectionLeft: 422 TypingCommand::deleteKeyPressed(document(), options, granularity); 423 break; 424 } 425 revealSelectionAfterEditingOperation(); 426 } 427 428 // FIXME: We should to move this down into deleteKeyPressed. 429 // clear the "start new kill ring sequence" setting, because it was set to true 430 // when the selection was updated by deleting the range 431 if (killRing) 432 setStartNewKillRingSequence(false); 433 434 return true; 435} 436 437void Editor::deleteSelectionWithSmartDelete(bool smartDelete) 438{ 439 if (m_frame.selection().isNone()) 440 return; 441 442 applyCommand(DeleteSelectionCommand::create(document(), smartDelete)); 443} 444 445#if PLATFORM(IOS) 446void Editor::clearText() 447{ 448 ClearTextCommand::CreateAndApply(&m_frame); 449} 450 451void Editor::insertDictationPhrases(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata) 452{ 453 if (m_frame.selection().isNone()) 454 return; 455 456 if (dictationPhrases->isEmpty()) 457 return; 458 459 applyCommand(DictationCommandIOS::create(document(), dictationPhrases, metadata)); 460} 461 462void Editor::setDictationPhrasesAsChildOfElement(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata, Element* element) 463{ 464 // Clear the composition. 465 clear(); 466 467 // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day 468 // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish. 469 clearUndoRedoOperations(); 470 471 m_frame.selection().clear(); 472 473 element->removeChildren(); 474 475 if (dictationPhrases->isEmpty()) { 476 client()->respondToChangedContents(); 477 return; 478 } 479 480 ExceptionCode ec; 481 RefPtr<Range> context = document().createRange(); 482 context->selectNodeContents(element, ec); 483 484 StringBuilder dictationPhrasesBuilder; 485 size_t dictationPhraseCount = dictationPhrases->size(); 486 for (size_t i = 0; i < dictationPhraseCount; i++) { 487 const String& firstInterpretation = dictationPhrases->at(i)[0]; 488 dictationPhrasesBuilder.append(firstInterpretation); 489 } 490 String serializedDictationPhrases = dictationPhrasesBuilder.toString(); 491 492 element->appendChild(createFragmentFromText(*context.get(), serializedDictationPhrases), ec); 493 494 // We need a layout in order to add markers below. 495 document().updateLayout(); 496 497 if (!element->firstChild()->isTextNode()) { 498 // Shouldn't happen. 499 ASSERT(element->firstChild()->isTextNode()); 500 return; 501 } 502 503 Text* textNode = static_cast<Text*>(element->firstChild()); 504 int previousDictationPhraseStart = 0; 505 for (size_t i = 0; i < dictationPhraseCount; i++) { 506 const Vector<String>& interpretations = dictationPhrases->at(i); 507 int dictationPhraseLength = interpretations[0].length(); 508 int dictationPhraseEnd = previousDictationPhraseStart + dictationPhraseLength; 509 if (interpretations.size() > 1) { 510 RefPtr<Range> dictationPhraseRange = Range::create(document(), textNode, previousDictationPhraseStart, textNode, dictationPhraseEnd); 511 document().markers().addDictationPhraseWithAlternativesMarker(dictationPhraseRange.get(), interpretations); 512 } 513 previousDictationPhraseStart = dictationPhraseEnd; 514 } 515 516 RefPtr<Range> resultRange = Range::create(document(), textNode, 0, textNode, textNode->length()); 517 document().markers().addDictationResultMarker(resultRange.get(), metadata); 518 519 client()->respondToChangedContents(); 520} 521#endif 522 523void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace) 524{ 525 Node* target = findEventTargetFromSelection(); 526 if (!target) 527 return; 528 target->dispatchEvent(TextEvent::createForPlainTextPaste(document().domWindow(), pastingText, smartReplace), IGNORE_EXCEPTION); 529} 530 531void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle, MailBlockquoteHandling respectsMailBlockquote) 532{ 533 Node* target = findEventTargetFromSelection(); 534 if (!target) 535 return; 536 target->dispatchEvent(TextEvent::createForFragmentPaste(document().domWindow(), pastingFragment, smartReplace, matchStyle, respectsMailBlockquote), IGNORE_EXCEPTION); 537} 538 539void Editor::pasteAsPlainTextBypassingDHTML() 540{ 541 pasteAsPlainTextWithPasteboard(*Pasteboard::createForCopyAndPaste()); 542} 543 544void Editor::pasteAsPlainTextWithPasteboard(Pasteboard& pasteboard) 545{ 546 String text = readPlainTextFromPasteboard(pasteboard); 547 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted)) 548 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard)); 549} 550 551String Editor::readPlainTextFromPasteboard(Pasteboard& pasteboard) 552{ 553 PasteboardPlainText text; 554 pasteboard.read(text); 555 return plainTextFromPasteboard(text); 556} 557 558#if !PLATFORM(MAC) 559 560String Editor::plainTextFromPasteboard(const PasteboardPlainText& text) 561{ 562 return text.text; 563} 564 565#endif 566 567#if !PLATFORM(COCOA) && !PLATFORM(EFL) 568void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling) 569{ 570 RefPtr<Range> range = selectedRange(); 571 if (!range) 572 return; 573 574 bool chosePlainText; 575 RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, *range, allowPlainText, chosePlainText); 576 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted)) 577 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, mailBlockquoteHandling); 578} 579#endif 580 581bool Editor::canSmartReplaceWithPasteboard(Pasteboard& pasteboard) 582{ 583 return client() && client()->smartInsertDeleteEnabled() && pasteboard.canSmartReplace(); 584} 585 586bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction) 587{ 588 if (!client()) 589 return false; 590 591 if (fragment) { 592 Node* child = fragment->firstChild(); 593 if (child && fragment->lastChild() == child && child->isCharacterDataNode()) 594 return client()->shouldInsertText(toCharacterData(child)->data(), replacingDOMRange.get(), givenAction); 595 } 596 597 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction); 598} 599 600void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, MailBlockquoteHandling mailBlockquoteHandling) 601{ 602 VisibleSelection selection = m_frame.selection().selection(); 603 if (selection.isNone() || !selection.isContentEditable() || !fragment) 604 return; 605 606 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting | ReplaceSelectionCommand::SanitizeFragment; 607 if (selectReplacement) 608 options |= ReplaceSelectionCommand::SelectReplacement; 609 if (smartReplace) 610 options |= ReplaceSelectionCommand::SmartReplace; 611 if (matchStyle) 612 options |= ReplaceSelectionCommand::MatchStyle; 613 if (mailBlockquoteHandling == MailBlockquoteHandling::IgnoreBlockquote) 614 options |= ReplaceSelectionCommand::IgnoreMailBlockquote; 615 616 applyCommand(ReplaceSelectionCommand::create(document(), fragment, options, EditActionPaste)); 617 revealSelectionAfterEditingOperation(); 618 619 selection = m_frame.selection().selection(); 620 if (selection.isInPasswordField() || !isContinuousSpellCheckingEnabled()) 621 return; 622 Node* nodeToCheck = selection.rootEditableElement(); 623 if (!nodeToCheck) 624 return; 625 626 RefPtr<Range> rangeToCheck = Range::create(document(), firstPositionInNode(nodeToCheck), lastPositionInNode(nodeToCheck)); 627 m_spellChecker->requestCheckingFor(SpellCheckRequest::create(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck, rangeToCheck)); 628} 629 630void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) 631{ 632 RefPtr<Range> range = selectedRange(); 633 if (!range) 634 return; 635 636 replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true); 637} 638 639PassRefPtr<Range> Editor::selectedRange() 640{ 641 return m_frame.selection().toNormalizedRange(); 642} 643 644#if PLATFORM(IOS) 645void Editor::confirmMarkedText() 646{ 647 // FIXME: This is a hacky workaround for the keyboard calling this method too late - 648 // after the selection and focus have already changed. See <rdar://problem/5975559> 649 Element* focused = document().focusedElement(); 650 Node* composition = compositionNode(); 651 652 if (composition && focused && focused != composition && !composition->isDescendantOrShadowDescendantOf(focused)) { 653 cancelComposition(); 654 document().setFocusedElement(focused); 655 } else 656 confirmComposition(); 657} 658 659void Editor::setTextAsChildOfElement(const String& text, Element* elem) 660{ 661 // Clear the composition 662 clear(); 663 664 // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day 665 // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish. 666 clearUndoRedoOperations(); 667 668 // If the element is empty already and we're not adding text, we can early return and avoid clearing/setting 669 // a selection at [0, 0] and the expense involved in creation VisiblePositions. 670 if (!elem->firstChild() && text.isEmpty()) 671 return; 672 673 // As a side effect this function sets a caret selection after the inserted content. Much of what 674 // follows is more expensive if there is a selection, so clear it since it's going to change anyway. 675 m_frame.selection().clear(); 676 677 // clear out all current children of element 678 elem->removeChildren(); 679 680 if (text.length()) { 681 // insert new text 682 // remove element from tree while doing it 683 // FIXME: The element we're inserting into is often the body element. It seems strange to be removing it 684 // (even if it is only temporary). ReplaceSelectionCommand doesn't bother doing this when it inserts 685 // content, why should we here? 686 ExceptionCode ec; 687 RefPtr<Node> parent = elem->parentNode(); 688 RefPtr<Node> siblingAfter = elem->nextSibling(); 689 if (parent) 690 elem->remove(ec); 691 692 RefPtr<Range> context = document().createRange(); 693 context->selectNodeContents(elem, ec); 694 RefPtr<DocumentFragment> fragment = createFragmentFromText(*context.get(), text); 695 elem->appendChild(fragment, ec); 696 697 // restore element to document 698 if (parent) { 699 if (siblingAfter) 700 parent->insertBefore(elem, siblingAfter.get(), ec); 701 else 702 parent->appendChild(elem, ec); 703 } 704 } 705 706 // set the selection to the end 707 VisibleSelection selection; 708 709 Position pos = createLegacyEditingPosition(elem, elem->childNodeCount()); 710 711 VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); 712 if (visiblePos.isNull()) 713 return; 714 715 selection.setBase(visiblePos); 716 selection.setExtent(visiblePos); 717 718 m_frame.selection().setSelection(selection); 719 720 client()->respondToChangedContents(); 721} 722#endif 723 724bool Editor::shouldDeleteRange(Range* range) const 725{ 726 if (!range || range->collapsed(IGNORE_EXCEPTION)) 727 return false; 728 729 if (!canDeleteRange(range)) 730 return false; 731 732 return client() && client()->shouldDeleteRange(range); 733} 734 735bool Editor::tryDHTMLCopy() 736{ 737 if (m_frame.selection().selection().isInPasswordField()) 738 return false; 739 740 return !dispatchCPPEvent(eventNames().copyEvent, DataTransferAccessPolicy::Writable); 741} 742 743bool Editor::tryDHTMLCut() 744{ 745 if (m_frame.selection().selection().isInPasswordField()) 746 return false; 747 748 return !dispatchCPPEvent(eventNames().cutEvent, DataTransferAccessPolicy::Writable); 749} 750 751bool Editor::tryDHTMLPaste() 752{ 753 return !dispatchCPPEvent(eventNames().pasteEvent, DataTransferAccessPolicy::Readable); 754} 755 756bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const 757{ 758 return client() && client()->shouldInsertText(text, range, action); 759} 760 761void Editor::respondToChangedContents(const VisibleSelection& endingSelection) 762{ 763 if (AXObjectCache::accessibilityEnabled()) { 764 Node* node = endingSelection.start().deprecatedNode(); 765 if (AXObjectCache* cache = document().existingAXObjectCache()) 766 cache->postNotification(node, AXObjectCache::AXValueChanged, TargetObservableParent); 767 } 768 769 updateMarkersForWordsAffectedByEditing(true); 770 771 if (client()) 772 client()->respondToChangedContents(); 773} 774 775bool Editor::hasBidiSelection() const 776{ 777 if (m_frame.selection().isNone()) 778 return false; 779 780 Node* startNode; 781 if (m_frame.selection().isRange()) { 782 startNode = m_frame.selection().selection().start().downstream().deprecatedNode(); 783 Node* endNode = m_frame.selection().selection().end().upstream().deprecatedNode(); 784 if (enclosingBlock(startNode) != enclosingBlock(endNode)) 785 return false; 786 } else 787 startNode = m_frame.selection().selection().visibleStart().deepEquivalent().deprecatedNode(); 788 789 auto renderer = startNode->renderer(); 790 while (renderer && !renderer->isRenderBlockFlow()) 791 renderer = renderer->parent(); 792 793 if (!renderer) 794 return false; 795 796 if (!renderer->style().isLeftToRightDirection()) 797 return true; 798 799 return toRenderBlockFlow(renderer)->containsNonZeroBidiLevel(); 800} 801 802TriState Editor::selectionUnorderedListState() const 803{ 804 if (m_frame.selection().isCaret()) { 805 if (enclosingElementWithTag(m_frame.selection().selection().start(), ulTag)) 806 return TrueTriState; 807 } else if (m_frame.selection().isRange()) { 808 auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), ulTag); 809 auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), ulTag); 810 if (startNode && endNode && startNode == endNode) 811 return TrueTriState; 812 } 813 814 return FalseTriState; 815} 816 817TriState Editor::selectionOrderedListState() const 818{ 819 if (m_frame.selection().isCaret()) { 820 if (enclosingElementWithTag(m_frame.selection().selection().start(), olTag)) 821 return TrueTriState; 822 } else if (m_frame.selection().isRange()) { 823 auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), olTag); 824 auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), olTag); 825 if (startNode && endNode && startNode == endNode) 826 return TrueTriState; 827 } 828 829 return FalseTriState; 830} 831 832PassRefPtr<Node> Editor::insertOrderedList() 833{ 834 if (!canEditRichly()) 835 return 0; 836 837 RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::OrderedList); 838 revealSelectionAfterEditingOperation(); 839 return newList; 840} 841 842PassRefPtr<Node> Editor::insertUnorderedList() 843{ 844 if (!canEditRichly()) 845 return 0; 846 847 RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::UnorderedList); 848 revealSelectionAfterEditingOperation(); 849 return newList; 850} 851 852bool Editor::canIncreaseSelectionListLevel() 853{ 854 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(&document()); 855} 856 857bool Editor::canDecreaseSelectionListLevel() 858{ 859 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(&document()); 860} 861 862PassRefPtr<Node> Editor::increaseSelectionListLevel() 863{ 864 if (!canEditRichly() || m_frame.selection().isNone()) 865 return 0; 866 867 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(&document()); 868 revealSelectionAfterEditingOperation(); 869 return newList; 870} 871 872PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered() 873{ 874 if (!canEditRichly() || m_frame.selection().isNone()) 875 return 0; 876 877 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(&document()); 878 revealSelectionAfterEditingOperation(); 879 return newList.release(); 880} 881 882PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered() 883{ 884 if (!canEditRichly() || m_frame.selection().isNone()) 885 return 0; 886 887 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(&document()); 888 revealSelectionAfterEditingOperation(); 889 return newList.release(); 890} 891 892void Editor::decreaseSelectionListLevel() 893{ 894 if (!canEditRichly() || m_frame.selection().isNone()) 895 return; 896 897 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(&document()); 898 revealSelectionAfterEditingOperation(); 899} 900 901void Editor::removeFormattingAndStyle() 902{ 903 applyCommand(RemoveFormatCommand::create(document())); 904} 905 906void Editor::clearLastEditCommand() 907{ 908 m_lastEditCommand.clear(); 909} 910#if PLATFORM(IOS) 911// If the selection is adjusted from UIKit without closing the typing, the typing command may 912// have a stale selection. 913void Editor::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping() 914{ 915 TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(&m_frame, m_frame.selection().selection()); 916} 917#endif 918 919// Returns whether caller should continue with "the default processing", which is the same as 920// the event handler NOT setting the return value to false 921bool Editor::dispatchCPPEvent(const AtomicString& eventType, DataTransferAccessPolicy policy) 922{ 923 Node* target = findEventTargetFromSelection(); 924 if (!target) 925 return true; 926 927 RefPtr<DataTransfer> dataTransfer = DataTransfer::createForCopyAndPaste(policy); 928 929 RefPtr<Event> event = ClipboardEvent::create(eventType, true, true, dataTransfer); 930 target->dispatchEvent(event, IGNORE_EXCEPTION); 931 bool noDefaultProcessing = event->defaultPrevented(); 932 if (noDefaultProcessing && policy == DataTransferAccessPolicy::Writable) { 933 OwnPtr<Pasteboard> pasteboard = Pasteboard::createForCopyAndPaste(); 934 pasteboard->clear(); 935 pasteboard->writePasteboard(dataTransfer->pasteboard()); 936 } 937 938 // invalidate dataTransfer here for security 939 dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); 940 941 return !noDefaultProcessing; 942} 943 944Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const 945{ 946 Node* target = selection.start().element(); 947 if (!target) 948 target = document().body(); 949 if (!target) 950 return 0; 951 952 return target; 953} 954 955Node* Editor::findEventTargetFromSelection() const 956{ 957 return findEventTargetFrom(m_frame.selection().selection()); 958} 959 960void Editor::applyStyle(StyleProperties* style, EditAction editingAction) 961{ 962 switch (m_frame.selection().selection().selectionType()) { 963 case VisibleSelection::NoSelection: 964 // do nothing 965 break; 966 case VisibleSelection::CaretSelection: 967 computeAndSetTypingStyle(style, editingAction); 968 break; 969 case VisibleSelection::RangeSelection: 970 if (style) 971 applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).get(), editingAction)); 972 break; 973 } 974} 975 976bool Editor::shouldApplyStyle(StyleProperties* style, Range* range) 977{ 978 return client()->shouldApplyStyle(style, range); 979} 980 981void Editor::applyParagraphStyle(StyleProperties* style, EditAction editingAction) 982{ 983 switch (m_frame.selection().selection().selectionType()) { 984 case VisibleSelection::NoSelection: 985 // do nothing 986 break; 987 case VisibleSelection::CaretSelection: 988 case VisibleSelection::RangeSelection: 989 if (style) 990 applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties)); 991 break; 992 } 993} 994 995void Editor::applyStyleToSelection(StyleProperties* style, EditAction editingAction) 996{ 997 if (!style || style->isEmpty() || !canEditRichly()) 998 return; 999 1000 if (client() && client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get())) 1001 applyStyle(style, editingAction); 1002} 1003 1004void Editor::applyParagraphStyleToSelection(StyleProperties* style, EditAction editingAction) 1005{ 1006 if (!style || style->isEmpty() || !canEditRichly()) 1007 return; 1008 1009 if (client() && client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get())) 1010 applyParagraphStyle(style, editingAction); 1011} 1012 1013bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const 1014{ 1015 return EditingStyle::create(propertyID, value)->triStateOfStyle( 1016 EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor).get()); 1017} 1018 1019TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const 1020{ 1021 return EditingStyle::create(propertyID, value)->triStateOfStyle(m_frame.selection().selection()); 1022} 1023 1024String Editor::selectionStartCSSPropertyValue(CSSPropertyID propertyID) 1025{ 1026 RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), 1027 propertyID == CSSPropertyBackgroundColor); 1028 if (!selectionStyle || !selectionStyle->style()) 1029 return String(); 1030 1031 if (propertyID == CSSPropertyFontSize) 1032 return String::number(selectionStyle->legacyFontSize(&document())); 1033 return selectionStyle->style()->getPropertyValue(propertyID); 1034} 1035 1036void Editor::indent() 1037{ 1038 applyCommand(IndentOutdentCommand::create(document(), IndentOutdentCommand::Indent)); 1039} 1040 1041void Editor::outdent() 1042{ 1043 applyCommand(IndentOutdentCommand::create(document(), IndentOutdentCommand::Outdent)); 1044} 1045 1046static void notifyTextFromControls(Element* startRoot, Element* endRoot) 1047{ 1048 HTMLTextFormControlElement* startingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(startRoot)); 1049 HTMLTextFormControlElement* endingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(endRoot)); 1050 if (startingTextControl) 1051 startingTextControl->didEditInnerTextValue(); 1052 if (endingTextControl && startingTextControl != endingTextControl) 1053 endingTextControl->didEditInnerTextValue(); 1054} 1055 1056static void dispatchEditableContentChangedEvents(PassRefPtr<Element> prpStartRoot, PassRefPtr<Element> prpEndRoot) 1057{ 1058 RefPtr<Element> startRoot = prpStartRoot; 1059 RefPtr<Element> endRoot = prpEndRoot; 1060 if (startRoot) 1061 startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), IGNORE_EXCEPTION); 1062 if (endRoot && endRoot != startRoot) 1063 endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), IGNORE_EXCEPTION); 1064} 1065 1066void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd) 1067{ 1068 document().updateLayout(); 1069 1070 EditCommandComposition* composition = cmd->composition(); 1071 ASSERT(composition); 1072 VisibleSelection newSelection(cmd->endingSelection()); 1073 1074 notifyTextFromControls(composition->startingRootEditableElement(), composition->endingRootEditableElement()); 1075 1076 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. 1077 FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0; 1078 changeSelectionAfterCommand(newSelection, options); 1079 dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement()); 1080 1081 updateEditorUINowIfScheduled(); 1082 1083 m_alternativeTextController->respondToAppliedEditing(cmd.get()); 1084 1085 if (!cmd->preservesTypingStyle()) 1086 m_frame.selection().clearTypingStyle(); 1087 1088 // Command will be equal to last edit command only in the case of typing 1089 if (m_lastEditCommand.get() == cmd) 1090 ASSERT(cmd->isTypingCommand()); 1091 else { 1092 // Only register a new undo command if the command passed in is 1093 // different from the last command 1094 m_lastEditCommand = cmd; 1095 if (client()) 1096 client()->registerUndoStep(m_lastEditCommand->ensureComposition()); 1097 } 1098 1099 respondToChangedContents(newSelection); 1100} 1101 1102void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd) 1103{ 1104 document().updateLayout(); 1105 1106 notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 1107 1108 VisibleSelection newSelection(cmd->startingSelection()); 1109 changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions()); 1110 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 1111 1112 updateEditorUINowIfScheduled(); 1113 1114 m_alternativeTextController->respondToUnappliedEditing(cmd.get()); 1115 1116 m_lastEditCommand = 0; 1117 if (client()) 1118 client()->registerRedoStep(cmd); 1119 respondToChangedContents(newSelection); 1120} 1121 1122void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd) 1123{ 1124 document().updateLayout(); 1125 1126 notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 1127 1128 VisibleSelection newSelection(cmd->endingSelection()); 1129 changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions()); 1130 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement()); 1131 1132 updateEditorUINowIfScheduled(); 1133 1134 m_lastEditCommand = 0; 1135 if (client()) 1136 client()->registerUndoStep(cmd); 1137 respondToChangedContents(newSelection); 1138} 1139 1140Editor::Editor(Frame& frame) 1141 : m_frame(frame) 1142#if ENABLE(DELETION_UI) 1143 , m_deleteButtonController(std::make_unique<DeleteButtonController>(frame)) 1144#endif 1145 , m_ignoreCompositionSelectionChange(false) 1146 , m_shouldStartNewKillRingSequence(false) 1147 // This is off by default, since most editors want this behavior (this matches IE but not FF). 1148 , m_shouldStyleWithCSS(false) 1149 , m_killRing(std::make_unique<KillRing>()) 1150 , m_spellChecker(std::make_unique<SpellChecker>(frame)) 1151 , m_alternativeTextController(std::make_unique<AlternativeTextController>(frame)) 1152 , m_areMarkedTextMatchesHighlighted(false) 1153 , m_defaultParagraphSeparator(EditorParagraphSeparatorIsDiv) 1154 , m_overwriteModeEnabled(false) 1155 , m_editorUIUpdateTimer(this, &Editor::editorUIUpdateTimerFired) 1156 , m_editorUIUpdateTimerShouldCheckSpellingAndGrammar(false) 1157 , m_editorUIUpdateTimerWasTriggeredByDictation(false) 1158#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS) 1159 , m_telephoneNumberDetectionUpdateTimer(this, &Editor::scanSelectionForTelephoneNumbers) 1160#endif 1161{ 1162} 1163 1164Editor::~Editor() 1165{ 1166} 1167 1168void Editor::clear() 1169{ 1170 m_compositionNode = 0; 1171 m_customCompositionUnderlines.clear(); 1172 m_shouldStyleWithCSS = false; 1173 m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv; 1174 1175#if ENABLE(DELETION_UI) 1176 m_deleteButtonController = std::make_unique<DeleteButtonController>(m_frame); 1177#endif 1178} 1179 1180bool Editor::insertText(const String& text, Event* triggeringEvent) 1181{ 1182 return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent); 1183} 1184 1185bool Editor::insertTextForConfirmedComposition(const String& text) 1186{ 1187 return m_frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition); 1188} 1189 1190bool Editor::insertDictatedText(const String& text, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent) 1191{ 1192 return m_alternativeTextController->insertDictatedText(text, dictationAlternatives, triggeringEvent); 1193} 1194 1195bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent) 1196{ 1197 if (text.isEmpty()) 1198 return false; 1199 1200 VisibleSelection selection = selectionForCommand(triggeringEvent); 1201 if (!selection.isContentEditable()) 1202 return false; 1203 RefPtr<Range> range = selection.toNormalizedRange(); 1204 1205 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped)) 1206 return true; 1207 1208 updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0])); 1209 1210 bool shouldConsiderApplyingAutocorrection = false; 1211 if (text == " " || text == "\t") 1212 shouldConsiderApplyingAutocorrection = true; 1213 1214 if (text.length() == 1 && u_ispunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0])) 1215 shouldConsiderApplyingAutocorrection = true; 1216 1217 bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate(); 1218 1219 // Get the selection to use for the event that triggered this insertText. 1220 // If the event handler changed the selection, we may want to use a different selection 1221 // that is contained in the event target. 1222 selection = selectionForCommand(triggeringEvent); 1223 if (selection.isContentEditable()) { 1224 if (Node* selectionStart = selection.start().deprecatedNode()) { 1225 Ref<Document> document(selectionStart->document()); 1226 1227 // Insert the text 1228 if (triggeringEvent && triggeringEvent->isDictation()) 1229 DictationCommand::insertText(&document.get(), text, triggeringEvent->dictationAlternatives(), selection); 1230 else { 1231 TypingCommand::Options options = 0; 1232 if (selectInsertedText) 1233 options |= TypingCommand::SelectInsertedText; 1234 if (autocorrectionWasApplied) 1235 options |= TypingCommand::RetainAutocorrectionIndicator; 1236 TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); 1237 } 1238 1239 // Reveal the current selection 1240 if (Frame* editedFrame = document->frame()) 1241 if (Page* page = editedFrame->page()) 1242 page->focusController().focusedOrMainFrame().selection().revealSelection(ScrollAlignment::alignCenterIfNeeded); 1243 } 1244 } 1245 1246 return true; 1247} 1248 1249bool Editor::insertLineBreak() 1250{ 1251 if (!canEdit()) 1252 return false; 1253 1254 if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped)) 1255 return true; 1256 1257 VisiblePosition caret = m_frame.selection().selection().visibleStart(); 1258 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); 1259 bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate(); 1260 TypingCommand::insertLineBreak(document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); 1261 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); 1262 1263 return true; 1264} 1265 1266bool Editor::insertParagraphSeparator() 1267{ 1268 if (!canEdit()) 1269 return false; 1270 1271 if (!canEditRichly()) 1272 return insertLineBreak(); 1273 1274 if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped)) 1275 return true; 1276 1277 VisiblePosition caret = m_frame.selection().selection().visibleStart(); 1278 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); 1279 bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate(); 1280 TypingCommand::insertParagraphSeparator(document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); 1281 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); 1282 1283 return true; 1284} 1285 1286void Editor::cut() 1287{ 1288 if (tryDHTMLCut()) 1289 return; // DHTML did the whole operation 1290 if (!canCut()) { 1291 systemBeep(); 1292 return; 1293 } 1294 1295 performCutOrCopy(CutAction); 1296} 1297 1298void Editor::copy() 1299{ 1300 if (tryDHTMLCopy()) 1301 return; // DHTML did the whole operation 1302 if (!canCopy()) { 1303 systemBeep(); 1304 return; 1305 } 1306 1307 performCutOrCopy(CopyAction); 1308} 1309 1310void Editor::performCutOrCopy(EditorActionSpecifier action) 1311{ 1312 RefPtr<Range> selection = selectedRange(); 1313 willWriteSelectionToPasteboard(selection); 1314 if (action == CutAction) { 1315 if (!shouldDeleteRange(selection.get())) 1316 return; 1317 1318 updateMarkersForWordsAffectedByEditing(true); 1319 } 1320 1321 if (enclosingTextFormControl(m_frame.selection().selection().start())) 1322 Pasteboard::createForCopyAndPaste()->writePlainText(selectedTextForDataTransfer(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace); 1323 else { 1324 HTMLImageElement* imageElement = nullptr; 1325 if (action == CopyAction) 1326 imageElement = imageElementFromImageDocument(document()); 1327 1328 if (imageElement) { 1329#if PLATFORM(COCOA) || PLATFORM(EFL) 1330 writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *imageElement, document().url(), document().title()); 1331#else 1332 Pasteboard::createForCopyAndPaste()->writeImage(*imageElement, document().url(), document().title()); 1333#endif 1334 } else { 1335#if PLATFORM(COCOA) || PLATFORM(EFL) 1336 writeSelectionToPasteboard(*Pasteboard::createForCopyAndPaste()); 1337#else 1338 // FIXME: Convert all other platforms to match Mac and delete this. 1339 Pasteboard::createForCopyAndPaste()->writeSelection(*selection, canSmartCopyOrDelete(), m_frame, IncludeImageAltTextForDataTransfer); 1340#endif 1341 } 1342 } 1343 1344 didWriteSelectionToPasteboard(); 1345 if (action == CutAction) 1346 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 1347} 1348 1349void Editor::paste() 1350{ 1351 paste(*Pasteboard::createForCopyAndPaste()); 1352} 1353 1354void Editor::paste(Pasteboard& pasteboard) 1355{ 1356 if (tryDHTMLPaste()) 1357 return; // DHTML did the whole operation 1358 if (!canPaste()) 1359 return; 1360 updateMarkersForWordsAffectedByEditing(false); 1361 CachedResourceLoader* loader = document().cachedResourceLoader(); 1362 ResourceCacheValidationSuppressor validationSuppressor(loader); 1363 if (m_frame.selection().selection().isContentRichlyEditable()) 1364 pasteWithPasteboard(&pasteboard, true); 1365 else 1366 pasteAsPlainTextWithPasteboard(pasteboard); 1367} 1368 1369void Editor::pasteAsPlainText() 1370{ 1371 if (tryDHTMLPaste()) 1372 return; 1373 if (!canPaste()) 1374 return; 1375 updateMarkersForWordsAffectedByEditing(false); 1376 pasteAsPlainTextWithPasteboard(*Pasteboard::createForCopyAndPaste()); 1377} 1378 1379void Editor::performDelete() 1380{ 1381 if (!canDelete()) { 1382 systemBeep(); 1383 return; 1384 } 1385 1386 addToKillRing(selectedRange().get(), false); 1387 deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); 1388 1389 // clear the "start new kill ring sequence" setting, because it was set to true 1390 // when the selection was updated by deleting the range 1391 setStartNewKillRingSequence(false); 1392} 1393 1394void Editor::simplifyMarkup(Node* startNode, Node* endNode) 1395{ 1396 if (!startNode) 1397 return; 1398 if (endNode) { 1399 if (&startNode->document() != &endNode->document()) 1400 return; 1401 // check if start node is before endNode 1402 Node* node = startNode; 1403 while (node && node != endNode) 1404 node = NodeTraversal::next(node); 1405 if (!node) 1406 return; 1407 } 1408 1409 applyCommand(SimplifyMarkupCommand::create(document(), startNode, (endNode) ? NodeTraversal::next(endNode) : 0)); 1410} 1411 1412void Editor::copyURL(const URL& url, const String& title) 1413{ 1414 copyURL(url, title, *Pasteboard::createForCopyAndPaste()); 1415} 1416 1417void Editor::copyURL(const URL& url, const String& title, Pasteboard& pasteboard) 1418{ 1419 PasteboardURL pasteboardURL; 1420 pasteboardURL.url = url; 1421 pasteboardURL.title = title; 1422 1423#if PLATFORM(MAC) 1424 fillInUserVisibleForm(pasteboardURL); 1425#endif 1426 1427 pasteboard.write(pasteboardURL); 1428} 1429 1430#if !PLATFORM(IOS) 1431void Editor::copyImage(const HitTestResult& result) 1432{ 1433 Element* element = result.innerNonSharedElement(); 1434 if (!element) 1435 return; 1436 1437 URL url = result.absoluteLinkURL(); 1438 if (url.isEmpty()) 1439 url = result.absoluteImageURL(); 1440 1441#if PLATFORM(COCOA) || PLATFORM(EFL) 1442 writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *element, url, result.altDisplayString()); 1443#else 1444 Pasteboard::createForCopyAndPaste()->writeImage(*element, url, result.altDisplayString()); 1445#endif 1446} 1447#endif 1448 1449bool Editor::isContinuousSpellCheckingEnabled() const 1450{ 1451 return client() && client()->isContinuousSpellCheckingEnabled(); 1452} 1453 1454void Editor::toggleContinuousSpellChecking() 1455{ 1456 if (client()) 1457 client()->toggleContinuousSpellChecking(); 1458} 1459 1460bool Editor::isGrammarCheckingEnabled() 1461{ 1462 return client() && client()->isGrammarCheckingEnabled(); 1463} 1464 1465void Editor::toggleGrammarChecking() 1466{ 1467 if (client()) 1468 client()->toggleGrammarChecking(); 1469} 1470 1471int Editor::spellCheckerDocumentTag() 1472{ 1473 return client() ? client()->spellCheckerDocumentTag() : 0; 1474} 1475 1476#if USE(APPKIT) 1477 1478void Editor::uppercaseWord() 1479{ 1480 if (client()) 1481 client()->uppercaseWord(); 1482} 1483 1484void Editor::lowercaseWord() 1485{ 1486 if (client()) 1487 client()->lowercaseWord(); 1488} 1489 1490void Editor::capitalizeWord() 1491{ 1492 if (client()) 1493 client()->capitalizeWord(); 1494} 1495 1496#endif 1497 1498#if USE(AUTOMATIC_TEXT_REPLACEMENT) 1499 1500void Editor::showSubstitutionsPanel() 1501{ 1502 if (!client()) { 1503 LOG_ERROR("No NSSpellChecker"); 1504 return; 1505 } 1506 1507 if (client()->substitutionsPanelIsShowing()) { 1508 client()->showSubstitutionsPanel(false); 1509 return; 1510 } 1511 client()->showSubstitutionsPanel(true); 1512} 1513 1514bool Editor::substitutionsPanelIsShowing() 1515{ 1516 if (!client()) 1517 return false; 1518 return client()->substitutionsPanelIsShowing(); 1519} 1520 1521void Editor::toggleSmartInsertDelete() 1522{ 1523 if (client()) 1524 client()->toggleSmartInsertDelete(); 1525} 1526 1527bool Editor::isAutomaticQuoteSubstitutionEnabled() 1528{ 1529 return client() && client()->isAutomaticQuoteSubstitutionEnabled(); 1530} 1531 1532void Editor::toggleAutomaticQuoteSubstitution() 1533{ 1534 if (client()) 1535 client()->toggleAutomaticQuoteSubstitution(); 1536} 1537 1538bool Editor::isAutomaticLinkDetectionEnabled() 1539{ 1540 return client() && client()->isAutomaticLinkDetectionEnabled(); 1541} 1542 1543void Editor::toggleAutomaticLinkDetection() 1544{ 1545 if (client()) 1546 client()->toggleAutomaticLinkDetection(); 1547} 1548 1549bool Editor::isAutomaticDashSubstitutionEnabled() 1550{ 1551 return client() && client()->isAutomaticDashSubstitutionEnabled(); 1552} 1553 1554void Editor::toggleAutomaticDashSubstitution() 1555{ 1556 if (client()) 1557 client()->toggleAutomaticDashSubstitution(); 1558} 1559 1560bool Editor::isAutomaticTextReplacementEnabled() 1561{ 1562 return client() && client()->isAutomaticTextReplacementEnabled(); 1563} 1564 1565void Editor::toggleAutomaticTextReplacement() 1566{ 1567 if (client()) 1568 client()->toggleAutomaticTextReplacement(); 1569} 1570 1571bool Editor::isAutomaticSpellingCorrectionEnabled() 1572{ 1573 return m_alternativeTextController->isAutomaticSpellingCorrectionEnabled(); 1574} 1575 1576void Editor::toggleAutomaticSpellingCorrection() 1577{ 1578 if (client()) 1579 client()->toggleAutomaticSpellingCorrection(); 1580} 1581 1582#endif 1583 1584bool Editor::shouldEndEditing(Range* range) 1585{ 1586 return client() && client()->shouldEndEditing(range); 1587} 1588 1589bool Editor::shouldBeginEditing(Range* range) 1590{ 1591 return client() && client()->shouldBeginEditing(range); 1592} 1593 1594void Editor::clearUndoRedoOperations() 1595{ 1596 if (client()) 1597 client()->clearUndoRedoOperations(); 1598} 1599 1600bool Editor::canUndo() 1601{ 1602 return client() && client()->canUndo(); 1603} 1604 1605void Editor::undo() 1606{ 1607 if (client()) 1608 client()->undo(); 1609} 1610 1611bool Editor::canRedo() 1612{ 1613 return client() && client()->canRedo(); 1614} 1615 1616void Editor::redo() 1617{ 1618 if (client()) 1619 client()->redo(); 1620} 1621 1622void Editor::didBeginEditing() 1623{ 1624 if (client()) 1625 client()->didBeginEditing(); 1626} 1627 1628void Editor::didEndEditing() 1629{ 1630 if (client()) 1631 client()->didEndEditing(); 1632} 1633 1634void Editor::willWriteSelectionToPasteboard(PassRefPtr<Range> range) 1635{ 1636 if (client()) 1637 client()->willWriteSelectionToPasteboard(range.get()); 1638} 1639 1640void Editor::didWriteSelectionToPasteboard() 1641{ 1642 if (client()) 1643 client()->didWriteSelectionToPasteboard(); 1644} 1645 1646void Editor::toggleBold() 1647{ 1648 command("ToggleBold").execute(); 1649} 1650 1651void Editor::toggleUnderline() 1652{ 1653 command("ToggleUnderline").execute(); 1654} 1655 1656void Editor::setBaseWritingDirection(WritingDirection direction) 1657{ 1658#if PLATFORM(IOS) 1659 if (inSameParagraph(m_frame.selection().selection().visibleStart(), m_frame.selection().selection().visibleEnd()) && 1660 baseWritingDirectionForSelectionStart() == direction) 1661 return; 1662#endif 1663 1664 Element* focusedElement = document().focusedElement(); 1665 if (focusedElement && isHTMLTextFormControlElement(*focusedElement)) { 1666 if (direction == NaturalWritingDirection) 1667 return; 1668 toHTMLElement(focusedElement)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl"); 1669 focusedElement->dispatchInputEvent(); 1670 document().updateStyleIfNeeded(); 1671 return; 1672 } 1673 1674 RefPtr<MutableStyleProperties> style = MutableStyleProperties::create(); 1675 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false); 1676 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection); 1677} 1678 1679WritingDirection Editor::baseWritingDirectionForSelectionStart() const 1680{ 1681 WritingDirection result = LeftToRightWritingDirection; 1682 1683 Position pos = m_frame.selection().selection().visibleStart().deepEquivalent(); 1684 Node* node = pos.deprecatedNode(); 1685 if (!node) 1686 return result; 1687 1688 auto renderer = node->renderer(); 1689 if (!renderer) 1690 return result; 1691 1692 if (!renderer->isRenderBlockFlow()) { 1693 renderer = renderer->containingBlock(); 1694 if (!renderer) 1695 return result; 1696 } 1697 1698 switch (renderer->style().direction()) { 1699 case LTR: 1700 return LeftToRightWritingDirection; 1701 case RTL: 1702 return RightToLeftWritingDirection; 1703 } 1704 1705 return result; 1706} 1707 1708void Editor::selectComposition() 1709{ 1710 RefPtr<Range> range = compositionRange(); 1711 if (!range) 1712 return; 1713 1714 // The composition can start inside a composed character sequence, so we have to override checks. 1715 // See <http://bugs.webkit.org/show_bug.cgi?id=15781> 1716 VisibleSelection selection; 1717 selection.setWithoutValidation(range->startPosition(), range->endPosition()); 1718 m_frame.selection().setSelection(selection, 0); 1719} 1720 1721void Editor::confirmComposition() 1722{ 1723 if (!m_compositionNode) 1724 return; 1725 setComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition); 1726} 1727 1728void Editor::cancelComposition() 1729{ 1730 if (!m_compositionNode) 1731 return; 1732 setComposition(emptyString(), CancelComposition); 1733} 1734 1735bool Editor::cancelCompositionIfSelectionIsInvalid() 1736{ 1737 unsigned start; 1738 unsigned end; 1739 if (!hasComposition() || ignoreCompositionSelectionChange() || getCompositionSelection(start, end)) 1740 return false; 1741 1742 cancelComposition(); 1743 return true; 1744} 1745 1746void Editor::confirmComposition(const String& text) 1747{ 1748 setComposition(text, ConfirmComposition); 1749} 1750 1751void Editor::setComposition(const String& text, SetCompositionMode mode) 1752{ 1753 ASSERT(mode == ConfirmComposition || mode == CancelComposition); 1754 UserTypingGestureIndicator typingGestureIndicator(m_frame); 1755 1756 setIgnoreCompositionSelectionChange(true); 1757 1758 if (mode == CancelComposition) 1759 ASSERT(text == emptyString()); 1760 else 1761 selectComposition(); 1762 1763 if (m_frame.selection().isNone()) { 1764 setIgnoreCompositionSelectionChange(false); 1765 return; 1766 } 1767 1768 // Dispatch a compositionend event to the focused node. 1769 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of 1770 // the DOM Event specification. 1771 if (Element* target = document().focusedElement()) { 1772 RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text); 1773 target->dispatchEvent(event.release(), IGNORE_EXCEPTION); 1774 } 1775 1776 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input 1777 // will delete the old composition with an optimized replace operation. 1778 if (text.isEmpty() && mode != CancelComposition) 1779 TypingCommand::deleteSelection(document(), 0); 1780 1781 m_compositionNode = 0; 1782 m_customCompositionUnderlines.clear(); 1783 1784 insertTextForConfirmedComposition(text); 1785 1786 if (mode == CancelComposition) { 1787 // An open typing command that disagrees about current selection would cause issues with typing later on. 1788 TypingCommand::closeTyping(&m_frame); 1789 } 1790 1791 setIgnoreCompositionSelectionChange(false); 1792} 1793 1794void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) 1795{ 1796 UserTypingGestureIndicator typingGestureIndicator(m_frame); 1797 1798 setIgnoreCompositionSelectionChange(true); 1799 1800 // Updates styles before setting selection for composition to prevent 1801 // inserting the previous composition text into text nodes oddly. 1802 // See https://bugs.webkit.org/show_bug.cgi?id=46868 1803 document().updateStyleIfNeeded(); 1804 1805 selectComposition(); 1806 1807 if (m_frame.selection().isNone()) { 1808 setIgnoreCompositionSelectionChange(false); 1809 return; 1810 } 1811 1812#if PLATFORM(IOS) 1813 client()->startDelayingAndCoalescingContentChangeNotifications(); 1814#endif 1815 1816 Element* target = document().focusedElement(); 1817 if (target) { 1818 // Dispatch an appropriate composition event to the focused node. 1819 // We check the composition status and choose an appropriate composition event since this 1820 // function is used for three purposes: 1821 // 1. Starting a new composition. 1822 // Send a compositionstart and a compositionupdate event when this function creates 1823 // a new composition node, i.e. 1824 // m_compositionNode == 0 && !text.isEmpty(). 1825 // Sending a compositionupdate event at this time ensures that at least one 1826 // compositionupdate event is dispatched. 1827 // 2. Updating the existing composition node. 1828 // Send a compositionupdate event when this function updates the existing composition 1829 // node, i.e. m_compositionNode != 0 && !text.isEmpty(). 1830 // 3. Canceling the ongoing composition. 1831 // Send a compositionend event when function deletes the existing composition node, i.e. 1832 // m_compositionNode != 0 && test.isEmpty(). 1833 RefPtr<CompositionEvent> event; 1834 if (!m_compositionNode) { 1835 // We should send a compositionstart event only when the given text is not empty because this 1836 // function doesn't create a composition node when the text is empty. 1837 if (!text.isEmpty()) { 1838 target->dispatchEvent(CompositionEvent::create(eventNames().compositionstartEvent, document().domWindow(), selectedText())); 1839 event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text); 1840 } 1841 } else { 1842 if (!text.isEmpty()) 1843 event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text); 1844 else 1845 event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text); 1846 } 1847 if (event.get()) 1848 target->dispatchEvent(event, IGNORE_EXCEPTION); 1849 } 1850 1851 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input 1852 // will delete the old composition with an optimized replace operation. 1853 if (text.isEmpty()) 1854 TypingCommand::deleteSelection(document(), TypingCommand::PreventSpellChecking); 1855 1856 m_compositionNode = 0; 1857 m_customCompositionUnderlines.clear(); 1858 1859 if (!text.isEmpty()) { 1860 TypingCommand::insertText(document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); 1861 1862 // Find out what node has the composition now. 1863 Position base = m_frame.selection().selection().base().downstream(); 1864 Position extent = m_frame.selection().selection().extent(); 1865 Node* baseNode = base.deprecatedNode(); 1866 unsigned baseOffset = base.deprecatedEditingOffset(); 1867 Node* extentNode = extent.deprecatedNode(); 1868 unsigned extentOffset = extent.deprecatedEditingOffset(); 1869 1870 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) { 1871 m_compositionNode = toText(baseNode); 1872 m_compositionStart = baseOffset; 1873 m_compositionEnd = extentOffset; 1874 m_customCompositionUnderlines = underlines; 1875 size_t numUnderlines = m_customCompositionUnderlines.size(); 1876 for (size_t i = 0; i < numUnderlines; ++i) { 1877 m_customCompositionUnderlines[i].startOffset += baseOffset; 1878 m_customCompositionUnderlines[i].endOffset += baseOffset; 1879 } 1880 if (baseNode->renderer()) 1881 baseNode->renderer()->repaint(); 1882 1883 unsigned start = std::min(baseOffset + selectionStart, extentOffset); 1884 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset); 1885 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); 1886 m_frame.selection().setSelectedRange(selectedRange.get(), DOWNSTREAM, false); 1887 } 1888 } 1889 1890 setIgnoreCompositionSelectionChange(false); 1891 1892#if PLATFORM(IOS) 1893 client()->stopDelayingAndCoalescingContentChangeNotifications(); 1894#endif 1895} 1896 1897void Editor::ignoreSpelling() 1898{ 1899 if (!client()) 1900 return; 1901 1902 RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange(); 1903 if (selectedRange) 1904 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 1905 1906 String text = selectedText(); 1907 ASSERT(text.length()); 1908 textChecker()->ignoreWordInSpellDocument(text); 1909} 1910 1911void Editor::learnSpelling() 1912{ 1913 if (!client()) 1914 return; 1915 1916 // FIXME: On Mac OS X, when use "learn" button on "Spelling and Grammar" panel, we don't call this function. It should remove misspelling markers around the learned word, see <rdar://problem/5396072>. 1917 1918 RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange(); 1919 if (selectedRange) 1920 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 1921 1922 String text = selectedText(); 1923 ASSERT(text.length()); 1924 textChecker()->learnWord(text); 1925} 1926 1927#if !PLATFORM(IOS) 1928void Editor::advanceToNextMisspelling(bool startBeforeSelection) 1929{ 1930 // The basic approach is to search in two phases - from the selection end to the end of the doc, and 1931 // then we wrap and search from the doc start to (approximately) where we started. 1932 1933 // Start at the end of the selection, search to edge of document. Starting at the selection end makes 1934 // repeated "check spelling" commands work. 1935 VisibleSelection selection(m_frame.selection().selection()); 1936 RefPtr<Range> spellingSearchRange(rangeOfContents(document())); 1937 1938 bool startedWithSelection = false; 1939 if (selection.start().deprecatedNode()) { 1940 startedWithSelection = true; 1941 if (startBeforeSelection) { 1942 VisiblePosition start(selection.visibleStart()); 1943 // We match AppKit's rule: Start 1 character before the selection. 1944 VisiblePosition oneBeforeStart = start.previous(); 1945 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start); 1946 } else 1947 setStart(spellingSearchRange.get(), selection.visibleEnd()); 1948 } 1949 1950 Position position = spellingSearchRange->startPosition(); 1951 if (!isEditablePosition(position)) { 1952 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the 1953 // selection is editable. 1954 // This can happen in Mail for a mix of non-editable and editable content (like Stationary), 1955 // when spell checking the whole document before sending the message. 1956 // In that case the document might not be editable, but there are editable pockets that need to be spell checked. 1957 1958 position = firstEditablePositionAfterPositionInRoot(position, document().documentElement()).deepEquivalent(); 1959 if (position.isNull()) 1960 return; 1961 1962 Position rangeCompliantPosition = position.parentAnchoredEquivalent(); 1963 spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), IGNORE_EXCEPTION); 1964 startedWithSelection = false; // won't need to wrap 1965 } 1966 1967 // topNode defines the whole range we want to operate on 1968 Node* topNode = highestEditableRoot(position); 1969 // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>) 1970 spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), IGNORE_EXCEPTION); 1971 1972 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking 1973 // at a word boundary. Going back by one char and then forward by a word does the trick. 1974 if (startedWithSelection) { 1975 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous(); 1976 if (oneBeforeStart.isNotNull()) 1977 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart)); 1978 // else we were already at the start of the editable node 1979 } 1980 1981 if (spellingSearchRange->collapsed(IGNORE_EXCEPTION)) 1982 return; // nothing to search in 1983 1984 // Get the spell checker if it is available 1985 if (!client()) 1986 return; 1987 1988 // We go to the end of our first range instead of the start of it, just to be sure 1989 // we don't get foiled by any word boundary problems at the start. It means we might 1990 // do a tiny bit more searching. 1991 Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer(); 1992 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(); 1993 1994 int misspellingOffset = 0; 1995 GrammarDetail grammarDetail; 1996 int grammarPhraseOffset = 0; 1997 RefPtr<Range> grammarSearchRange; 1998 String badGrammarPhrase; 1999 String misspelledWord; 2000 2001 bool isSpelling = true; 2002 int foundOffset = 0; 2003 String foundItem; 2004 RefPtr<Range> firstMisspellingRange; 2005 if (unifiedTextCheckerEnabled()) { 2006 grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION); 2007 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); 2008 if (isSpelling) { 2009 misspelledWord = foundItem; 2010 misspellingOffset = foundOffset; 2011 } else { 2012 badGrammarPhrase = foundItem; 2013 grammarPhraseOffset = foundOffset; 2014 } 2015 } else { 2016 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); 2017 2018#if USE(GRAMMAR_CHECKING) 2019 grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION); 2020 if (!misspelledWord.isEmpty()) { 2021 // Stop looking at start of next misspelled word 2022 CharacterIterator chars(*grammarSearchRange); 2023 chars.advance(misspellingOffset); 2024 grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION); 2025 } 2026 2027 if (isGrammarCheckingEnabled()) 2028 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); 2029#endif 2030 } 2031 2032 // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the 2033 // block rather than at a selection). 2034 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { 2035 spellingSearchRange->setStart(topNode, 0, IGNORE_EXCEPTION); 2036 // going until the end of the very first chunk we tested is far enough 2037 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, IGNORE_EXCEPTION); 2038 2039 if (unifiedTextCheckerEnabled()) { 2040 grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION); 2041 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); 2042 if (isSpelling) { 2043 misspelledWord = foundItem; 2044 misspellingOffset = foundOffset; 2045 } else { 2046 badGrammarPhrase = foundItem; 2047 grammarPhraseOffset = foundOffset; 2048 } 2049 } else { 2050 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange); 2051 2052#if USE(GRAMMAR_CHECKING) 2053 grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION); 2054 if (!misspelledWord.isEmpty()) { 2055 // Stop looking at start of next misspelled word 2056 CharacterIterator chars(*grammarSearchRange); 2057 chars.advance(misspellingOffset); 2058 grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION); 2059 } 2060 2061 if (isGrammarCheckingEnabled()) 2062 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false); 2063#endif 2064 } 2065 } 2066 2067#if !USE(GRAMMAR_CHECKING) 2068 ASSERT(badGrammarPhrase.isEmpty()); 2069 UNUSED_PARAM(grammarPhraseOffset); 2070#else 2071 if (!badGrammarPhrase.isEmpty()) { 2072 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar 2073 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling 2074 // panel, and store a marker so we draw the green squiggle later. 2075 2076 ASSERT(badGrammarPhrase.length() > 0); 2077 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0); 2078 2079 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph 2080 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length); 2081 m_frame.selection().setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY)); 2082 m_frame.selection().revealSelection(); 2083 2084 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail); 2085 document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription); 2086 } else 2087#endif 2088 if (!misspelledWord.isEmpty()) { 2089 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store 2090 // a marker so we draw the red squiggle later. 2091 2092 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length()); 2093 m_frame.selection().setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM)); 2094 m_frame.selection().revealSelection(); 2095 2096 client()->updateSpellingUIWithMisspelledWord(misspelledWord); 2097 document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling); 2098 } 2099} 2100#endif // !PLATFORM(IOS) 2101 2102String Editor::misspelledWordAtCaretOrRange(Node* clickedNode) const 2103{ 2104 if (!isContinuousSpellCheckingEnabled() || !clickedNode || !isSpellCheckingEnabledFor(clickedNode)) 2105 return String(); 2106 2107 VisibleSelection selection = m_frame.selection().selection(); 2108 if (!selection.isContentEditable() || selection.isNone()) 2109 return String(); 2110 2111 VisibleSelection wordSelection(selection.base()); 2112 wordSelection.expandUsingGranularity(WordGranularity); 2113 RefPtr<Range> wordRange = wordSelection.toNormalizedRange(); 2114 2115 // In compliance with GTK+ applications, additionally allow to provide suggestions when the current 2116 // selection exactly match the word selection. 2117 if (selection.isRange() && !areRangesEqual(wordRange.get(), selection.toNormalizedRange().get())) 2118 return String(); 2119 2120 String word = wordRange->text(); 2121 if (word.isEmpty() || !client()) 2122 return String(); 2123 2124 int wordLength = word.length(); 2125 int misspellingLocation = -1; 2126 int misspellingLength = 0; 2127 textChecker()->checkSpellingOfString(word, &misspellingLocation, &misspellingLength); 2128 2129 return misspellingLength == wordLength ? word : String(); 2130} 2131 2132String Editor::misspelledSelectionString() const 2133{ 2134 String selectedString = selectedText(); 2135 int length = selectedString.length(); 2136 if (!length || !client()) 2137 return String(); 2138 2139 int misspellingLocation = -1; 2140 int misspellingLength = 0; 2141 textChecker()->checkSpellingOfString(selectedString, &misspellingLocation, &misspellingLength); 2142 2143 // The selection only counts as misspelled if the selected text is exactly one misspelled word 2144 if (misspellingLength != length) 2145 return String(); 2146 2147 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen). 2148 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work 2149 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling 2150 // or a grammar error. 2151 client()->updateSpellingUIWithMisspelledWord(selectedString); 2152 2153 return selectedString; 2154} 2155 2156bool Editor::isSelectionUngrammatical() 2157{ 2158#if USE(GRAMMAR_CHECKING) 2159 RefPtr<Range> range = m_frame.selection().toNormalizedRange(); 2160 if (!range) 2161 return false; 2162 return TextCheckingHelper(client(), range).isUngrammatical(); 2163#else 2164 return false; 2165#endif 2166} 2167 2168Vector<String> Editor::guessesForMisspelledWord(const String& word) const 2169{ 2170 ASSERT(word.length()); 2171 2172 Vector<String> guesses; 2173 if (client()) 2174 textChecker()->getGuessesForWord(word, String(), guesses); 2175 return guesses; 2176} 2177 2178Vector<String> Editor::guessesForMisspelledOrUngrammatical(bool& misspelled, bool& ungrammatical) 2179{ 2180 if (unifiedTextCheckerEnabled()) { 2181 RefPtr<Range> range; 2182 VisibleSelection selection = m_frame.selection().selection(); 2183 if (selection.isCaret() && behavior().shouldAllowSpellingSuggestionsWithoutSelection()) { 2184 VisibleSelection wordSelection = VisibleSelection(selection.base()); 2185 wordSelection.expandUsingGranularity(WordGranularity); 2186 range = wordSelection.toNormalizedRange(); 2187 } else 2188 range = selection.toNormalizedRange(); 2189 if (!range) 2190 return Vector<String>(); 2191 return TextCheckingHelper(client(), range).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical); 2192 } 2193 2194 String misspelledWord = behavior().shouldAllowSpellingSuggestionsWithoutSelection() ? misspelledWordAtCaretOrRange(document().focusedElement()) : misspelledSelectionString(); 2195 misspelled = !misspelledWord.isEmpty(); 2196 // Only unified text checker supports guesses for ungrammatical phrases. 2197 ungrammatical = false; 2198 2199 if (misspelled) 2200 return guessesForMisspelledWord(misspelledWord); 2201 return Vector<String>(); 2202} 2203 2204void Editor::showSpellingGuessPanel() 2205{ 2206 if (!client()) { 2207 LOG_ERROR("No NSSpellChecker"); 2208 return; 2209 } 2210 2211 if (client()->spellingUIIsShowing()) { 2212 client()->showSpellingUI(false); 2213 return; 2214 } 2215 2216#if !PLATFORM(IOS) 2217 advanceToNextMisspelling(true); 2218#endif 2219 client()->showSpellingUI(true); 2220} 2221 2222bool Editor::spellingPanelIsShowing() 2223{ 2224 if (!client()) 2225 return false; 2226 return client()->spellingUIIsShowing(); 2227} 2228 2229void Editor::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection) 2230{ 2231 RefPtr<Range> selectedRange = movingSelection.toNormalizedRange(); 2232 if (selectedRange) { 2233 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling); 2234 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Grammar); 2235 } 2236} 2237 2238void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection) 2239{ 2240 markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled(), movingSelection); 2241} 2242 2243void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement) 2244{ 2245#if PLATFORM(IOS) 2246 UNUSED_PARAM(selectionAfterTyping); 2247 UNUSED_PARAM(doReplacement); 2248 TextCheckingTypeMask textCheckingOptions = 0; 2249 if (isContinuousSpellCheckingEnabled()) 2250 textCheckingOptions |= TextCheckingTypeSpelling; 2251 if (!(textCheckingOptions & TextCheckingTypeSpelling)) 2252 return; 2253 2254 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)); 2255 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get()); 2256#else 2257#if !USE(AUTOMATIC_TEXT_REPLACEMENT) 2258 UNUSED_PARAM(doReplacement); 2259#endif 2260 2261 if (unifiedTextCheckerEnabled()) { 2262 m_alternativeTextController->applyPendingCorrection(selectionAfterTyping); 2263 2264 TextCheckingTypeMask textCheckingOptions = 0; 2265 2266 if (isContinuousSpellCheckingEnabled()) 2267 textCheckingOptions |= TextCheckingTypeSpelling; 2268 2269#if USE(AUTOMATIC_TEXT_REPLACEMENT) 2270 if (doReplacement 2271 && (isAutomaticQuoteSubstitutionEnabled() 2272 || isAutomaticLinkDetectionEnabled() 2273 || isAutomaticDashSubstitutionEnabled() 2274 || isAutomaticTextReplacementEnabled() 2275 || ((textCheckingOptions & TextCheckingTypeSpelling) && isAutomaticSpellingCorrectionEnabled()))) 2276 textCheckingOptions |= TextCheckingTypeReplacement; 2277#endif 2278 if (!(textCheckingOptions & (TextCheckingTypeSpelling | TextCheckingTypeReplacement))) 2279 return; 2280 2281 if (isGrammarCheckingEnabled()) 2282 textCheckingOptions |= TextCheckingTypeGrammar; 2283 2284 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)); 2285 if (textCheckingOptions & TextCheckingTypeGrammar) { 2286 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)); 2287 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get()); 2288 } else 2289 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get()); 2290 return; 2291 } 2292 2293 if (!isContinuousSpellCheckingEnabled()) 2294 return; 2295 2296 // Check spelling of one word 2297 RefPtr<Range> misspellingRange; 2298 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange); 2299 2300 // Autocorrect the misspelled word. 2301 if (!misspellingRange) 2302 return; 2303 2304 // Get the misspelled word. 2305 const String misspelledWord = plainText(misspellingRange.get()); 2306 String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord); 2307 2308 // If autocorrected word is non empty, replace the misspelled word by this word. 2309 if (!autocorrectedString.isEmpty()) { 2310 VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM); 2311 if (newSelection != m_frame.selection().selection()) { 2312 if (!m_frame.selection().shouldChangeSelection(newSelection)) 2313 return; 2314 m_frame.selection().setSelection(newSelection); 2315 } 2316 2317 if (!m_frame.editor().shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped)) 2318 return; 2319 m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false); 2320 2321 // Reset the charet one character further. 2322 m_frame.selection().moveTo(m_frame.selection().selection().end()); 2323 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity); 2324 } 2325 2326 if (!isGrammarCheckingEnabled()) 2327 return; 2328 2329 // Check grammar of entire sentence 2330 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart))); 2331#endif 2332} 2333 2334void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange) 2335{ 2336#if !PLATFORM(IOS) 2337 // This function is called with a selection already expanded to word boundaries. 2338 // Might be nice to assert that here. 2339 2340 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that 2341 // grammar checking can only be on if spell checking is also on. 2342 if (!isContinuousSpellCheckingEnabled()) 2343 return; 2344 2345 RefPtr<Range> searchRange(selection.toNormalizedRange()); 2346 if (!searchRange) 2347 return; 2348 2349 // If we're not in an editable node, bail. 2350 Node* editableNode = searchRange->startContainer(); 2351 if (!editableNode || !editableNode->hasEditableStyle()) 2352 return; 2353 2354 if (!isSpellCheckingEnabledFor(editableNode)) 2355 return; 2356 2357 // Get the spell checker if it is available 2358 if (!client()) 2359 return; 2360 2361 TextCheckingHelper checker(client(), searchRange); 2362 if (checkSpelling) 2363 checker.markAllMisspellings(firstMisspellingRange); 2364 else { 2365#if USE(GRAMMAR_CHECKING) 2366 if (isGrammarCheckingEnabled()) 2367 checker.markAllBadGrammar(); 2368#else 2369 ASSERT_NOT_REACHED(); 2370#endif 2371 } 2372#else 2373 UNUSED_PARAM(selection); 2374 UNUSED_PARAM(checkSpelling); 2375 UNUSED_PARAM(firstMisspellingRange); 2376#endif // !PLATFORM(IOS) 2377} 2378 2379bool Editor::isSpellCheckingEnabledFor(Node* node) const 2380{ 2381 if (!node) 2382 return false; 2383 const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement(); 2384 if (!focusedElement) 2385 return false; 2386 return focusedElement->isSpellCheckingEnabled(); 2387} 2388 2389bool Editor::isSpellCheckingEnabledInFocusedNode() const 2390{ 2391 return isSpellCheckingEnabledFor(m_frame.selection().selection().start().deprecatedNode()); 2392} 2393 2394void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange) 2395{ 2396 markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange); 2397} 2398 2399void Editor::markBadGrammar(const VisibleSelection& selection) 2400{ 2401#if USE(GRAMMAR_CHECKING) 2402 RefPtr<Range> firstMisspellingRange; 2403 markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange); 2404#else 2405 ASSERT_NOT_REACHED(); 2406#endif 2407} 2408 2409void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange) 2410{ 2411 ASSERT(unifiedTextCheckerEnabled()); 2412 2413 // There shouldn't be pending autocorrection at this moment. 2414 ASSERT(!m_alternativeTextController->hasPendingCorrection()); 2415 2416 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar; 2417 bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel; 2418 2419 // This function is called with selections already expanded to word boundaries. 2420 if (!client() || !spellingRange || (shouldMarkGrammar && !grammarRange)) 2421 return; 2422 2423 // If we're not in an editable node, bail. 2424 Node* editableNode = spellingRange->startContainer(); 2425 if (!editableNode || !editableNode->hasEditableStyle()) 2426 return; 2427 2428 if (!isSpellCheckingEnabledFor(editableNode)) 2429 return; 2430 2431 Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange; 2432 TextCheckingParagraph paragraphToCheck(rangeToCheck); 2433 if (paragraphToCheck.isRangeEmpty() || paragraphToCheck.isEmpty()) 2434 return; 2435 RefPtr<Range> paragraphRange = paragraphToCheck.paragraphRange(); 2436 2437 bool asynchronous = m_frame.settings().asynchronousSpellCheckingEnabled() && !shouldShowCorrectionPanel; 2438 2439 // In asynchronous mode, we intentionally check paragraph-wide sentence. 2440 RefPtr<SpellCheckRequest> request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessIncremental, asynchronous ? paragraphRange : rangeToCheck, paragraphRange); 2441 2442 if (asynchronous) { 2443 m_spellChecker->requestCheckingFor(request.release()); 2444 return; 2445 } 2446 2447 Vector<TextCheckingResult> results; 2448 checkTextOfParagraph(*textChecker(), paragraphToCheck.text(), resolveTextCheckingTypeMask(textCheckingOptions), results); 2449 markAndReplaceFor(request.release(), results); 2450} 2451 2452static bool isAutomaticTextReplacementType(TextCheckingType type) 2453{ 2454 switch (type) { 2455 case TextCheckingTypeNone: 2456 case TextCheckingTypeSpelling: 2457 case TextCheckingTypeGrammar: 2458 return false; 2459 case TextCheckingTypeLink: 2460 case TextCheckingTypeQuote: 2461 case TextCheckingTypeDash: 2462 case TextCheckingTypeReplacement: 2463 case TextCheckingTypeCorrection: 2464 case TextCheckingTypeShowCorrectionPanel: 2465 return true; 2466 } 2467 ASSERT_NOT_REACHED(); 2468 return false; 2469} 2470 2471static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, PassRefPtr<Range> rangeToReplace, const String& replacement, int resultLocation, int resultLength) 2472{ 2473 ContainerNode* scope = toContainerNode(highestAncestor(paragraph.paragraphRange()->startContainer())); 2474 2475 size_t paragraphLocation; 2476 size_t paragraphLength; 2477 TextIterator::getLocationAndLengthFromRange(scope, paragraph.paragraphRange().get(), paragraphLocation, paragraphLength); 2478 2479 applyCommand(SpellingCorrectionCommand::create(rangeToReplace, replacement)); 2480 2481 // TextCheckingParagraph may be orphaned after SpellingCorrectionCommand mutated DOM. 2482 // See <rdar://10305315>, http://webkit.org/b/89526. 2483 2484 RefPtr<Range> newParagraphRange = TextIterator::rangeFromLocationAndLength(scope, paragraphLocation, paragraphLength + replacement.length() - resultLength); 2485 2486 paragraph = TextCheckingParagraph(TextIterator::subrange(newParagraphRange.get(), resultLocation, replacement.length()), newParagraphRange); 2487} 2488 2489void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results) 2490{ 2491 ASSERT(request); 2492 2493 TextCheckingTypeMask textCheckingOptions = request->data().mask(); 2494 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraphRange()); 2495 2496 const bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling; 2497 const bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar; 2498 const bool shouldMarkLink = textCheckingOptions & TextCheckingTypeLink; 2499 const bool shouldPerformReplacement = textCheckingOptions & TextCheckingTypeReplacement; 2500 const bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel; 2501 const bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & TextCheckingTypeCorrection); 2502#if !USE(AUTOCORRECTION_PANEL) 2503 ASSERT(!shouldShowCorrectionPanel); 2504#endif 2505 2506 // Expand the range to encompass entire paragraphs, since text checking needs that much context. 2507 int selectionOffset = 0; 2508 bool useAmbiguousBoundaryOffset = false; 2509 bool selectionChanged = false; 2510 bool restoreSelectionAfterChange = false; 2511 bool adjustSelectionForParagraphBoundaries = false; 2512 2513 if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) { 2514 if (m_frame.selection().selection().selectionType() == VisibleSelection::CaretSelection) { 2515 // Attempt to save the caret position so we can restore it later if needed 2516 Position caretPosition = m_frame.selection().selection().end(); 2517 selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION); 2518 restoreSelectionAfterChange = true; 2519 if (selectionOffset > 0 && (selectionOffset > paragraph.textLength() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter)) 2520 adjustSelectionForParagraphBoundaries = true; 2521 if (selectionOffset > 0 && selectionOffset <= paragraph.textLength() && isAmbiguousBoundaryCharacter(paragraph.textCharAt(selectionOffset - 1))) 2522 useAmbiguousBoundaryOffset = true; 2523 } 2524 } 2525 2526 int offsetDueToReplacement = 0; 2527 2528 for (unsigned i = 0; i < results.size(); i++) { 2529 const int spellingRangeEndOffset = paragraph.checkingEnd() + offsetDueToReplacement; 2530 const TextCheckingType resultType = results[i].type; 2531 const int resultLocation = results[i].location + offsetDueToReplacement; 2532 const int resultLength = results[i].length; 2533 const int resultEndLocation = resultLocation + resultLength; 2534 const String& replacement = results[i].replacement; 2535 const bool resultEndsAtAmbiguousBoundary = useAmbiguousBoundaryOffset && resultEndLocation == selectionOffset - 1; 2536 2537 // Only mark misspelling if: 2538 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false. 2539 // 2. Result falls within spellingRange. 2540 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark 2541 // "wouldn'" as misspelled right after apostrophe is typed. 2542 if (shouldMarkSpelling && !shouldShowCorrectionPanel && resultType == TextCheckingTypeSpelling 2543 && resultLocation >= paragraph.checkingStart() && resultEndLocation <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) { 2544 ASSERT(resultLength > 0 && resultLocation >= 0); 2545 RefPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength); 2546 if (!m_alternativeTextController->isSpellingMarkerAllowed(misspellingRange)) 2547 continue; 2548 misspellingRange->startContainer()->document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling, replacement); 2549 } else if (shouldMarkGrammar && resultType == TextCheckingTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) { 2550 ASSERT(resultLength > 0 && resultLocation >= 0); 2551 const Vector<GrammarDetail>& details = results[i].details; 2552 for (unsigned j = 0; j < details.size(); j++) { 2553 const GrammarDetail& detail = details[j]; 2554 ASSERT(detail.length > 0 && detail.location >= 0); 2555 if (paragraph.checkingRangeCovers(resultLocation + detail.location, detail.length)) { 2556 RefPtr<Range> badGrammarRange = paragraph.subrange(resultLocation + detail.location, detail.length); 2557 badGrammarRange->startContainer()->document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail.userDescription); 2558 } 2559 } 2560 } else if (resultEndLocation <= spellingRangeEndOffset && resultEndLocation >= paragraph.checkingStart() 2561 && isAutomaticTextReplacementType(resultType)) { 2562 // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation. 2563 ASSERT(resultLength > 0 && resultLocation >= 0); 2564 2565 if (shouldShowCorrectionPanel && (resultEndLocation < spellingRangeEndOffset 2566 || !(resultType & (TextCheckingTypeReplacement | TextCheckingTypeCorrection)))) 2567 continue; 2568 2569 // Apply replacement if: 2570 // 1. The replacement length is non-zero. 2571 // 2. The result doesn't end at an ambiguous boundary. 2572 // (FIXME: this is required until 6853027 is fixed and text checking can do this for us 2573 bool doReplacement = replacement.length() > 0 && !resultEndsAtAmbiguousBoundary; 2574 RefPtr<Range> rangeToReplace = paragraph.subrange(resultLocation, resultLength); 2575 2576 // adding links should be done only immediately after they are typed 2577 if (resultType == TextCheckingTypeLink && selectionOffset != resultEndLocation + 1) 2578 continue; 2579 2580 if (!(shouldPerformReplacement || shouldCheckForCorrection || shouldMarkLink) || !doReplacement) 2581 continue; 2582 2583 String replacedString = plainText(rangeToReplace.get()); 2584 const bool existingMarkersPermitReplacement = m_alternativeTextController->processMarkersOnTextToBeReplacedByResult(&results[i], rangeToReplace.get(), replacedString); 2585 if (!existingMarkersPermitReplacement) 2586 continue; 2587 2588 if (shouldShowCorrectionPanel) { 2589 if (resultEndLocation == spellingRangeEndOffset) { 2590 // We only show the correction panel on the last word. 2591 m_alternativeTextController->show(rangeToReplace, replacement); 2592 break; 2593 } 2594 // If this function is called for showing correction panel, we ignore other correction or replacement. 2595 continue; 2596 } 2597 2598 VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM); 2599 if (selectionToReplace != m_frame.selection().selection()) { 2600 if (!m_frame.selection().shouldChangeSelection(selectionToReplace)) 2601 continue; 2602 } 2603 2604 if (resultType == TextCheckingTypeLink) { 2605 m_frame.selection().setSelection(selectionToReplace); 2606 selectionChanged = true; 2607 restoreSelectionAfterChange = false; 2608 if (canEditRichly()) 2609 applyCommand(CreateLinkCommand::create(document(), replacement)); 2610 } else if (canEdit() && shouldInsertText(replacement, rangeToReplace.get(), EditorInsertActionTyped)) { 2611 correctSpellcheckingPreservingTextCheckingParagraph(paragraph, rangeToReplace, replacement, resultLocation, resultLength); 2612 2613 if (AXObjectCache* cache = document().existingAXObjectCache()) { 2614 if (Element* root = m_frame.selection().selection().rootEditableElement()) 2615 cache->postNotification(root, AXObjectCache::AXAutocorrectionOccured); 2616 } 2617 2618 // Skip all other results for the replaced text. 2619 while (i + 1 < results.size() && results[i + 1].location + offsetDueToReplacement <= resultLocation) 2620 i++; 2621 2622 selectionChanged = true; 2623 offsetDueToReplacement += replacement.length() - resultLength; 2624 if (resultLocation < selectionOffset) 2625 selectionOffset += replacement.length() - resultLength; 2626 2627 // Add a marker so that corrections can easily be undone and won't be re-corrected. 2628 if (resultType == TextCheckingTypeCorrection) 2629 m_alternativeTextController->markCorrection(paragraph.subrange(resultLocation, replacement.length()), replacedString); 2630 } 2631 } 2632 } 2633 2634 if (selectionChanged) { 2635 TextCheckingParagraph extendedParagraph(paragraph); 2636 // Restore the caret position if we have made any replacements 2637 extendedParagraph.expandRangeToNextEnd(); 2638 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= extendedParagraph.rangeLength()) { 2639 RefPtr<Range> selectionRange = extendedParagraph.subrange(0, selectionOffset); 2640 m_frame.selection().moveTo(selectionRange->endPosition(), DOWNSTREAM); 2641 if (adjustSelectionForParagraphBoundaries) 2642 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity); 2643 } else { 2644 // If this fails for any reason, the fallback is to go one position beyond the last replacement 2645 m_frame.selection().moveTo(m_frame.selection().selection().end()); 2646 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity); 2647 } 2648 } 2649} 2650 2651void Editor::changeBackToReplacedString(const String& replacedString) 2652{ 2653#if !PLATFORM(IOS) 2654 ASSERT(unifiedTextCheckerEnabled()); 2655 2656 if (replacedString.isEmpty()) 2657 return; 2658 2659 RefPtr<Range> selection = selectedRange(); 2660 if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted)) 2661 return; 2662 2663 m_alternativeTextController->recordAutocorrectionResponseReversed(replacedString, selection); 2664 TextCheckingParagraph paragraph(selection); 2665 replaceSelectionWithText(replacedString, false, false); 2666 RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length()); 2667 changedRange->startContainer()->document().markers().addMarker(changedRange.get(), DocumentMarker::Replacement, String()); 2668 m_alternativeTextController->markReversed(changedRange.get()); 2669#else 2670 ASSERT_NOT_REACHED(); 2671 UNUSED_PARAM(replacedString); 2672#endif // !PLATFORM(IOS) 2673} 2674 2675 2676void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection) 2677{ 2678 if (unifiedTextCheckerEnabled()) { 2679 if (!isContinuousSpellCheckingEnabled()) 2680 return; 2681 2682 // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings. 2683 TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling; 2684 if (markGrammar && isGrammarCheckingEnabled()) 2685 textCheckingOptions |= TextCheckingTypeGrammar; 2686 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get()); 2687 return; 2688 } 2689 2690 RefPtr<Range> firstMisspellingRange; 2691 markMisspellings(spellingSelection, firstMisspellingRange); 2692 if (markGrammar) 2693 markBadGrammar(grammarSelection); 2694} 2695 2696void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction) 2697{ 2698 m_alternativeTextController->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction); 2699} 2700 2701void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary) 2702{ 2703 if (!document().markers().hasMarkers()) 2704 return; 2705 2706 if (!m_alternativeTextController->shouldRemoveMarkersUponEditing() && (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling))) 2707 return; 2708 2709 // We want to remove the markers from a word if an editing command will change the word. This can happen in one of 2710 // several scenarios: 2711 // 1. Insert in the middle of a word. 2712 // 2. Appending non whitespace at the beginning of word. 2713 // 3. Appending non whitespace at the end of word. 2714 // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to 2715 // remove the markers on that word. 2716 // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of 2717 // selection, and remove words between the selection boundaries. 2718 // 2719 VisiblePosition startOfSelection = m_frame.selection().selection().start(); 2720 VisiblePosition endOfSelection = m_frame.selection().selection().end(); 2721 if (startOfSelection.isNull()) 2722 return; 2723 // First word is the word that ends after or on the start of selection. 2724 VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary); 2725 VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary); 2726 // Last word is the word that begins before or on the end of selection 2727 VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary); 2728 VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary); 2729 2730 if (startOfFirstWord.isNull()) { 2731 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); 2732 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); 2733 } 2734 2735 if (endOfLastWord.isNull()) { 2736 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); 2737 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); 2738 } 2739 2740 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection, 2741 // we choose next word as the first word. 2742 if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) { 2743 startOfFirstWord = nextWordPosition(startOfFirstWord); 2744 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); 2745 if (startOfFirstWord == endOfSelection) 2746 return; 2747 } 2748 2749 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection, 2750 // we choose previous word as the last word. 2751 if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) { 2752 startOfLastWord = previousWordPosition(startOfLastWord); 2753 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); 2754 if (endOfLastWord == startOfSelection) 2755 return; 2756 } 2757 2758 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull()) 2759 return; 2760 2761 // Now we remove markers on everything between startOfFirstWord and endOfLastWord. 2762 // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the 2763 // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant 2764 // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde, 2765 // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of 2766 // of marker that contains the word in question, and remove marker on that whole range. 2767 RefPtr<Range> wordRange = Range::create(document(), startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent()); 2768 2769 Vector<DocumentMarker*> markers = document().markers().markersInRange(wordRange.get(), DocumentMarker::DictationAlternatives); 2770 for (size_t i = 0; i < markers.size(); ++i) 2771 m_alternativeTextController->removeDictationAlternativesForMarker(markers[i]); 2772 2773#if PLATFORM(IOS) 2774 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives | DocumentMarker::DictationPhraseWithAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker); 2775#else 2776 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::Grammar | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker); 2777#endif 2778 document().markers().clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement); 2779} 2780 2781void Editor::deletedAutocorrectionAtPosition(const Position& position, const String& originalString) 2782{ 2783 m_alternativeTextController->deletedAutocorrectionAtPosition(position, originalString); 2784} 2785 2786PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) 2787{ 2788 Document* document = m_frame.documentAtPoint(windowPoint); 2789 if (!document) 2790 return 0; 2791 2792 Frame* frame = document->frame(); 2793 ASSERT(frame); 2794 FrameView* frameView = frame->view(); 2795 if (!frameView) 2796 return 0; 2797 IntPoint framePoint = frameView->windowToContents(windowPoint); 2798 VisibleSelection selection(frame->visiblePositionForPoint(framePoint)); 2799 2800 return avoidIntersectionWithDeleteButtonController(selection.toNormalizedRange().get()); 2801} 2802 2803void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) 2804{ 2805 if (m_ignoreCompositionSelectionChange) 2806 return; 2807 2808 m_frame.selection().revealSelection(alignment, revealExtentOption); 2809} 2810 2811void Editor::setIgnoreCompositionSelectionChange(bool ignore) 2812{ 2813 if (m_ignoreCompositionSelectionChange == ignore) 2814 return; 2815 2816 m_ignoreCompositionSelectionChange = ignore; 2817#if PLATFORM(IOS) 2818 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830> 2819 if (!ignore) 2820 respondToChangedSelection(m_frame.selection().selection(), 0); 2821#endif 2822 if (!ignore) 2823 revealSelectionAfterEditingOperation(ScrollAlignment::alignToEdgeIfNeeded, RevealExtent); 2824} 2825 2826PassRefPtr<Range> Editor::compositionRange() const 2827{ 2828 if (!m_compositionNode) 2829 return 0; 2830 unsigned length = m_compositionNode->length(); 2831 unsigned start = std::min(m_compositionStart, length); 2832 unsigned end = std::min(std::max(start, m_compositionEnd), length); 2833 if (start >= end) 2834 return 0; 2835 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end); 2836} 2837 2838bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const 2839{ 2840 if (!m_compositionNode) 2841 return false; 2842 const VisibleSelection& selection = m_frame.selection().selection(); 2843 Position start = selection.start(); 2844 if (start.deprecatedNode() != m_compositionNode) 2845 return false; 2846 Position end = selection.end(); 2847 if (end.deprecatedNode() != m_compositionNode) 2848 return false; 2849 2850 if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart) 2851 return false; 2852 if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd) 2853 return false; 2854 2855 selectionStart = start.deprecatedEditingOffset() - m_compositionStart; 2856 selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd; 2857 return true; 2858} 2859 2860void Editor::transpose() 2861{ 2862 if (!canEdit()) 2863 return; 2864 2865 VisibleSelection selection = m_frame.selection().selection(); 2866 if (!selection.isCaret()) 2867 return; 2868 2869 // Make a selection that goes back one character and forward two characters. 2870 VisiblePosition caret = selection.visibleStart(); 2871 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); 2872 VisiblePosition previous = next.previous(); 2873 if (next == previous) 2874 return; 2875 previous = previous.previous(); 2876 if (!inSameParagraph(next, previous)) 2877 return; 2878 RefPtr<Range> range = makeRange(previous, next); 2879 if (!range) 2880 return; 2881 VisibleSelection newSelection(range.get(), DOWNSTREAM); 2882 2883 // Transpose the two characters. 2884 String text = plainText(range.get()); 2885 if (text.length() != 2) 2886 return; 2887 String transposed = text.right(1) + text.left(1); 2888 2889 // Select the two characters. 2890 if (newSelection != m_frame.selection().selection()) { 2891 if (!m_frame.selection().shouldChangeSelection(newSelection)) 2892 return; 2893 m_frame.selection().setSelection(newSelection); 2894 } 2895 2896 // Insert the transposed characters. 2897 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped)) 2898 return; 2899 replaceSelectionWithText(transposed, false, false); 2900} 2901 2902void Editor::addToKillRing(Range* range, bool prepend) 2903{ 2904 if (m_shouldStartNewKillRingSequence) 2905 killRing().startNewSequence(); 2906 2907 String text = plainText(range); 2908 if (prepend) 2909 killRing().prepend(text); 2910 else 2911 killRing().append(text); 2912 m_shouldStartNewKillRingSequence = false; 2913} 2914 2915void Editor::startAlternativeTextUITimer() 2916{ 2917 m_alternativeTextController->startAlternativeTextUITimer(AlternativeTextTypeCorrection); 2918} 2919 2920void Editor::handleAlternativeTextUIResult(const String& correction) 2921{ 2922 m_alternativeTextController->handleAlternativeTextUIResult(correction); 2923} 2924 2925 2926void Editor::dismissCorrectionPanelAsIgnored() 2927{ 2928 m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored); 2929} 2930 2931void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options) 2932{ 2933 // If the new selection is orphaned, then don't update the selection. 2934 if (newSelection.start().isOrphan() || newSelection.end().isOrphan()) 2935 return; 2936 2937 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection, 2938 // because there is work that it must do in this situation. 2939 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls. 2940 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid 2941 bool selectionDidNotChangeDOMPosition = newSelection == m_frame.selection().selection(); 2942 if (selectionDidNotChangeDOMPosition || m_frame.selection().shouldChangeSelection(newSelection)) 2943 m_frame.selection().setSelection(newSelection, options); 2944 2945 // Some editing operations change the selection visually without affecting its position within the DOM. 2946 // For example when you press return in the following (the caret is marked by ^): 2947 // <div contentEditable="true"><div>^Hello</div></div> 2948 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't 2949 // change the caret's DOM position (["hello", 0]). In these situations the above FrameSelection::setSelection call 2950 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and 2951 // starts a new kill ring sequence, but we want to do these things (matches AppKit). 2952#if PLATFORM(IOS) 2953 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830> 2954 if (m_ignoreCompositionSelectionChange) 2955 return; 2956#endif 2957 if (selectionDidNotChangeDOMPosition && client()) 2958 client()->respondToChangedSelection(&m_frame); 2959} 2960 2961String Editor::selectedText() const 2962{ 2963 return selectedText(TextIteratorDefaultBehavior); 2964} 2965 2966String Editor::selectedTextForDataTransfer() const 2967{ 2968 if (m_frame.settings().selectionIncludesAltImageText()) 2969 return selectedText(TextIteratorEmitsImageAltText); 2970 return selectedText(); 2971} 2972 2973String Editor::selectedText(TextIteratorBehavior behavior) const 2974{ 2975 // We remove '\0' characters because they are not visibly rendered to the user. 2976 return plainText(m_frame.selection().toNormalizedRange().get(), behavior).replaceWithLiteral('\0', ""); 2977} 2978 2979static inline void collapseCaretWidth(IntRect& rect) 2980{ 2981 // FIXME: Width adjustment doesn't work for rotated text. 2982 if (rect.width() == caretWidth) 2983 rect.setWidth(0); 2984 else if (rect.height() == caretWidth) 2985 rect.setHeight(0); 2986} 2987 2988IntRect Editor::firstRectForRange(Range* range) const 2989{ 2990 ASSERT(range->startContainer()); 2991 ASSERT(range->endContainer()); 2992 2993 VisiblePosition startVisiblePosition(range->startPosition(), DOWNSTREAM); 2994 2995 if (range->collapsed(ASSERT_NO_EXCEPTION)) { 2996 // FIXME: Getting caret rect and removing caret width is a very roundabout way to get collapsed range location. 2997 // In particular, width adjustment doesn't work for rotated text. 2998 IntRect startCaretRect = RenderedPosition(startVisiblePosition).absoluteRect(); 2999 collapseCaretWidth(startCaretRect); 3000 return startCaretRect; 3001 } 3002 3003 VisiblePosition endVisiblePosition(range->endPosition(), UPSTREAM); 3004 3005 if (inSameLine(startVisiblePosition, endVisiblePosition)) 3006 return enclosingIntRect(RenderObject::absoluteBoundingBoxRectForRange(range)); 3007 3008 LayoutUnit extraWidthToEndOfLine = 0; 3009 IntRect startCaretRect = RenderedPosition(startVisiblePosition).absoluteRect(&extraWidthToEndOfLine); 3010 if (startCaretRect == IntRect()) 3011 return IntRect(); 3012 3013 // When start and end aren't on the same line, we want to go from start to the end of its line. 3014 bool textIsHorizontal = startCaretRect.width() == caretWidth; 3015 return textIsHorizontal ? 3016 IntRect(startCaretRect.x(), 3017 startCaretRect.y(), 3018 startCaretRect.width() + extraWidthToEndOfLine, 3019 startCaretRect.height()) : 3020 IntRect(startCaretRect.x(), 3021 startCaretRect.y(), 3022 startCaretRect.width(), 3023 startCaretRect.height() + extraWidthToEndOfLine); 3024} 3025 3026bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const 3027{ 3028#if PLATFORM(IOS) 3029 if (m_frame.selectionChangeCallbacksDisabled()) 3030 return true; 3031#endif 3032 return client() && client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting); 3033} 3034 3035void Editor::computeAndSetTypingStyle(StyleProperties* style, EditAction editingAction) 3036{ 3037 if (!style || style->isEmpty()) { 3038 m_frame.selection().clearTypingStyle(); 3039 return; 3040 } 3041 3042 // Calculate the current typing style. 3043 RefPtr<EditingStyle> typingStyle; 3044 if (m_frame.selection().typingStyle()) { 3045 typingStyle = m_frame.selection().typingStyle()->copy(); 3046 typingStyle->overrideWithStyle(style); 3047 } else 3048 typingStyle = EditingStyle::create(style); 3049 3050 typingStyle->prepareToApplyAt(m_frame.selection().selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection); 3051 3052 // Handle block styles, substracting these from the typing style. 3053 RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties(); 3054 if (!blockStyle->isEmpty()) 3055 applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction)); 3056 3057 // Set the remaining style as the typing style. 3058 m_frame.selection().setTypingStyle(typingStyle); 3059} 3060 3061void Editor::textFieldDidBeginEditing(Element* e) 3062{ 3063 if (client()) 3064 client()->textFieldDidBeginEditing(e); 3065} 3066 3067void Editor::textFieldDidEndEditing(Element* e) 3068{ 3069 dismissCorrectionPanelAsIgnored(); 3070 if (client()) 3071 client()->textFieldDidEndEditing(e); 3072} 3073 3074void Editor::textDidChangeInTextField(Element* e) 3075{ 3076 if (client()) 3077 client()->textDidChangeInTextField(e); 3078} 3079 3080bool Editor::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke) 3081{ 3082 if (client()) 3083 return client()->doTextFieldCommandFromEvent(e, ke); 3084 3085 return false; 3086} 3087 3088void Editor::textWillBeDeletedInTextField(Element* input) 3089{ 3090 if (client()) 3091 client()->textWillBeDeletedInTextField(input); 3092} 3093 3094void Editor::textDidChangeInTextArea(Element* e) 3095{ 3096 if (client()) 3097 client()->textDidChangeInTextArea(e); 3098} 3099 3100void Editor::applyEditingStyleToBodyElement() const 3101{ 3102 RefPtr<NodeList> list = document().getElementsByTagName("body"); 3103 unsigned len = list->length(); 3104 for (unsigned i = 0; i < len; i++) 3105 applyEditingStyleToElement(toElement(list->item(i))); 3106} 3107 3108void Editor::applyEditingStyleToElement(Element* element) const 3109{ 3110 if (!element) 3111 return; 3112 ASSERT(element->isStyledElement()); 3113 if (!element->isStyledElement()) 3114 return; 3115 3116 // Mutate using the CSSOM wrapper so we get the same event behavior as a script. 3117 CSSStyleDeclaration* style = toStyledElement(element)->style(); 3118 style->setPropertyInternal(CSSPropertyWordWrap, "break-word", false, IGNORE_EXCEPTION); 3119 style->setPropertyInternal(CSSPropertyWebkitNbspMode, "space", false, IGNORE_EXCEPTION); 3120 style->setPropertyInternal(CSSPropertyWebkitLineBreak, "after-white-space", false, IGNORE_EXCEPTION); 3121} 3122 3123bool Editor::findString(const String& target, FindOptions options) 3124{ 3125 VisibleSelection selection = m_frame.selection().selection(); 3126 3127 RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options); 3128 3129 if (!resultRange) 3130 return false; 3131 3132 m_frame.selection().setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM)); 3133 3134 if (!(options & DoNotRevealSelection)) 3135 m_frame.selection().revealSelection(); 3136 3137 return true; 3138} 3139 3140PassRefPtr<Range> Editor::findStringAndScrollToVisible(const String& target, Range* previousMatch, FindOptions options) 3141{ 3142 RefPtr<Range> nextMatch = rangeOfString(target, previousMatch, options); 3143 if (!nextMatch) 3144 return 0; 3145 3146 nextMatch->firstNode()->renderer()->scrollRectToVisible(nextMatch->boundingBox(), 3147 ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); 3148 3149 return nextMatch.release(); 3150} 3151 3152PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options) 3153{ 3154 if (target.isEmpty()) 3155 return 0; 3156 3157 // Start from an edge of the reference range, if there's a reference range that's not in shadow content. Which edge 3158 // is used depends on whether we're searching forward or backward, and whether startInSelection is set. 3159 RefPtr<Range> searchRange(rangeOfContents(document())); 3160 3161 bool forward = !(options & Backwards); 3162 bool startInReferenceRange = referenceRange && (options & StartInSelection); 3163 if (referenceRange) { 3164 if (forward) 3165 searchRange->setStart(startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition()); 3166 else 3167 searchRange->setEnd(startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition()); 3168 } 3169 3170 RefPtr<Node> shadowTreeRoot = referenceRange && referenceRange->startContainer() ? referenceRange->startContainer()->nonBoundaryShadowTreeRootNode() : 0; 3171 if (shadowTreeRoot) { 3172 if (forward) 3173 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount()); 3174 else 3175 searchRange->setStart(shadowTreeRoot.get(), 0); 3176 } 3177 3178 RefPtr<Range> resultRange(findPlainText(*searchRange, target, options)); 3179 // If we started in the reference range and the found range exactly matches the reference range, find again. 3180 // Build a selection with the found range to remove collapsed whitespace. 3181 // Compare ranges instead of selection objects to ignore the way that the current selection was made. 3182 if (startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) { 3183 searchRange = rangeOfContents(document()); 3184 if (forward) 3185 searchRange->setStart(referenceRange->endPosition()); 3186 else 3187 searchRange->setEnd(referenceRange->startPosition()); 3188 3189 if (shadowTreeRoot) { 3190 if (forward) 3191 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount()); 3192 else 3193 searchRange->setStart(shadowTreeRoot.get(), 0); 3194 } 3195 3196 resultRange = findPlainText(*searchRange, target, options); 3197 } 3198 3199 // If nothing was found in the shadow tree, search in main content following the shadow tree. 3200 if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && shadowTreeRoot) { 3201 searchRange = rangeOfContents(document()); 3202 if (forward) 3203 searchRange->setStartAfter(shadowTreeRoot->shadowHost()); 3204 else 3205 searchRange->setEndBefore(shadowTreeRoot->shadowHost()); 3206 3207 resultRange = findPlainText(*searchRange, target, options); 3208 } 3209 3210 // If we didn't find anything and we're wrapping, search again in the entire document (this will 3211 // redundantly re-search the area already searched in some cases). 3212 if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && options & WrapAround) { 3213 searchRange = rangeOfContents(document()); 3214 resultRange = findPlainText(*searchRange, target, options); 3215 // We used to return false here if we ended up with the same range that we started with 3216 // (e.g., the reference range was already the only instance of this text). But we decided that 3217 // this should be a success case instead, so we'll just fall through in that case. 3218 } 3219 3220 return resultRange->collapsed(ASSERT_NO_EXCEPTION) ? 0 : resultRange.release(); 3221} 3222 3223static bool isFrameInRange(Frame* frame, Range* range) 3224{ 3225 bool inRange = false; 3226 for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document().ownerElement()) { 3227 if (&ownerElement->document() == &range->ownerDocument()) { 3228 inRange = range->intersectsNode(ownerElement, IGNORE_EXCEPTION); 3229 break; 3230 } 3231 } 3232 return inRange; 3233} 3234 3235unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches, Vector<RefPtr<Range>>* matches) 3236{ 3237 if (target.isEmpty()) 3238 return 0; 3239 3240 RefPtr<Range> searchRange; 3241 if (range) { 3242 if (&range->ownerDocument() == &document()) 3243 searchRange = range; 3244 else if (!isFrameInRange(&m_frame, range)) 3245 return 0; 3246 } 3247 if (!searchRange) 3248 searchRange = rangeOfContents(document()); 3249 3250 Node* originalEndContainer = searchRange->endContainer(); 3251 int originalEndOffset = searchRange->endOffset(); 3252 3253 unsigned matchCount = 0; 3254 do { 3255 RefPtr<Range> resultRange(findPlainText(*searchRange, target, options & ~Backwards)); 3256 if (resultRange->collapsed(IGNORE_EXCEPTION)) { 3257 if (!resultRange->startContainer()->isInShadowTree()) 3258 break; 3259 3260 searchRange->setStartAfter(resultRange->startContainer()->shadowHost(), IGNORE_EXCEPTION); 3261 searchRange->setEnd(originalEndContainer, originalEndOffset, IGNORE_EXCEPTION); 3262 continue; 3263 } 3264 3265 ++matchCount; 3266 if (matches) 3267 matches->append(resultRange); 3268 3269 if (markMatches) 3270 document().markers().addMarker(resultRange.get(), DocumentMarker::TextMatch); 3271 3272 // Stop looking if we hit the specified limit. A limit of 0 means no limit. 3273 if (limit > 0 && matchCount >= limit) 3274 break; 3275 3276 // Set the new start for the search range to be the end of the previous 3277 // result range. There is no need to use a VisiblePosition here, 3278 // since findPlainText will use a TextIterator to go over the visible 3279 // text nodes. 3280 searchRange->setStart(resultRange->endContainer(IGNORE_EXCEPTION), resultRange->endOffset(IGNORE_EXCEPTION), IGNORE_EXCEPTION); 3281 3282 Node* shadowTreeRoot = searchRange->shadowRoot(); 3283 if (searchRange->collapsed(IGNORE_EXCEPTION) && shadowTreeRoot) 3284 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), IGNORE_EXCEPTION); 3285 } while (true); 3286 3287 if (markMatches || matches) { 3288 // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match. 3289 if (m_frame.view() && m_frame.contentRenderer()) { 3290 document().updateLayout(); // Ensure layout is up to date. 3291 // FIXME: unclear if we need LegacyIOSDocumentVisibleRect. 3292 // FIXME: this should probably look at paintsEntireContents() 3293 LayoutRect visibleRect = m_frame.view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); 3294 if (!visibleRect.isEmpty()) { 3295 GraphicsContext context((PlatformGraphicsContext*)0); 3296 context.setPaintingDisabled(true); 3297 3298 PaintBehavior oldBehavior = m_frame.view()->paintBehavior(); 3299 m_frame.view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); 3300 m_frame.view()->paintContents(&context, enclosingIntRect(visibleRect)); 3301 m_frame.view()->setPaintBehavior(oldBehavior); 3302 } 3303 } 3304 } 3305 3306 return matchCount; 3307} 3308 3309void Editor::setMarkedTextMatchesAreHighlighted(bool flag) 3310{ 3311 if (flag == m_areMarkedTextMatchesHighlighted) 3312 return; 3313 3314 m_areMarkedTextMatchesHighlighted = flag; 3315 document().markers().repaintMarkers(DocumentMarker::TextMatch); 3316} 3317 3318void Editor::respondToChangedSelection(const VisibleSelection&, FrameSelection::SetSelectionOptions options) 3319{ 3320#if PLATFORM(IOS) 3321 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830> 3322 if (m_ignoreCompositionSelectionChange) 3323 return; 3324#endif 3325 3326 if (client()) 3327 client()->respondToChangedSelection(&m_frame); 3328 3329#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS) 3330 if (shouldDetectTelephoneNumbers()) 3331 m_telephoneNumberDetectionUpdateTimer.startOneShot(0); 3332#endif 3333 3334 setStartNewKillRingSequence(true); 3335 3336 if (m_editorUIUpdateTimer.isActive()) 3337 return; 3338 3339 // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself. 3340 m_editorUIUpdateTimerShouldCheckSpellingAndGrammar = options & FrameSelection::CloseTyping 3341 && !(options & FrameSelection::SpellCorrectionTriggered); 3342 m_editorUIUpdateTimerWasTriggeredByDictation = options & FrameSelection::DictationTriggered; 3343 m_editorUIUpdateTimer.startOneShot(0); 3344} 3345 3346#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS) 3347 3348bool Editor::shouldDetectTelephoneNumbers() 3349{ 3350 if (!m_frame.document()) 3351 return false; 3352 return document().isTelephoneNumberParsingEnabled() && TelephoneNumberDetector::isSupported(); 3353} 3354 3355void Editor::scanSelectionForTelephoneNumbers(Timer<Editor>&) 3356{ 3357 scanSelectionForTelephoneNumbers(); 3358} 3359 3360void Editor::scanSelectionForTelephoneNumbers() 3361{ 3362 if (!shouldDetectTelephoneNumbers() || !client()) 3363 return; 3364 3365 m_detectedTelephoneNumberRanges.clear(); 3366 3367 Vector<RefPtr<Range>> markedRanges; 3368 3369 FrameSelection& frameSelection = m_frame.selection(); 3370 if (!frameSelection.isRange()) { 3371 client()->selectedTelephoneNumberRangesChanged(); 3372 return; 3373 } 3374 RefPtr<Range> selectedRange = frameSelection.toNormalizedRange(); 3375 3376 // Extend the range a few characters in each direction to detect incompletely selected phone numbers. 3377 static const int charactersToExtend = 15; 3378 const VisibleSelection& visibleSelection = frameSelection.selection(); 3379 Position start = visibleSelection.start(); 3380 Position end = visibleSelection.end(); 3381 for (int i = 0; i < charactersToExtend; ++i) { 3382 if (directionOfEnclosingBlock(start) == LTR) 3383 start = start.previous(Character); 3384 else 3385 start = start.next(Character); 3386 3387 if (directionOfEnclosingBlock(end) == LTR) 3388 end = end.next(Character); 3389 else 3390 end = end.previous(Character); 3391 } 3392 3393 FrameSelection extendedSelection; 3394 extendedSelection.setStart(start); 3395 extendedSelection.setEnd(end); 3396 RefPtr<Range> extendedRange = extendedSelection.toNormalizedRange(); 3397 3398 if (!extendedRange) { 3399 client()->selectedTelephoneNumberRangesChanged(); 3400 return; 3401 } 3402 3403 scanRangeForTelephoneNumbers(*extendedRange, extendedRange->text(), markedRanges); 3404 3405 // Only consider ranges with a detected telephone number if they overlap with the actual selection range. 3406 for (auto& range : markedRanges) { 3407 if (rangesOverlap(range.get(), selectedRange.get())) 3408 m_detectedTelephoneNumberRanges.append(range); 3409 } 3410 3411 client()->selectedTelephoneNumberRangesChanged(); 3412} 3413 3414void Editor::scanRangeForTelephoneNumbers(Range& range, const StringView& stringView, Vector<RefPtr<Range>>& markedRanges) 3415{ 3416 // Don't scan for phone numbers inside editable regions. 3417 Node* startNode = range.startContainer(); 3418 ASSERT(startNode); 3419 if (startNode->hasEditableStyle()) 3420 return; 3421 3422 // relativeStartPosition and relativeEndPosition are the endpoints of the phone number range, 3423 // relative to the scannerPosition 3424 unsigned length = stringView.length(); 3425 unsigned scannerPosition = 0; 3426 int relativeStartPosition = 0; 3427 int relativeEndPosition = 0; 3428 3429 auto characters = stringView.upconvertedCharacters(); 3430 3431 while (scannerPosition < length && TelephoneNumberDetector::find(&characters[scannerPosition], length - scannerPosition, &relativeStartPosition, &relativeEndPosition)) { 3432 // The convention in the Data Detectors framework is that the end position is the first character NOT in the phone number 3433 // (that is, the length of the range is relativeEndPosition - relativeStartPosition). So subtract 1 to get the same 3434 // convention as the old WebCore phone number parser (so that the rest of the code is still valid if we want to go back 3435 // to the old parser). 3436 --relativeEndPosition; 3437 3438 ASSERT(scannerPosition + relativeEndPosition < length); 3439 3440 unsigned subrangeOffset = scannerPosition + relativeStartPosition; 3441 unsigned subrangeLength = relativeEndPosition - relativeStartPosition + 1; 3442 3443 RefPtr<Range> subrange = TextIterator::subrange(&range, subrangeOffset, subrangeLength); 3444 3445 markedRanges.append(subrange); 3446 range.ownerDocument().markers().addMarker(subrange.get(), DocumentMarker::TelephoneNumber); 3447 3448 scannerPosition += relativeEndPosition + 1; 3449 } 3450} 3451 3452#endif // ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS) 3453 3454void Editor::updateEditorUINowIfScheduled() 3455{ 3456 if (!m_editorUIUpdateTimer.isActive()) 3457 return; 3458 m_editorUIUpdateTimer.stop(); 3459 editorUIUpdateTimerFired(m_editorUIUpdateTimer); 3460} 3461 3462void Editor::editorUIUpdateTimerFired(Timer<Editor>&) 3463{ 3464 VisibleSelection oldSelection = m_oldSelectionForEditorUIUpdate; 3465 3466 m_alternativeTextController->stopPendingCorrection(oldSelection); 3467 3468 bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled(); 3469 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled(); 3470 if (isContinuousSpellCheckingEnabled) { 3471 VisibleSelection newAdjacentWords; 3472 VisibleSelection newSelectedSentence; 3473 bool caretBrowsing = m_frame.settings().caretBrowsingEnabled(); 3474 if (m_frame.selection().selection().isContentEditable() || caretBrowsing) { 3475 VisiblePosition newStart(m_frame.selection().selection().visibleStart()); 3476#if !PLATFORM(IOS) 3477 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary)); 3478#else 3479 // If this bug gets fixed, this PLATFORM(IOS) code could be removed: 3480 // <rdar://problem/7259611> Word boundary code on iPhone gives different results than desktop 3481 EWordSide startWordSide = LeftWordIfOnBoundary; 3482 UChar32 c = newStart.characterBefore(); 3483 // FIXME: VisiblePosition::characterAfter() and characterBefore() do not emit newlines the same 3484 // way as TextIterator, so we do an isStartOfParagraph check here. 3485 if (isSpaceOrNewline(c) || c == 0xA0 || isStartOfParagraph(newStart)) { 3486 startWordSide = RightWordIfOnBoundary; 3487 } 3488 newAdjacentWords = VisibleSelection(startOfWord(newStart, startWordSide), endOfWord(newStart, RightWordIfOnBoundary)); 3489#endif // !PLATFORM(IOS) 3490 if (isContinuousGrammarCheckingEnabled) 3491 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart)); 3492 } 3493 3494 // When typing we check spelling elsewhere, so don't redo it here. 3495 // If this is a change in selection resulting from a delete operation, 3496 // oldSelection may no longer be in the document. 3497 if (m_editorUIUpdateTimerShouldCheckSpellingAndGrammar && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) { 3498 VisiblePosition oldStart(oldSelection.visibleStart()); 3499 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary)); 3500 if (oldAdjacentWords != newAdjacentWords) { 3501 if (isContinuousGrammarCheckingEnabled) { 3502 VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart)); 3503 markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence); 3504 } else 3505 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords); 3506 } 3507 } 3508 3509 if (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)) { 3510 if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange()) 3511 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling); 3512 } 3513 if (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeGrammar)) { 3514 if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange()) 3515 document().markers().removeMarkers(sentenceRange.get(), DocumentMarker::Grammar); 3516 } 3517 } 3518 3519 // When continuous spell checking is off, existing markers disappear after the selection changes. 3520 if (!isContinuousSpellCheckingEnabled) 3521 document().markers().removeMarkers(DocumentMarker::Spelling); 3522 if (!isContinuousGrammarCheckingEnabled) 3523 document().markers().removeMarkers(DocumentMarker::Grammar); 3524 3525#if ENABLE(DELETION_UI) 3526 m_deleteButtonController->respondToChangedSelection(oldSelection); 3527#endif 3528 if (!m_editorUIUpdateTimerWasTriggeredByDictation) 3529 m_alternativeTextController->respondToChangedSelection(oldSelection); 3530 3531 m_oldSelectionForEditorUIUpdate = m_frame.selection().selection(); 3532} 3533 3534static Node* findFirstMarkable(Node* node) 3535{ 3536 while (node) { 3537 if (!node->renderer()) 3538 return 0; 3539 if (node->renderer()->isTextOrLineBreak()) 3540 return node; 3541 if (isHTMLTextFormControlElement(*node)) 3542 node = toHTMLTextFormControlElement(node)->visiblePositionForIndex(1).deepEquivalent().deprecatedNode(); 3543 else if (node->firstChild()) 3544 node = node->firstChild(); 3545 else 3546 node = node->nextSibling(); 3547 } 3548 3549 return 0; 3550} 3551 3552bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const 3553{ 3554 Node* node = findFirstMarkable(m_frame.selection().selection().start().deprecatedNode()); 3555 if (!node) 3556 return false; 3557 3558 unsigned int startOffset = static_cast<unsigned int>(from); 3559 unsigned int endOffset = static_cast<unsigned int>(from + length); 3560 Vector<DocumentMarker*> markers = document().markers().markersFor(node); 3561 for (size_t i = 0; i < markers.size(); ++i) { 3562 DocumentMarker* marker = markers[i]; 3563 if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType) 3564 return true; 3565 } 3566 3567 return false; 3568} 3569 3570TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions) 3571{ 3572 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling; 3573 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar; 3574#if !PLATFORM(IOS) 3575 bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel; 3576 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & TextCheckingTypeCorrection); 3577#endif 3578 3579 TextCheckingTypeMask checkingTypes = 0; 3580 if (shouldMarkSpelling) 3581 checkingTypes |= TextCheckingTypeSpelling; 3582 if (shouldMarkGrammar) 3583 checkingTypes |= TextCheckingTypeGrammar; 3584#if !PLATFORM(IOS) 3585 if (shouldCheckForCorrection) 3586 checkingTypes |= TextCheckingTypeCorrection; 3587 if (shouldShowCorrectionPanel) 3588 checkingTypes |= TextCheckingTypeShowCorrectionPanel; 3589 3590#if USE(AUTOMATIC_TEXT_REPLACEMENT) 3591 bool shouldPerformReplacement = textCheckingOptions & TextCheckingTypeReplacement; 3592 if (shouldPerformReplacement) { 3593 if (isAutomaticLinkDetectionEnabled()) 3594 checkingTypes |= TextCheckingTypeLink; 3595 if (isAutomaticQuoteSubstitutionEnabled()) 3596 checkingTypes |= TextCheckingTypeQuote; 3597 if (isAutomaticDashSubstitutionEnabled()) 3598 checkingTypes |= TextCheckingTypeDash; 3599 if (isAutomaticTextReplacementEnabled()) 3600 checkingTypes |= TextCheckingTypeReplacement; 3601 if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled()) 3602 checkingTypes |= TextCheckingTypeCorrection; 3603 } 3604#endif 3605#endif // !PLATFORM(IOS) 3606 3607 return checkingTypes; 3608} 3609 3610void Editor::deviceScaleFactorChanged() 3611{ 3612#if ENABLE(DELETION_UI) 3613 m_deleteButtonController->deviceScaleFactorChanged(); 3614#endif 3615} 3616 3617bool Editor::unifiedTextCheckerEnabled() const 3618{ 3619 return WebCore::unifiedTextCheckerEnabled(&m_frame); 3620} 3621 3622Vector<String> Editor::dictationAlternativesForMarker(const DocumentMarker* marker) 3623{ 3624 return m_alternativeTextController->dictationAlternativesForMarker(marker); 3625} 3626 3627void Editor::applyDictationAlternativelternative(const String& alternativeString) 3628{ 3629 m_alternativeTextController->applyDictationAlternative(alternativeString); 3630} 3631 3632void Editor::toggleOverwriteModeEnabled() 3633{ 3634 m_overwriteModeEnabled = !m_overwriteModeEnabled; 3635 m_frame.selection().setShouldShowBlockCursor(m_overwriteModeEnabled); 3636} 3637 3638Document& Editor::document() const 3639{ 3640 ASSERT(m_frame.document()); 3641 return *m_frame.document(); 3642} 3643 3644#if PLATFORM(COCOA) 3645// FIXME: This figures out the current style by inserting a <span>! 3646RenderStyle* Editor::styleForSelectionStart(Frame* frame, Node *&nodeToRemove) 3647{ 3648 nodeToRemove = nullptr; 3649 3650 if (frame->selection().isNone()) 3651 return nullptr; 3652 3653 Position position = frame->selection().selection().visibleStart().deepEquivalent(); 3654 if (!position.isCandidate() || position.isNull()) 3655 return nullptr; 3656 3657 RefPtr<EditingStyle> typingStyle = frame->selection().typingStyle(); 3658 if (!typingStyle || !typingStyle->style()) 3659 return &position.deprecatedNode()->renderer()->style(); 3660 3661 RefPtr<Element> styleElement = frame->document()->createElement(spanTag, false); 3662 3663 String styleText = typingStyle->style()->asText() + " display: inline"; 3664 styleElement->setAttribute(styleAttr, styleText); 3665 3666 styleElement->appendChild(frame->document()->createEditingTextNode(""), ASSERT_NO_EXCEPTION); 3667 3668 position.deprecatedNode()->parentNode()->appendChild(styleElement, ASSERT_NO_EXCEPTION); 3669 3670 nodeToRemove = styleElement.get(); 3671 3672 frame->document()->updateStyleIfNeeded(); 3673 return styleElement->renderer() ? &styleElement->renderer()->style() : nullptr; 3674} 3675#endif 3676 3677} // namespace WebCore 3678