2 * Copyright (C) 2006, 2007, 2008, 2011, 2013-2015 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
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"
51 #include "FrameTree.h"
52 #include "FrameView.h"
53 #include "GraphicsContext.h"
54 #include "HTMLCollection.h"
55 #include "HTMLFormControlElement.h"
56 #include "HTMLFrameOwnerElement.h"
57 #include "HTMLImageElement.h"
58 #include "HTMLNames.h"
59 #include "HTMLTextAreaElement.h"
60 #include "HitTestResult.h"
61 #include "IndentOutdentCommand.h"
62 #include "InsertListCommand.h"
63 #include "KeyboardEvent.h"
66 #include "MainFrame.h"
67 #include "ModifySelectionListLevel.h"
69 #include "NodeTraversal.h"
71 #include "Pasteboard.h"
73 #include "RemoveFormatCommand.h"
74 #include "RenderBlock.h"
75 #include "RenderTextControl.h"
76 #include "RenderedDocumentMarker.h"
77 #include "RenderedPosition.h"
78 #include "ReplaceSelectionCommand.h"
80 #include "ShadowRoot.h"
81 #include "SimplifyMarkupCommand.h"
83 #include "SpellChecker.h"
84 #include "SpellingCorrectionCommand.h"
85 #include "StyleProperties.h"
86 #include "TelephoneNumberDetector.h"
88 #include "TextCheckerClient.h"
89 #include "TextCheckingHelper.h"
90 #include "TextEvent.h"
91 #include "TextIterator.h"
92 #include "TypingCommand.h"
93 #include "UserTypingGestureIndicator.h"
94 #include "VisibleUnits.h"
95 #include "htmlediting.h"
97 #include <wtf/unicode/CharacterNames.h>
100 #include "DictationCommandIOS.h"
101 #include <wtf/text/StringBuilder.h>
102 #include <wtf/text/WTFString.h>
106 #include "ServicesOverlayController.h"
111 class ClearTextCommand : public DeleteSelectionCommand {
113 ClearTextCommand(Document& document);
114 static void CreateAndApply(const RefPtr<Frame> frame);
117 virtual EditAction editingAction() const;
120 ClearTextCommand::ClearTextCommand(Document& document)
121 : DeleteSelectionCommand(document, false, true, false, false, true)
125 EditAction ClearTextCommand::editingAction() const
127 return EditActionDelete;
130 void ClearTextCommand::CreateAndApply(const RefPtr<Frame> frame)
132 if (frame->selection().isNone())
135 // Don't leave around stale composition state.
136 frame->editor().clear();
138 const VisibleSelection oldSelection = frame->selection().selection();
139 frame->selection().selectAll();
140 auto clearCommand = adoptRef(*new ClearTextCommand(*frame->document()));
141 clearCommand->setStartingSelection(oldSelection);
142 applyCommand(WTFMove(clearCommand));
145 using namespace HTMLNames;
147 using namespace Unicode;
149 // When an event handler has moved the selection outside of a text control
150 // we should use the target control's selection for this editing operation.
151 VisibleSelection Editor::selectionForCommand(Event* event)
153 VisibleSelection selection = m_frame.selection().selection();
156 // If the target is a text control, and the current selection is outside of its shadow tree,
157 // then use the saved selection for that text control.
158 HTMLTextFormControlElement* textFormControlOfSelectionStart = enclosingTextFormControl(selection.start());
159 HTMLTextFormControlElement* textFromControlOfTarget = is<HTMLTextFormControlElement>(*event->target()->toNode()) ? downcast<HTMLTextFormControlElement>(event->target()->toNode()) : nullptr;
160 if (textFromControlOfTarget && (selection.start().isNull() || textFromControlOfTarget != textFormControlOfSelectionStart)) {
161 if (RefPtr<Range> range = textFromControlOfTarget->selection())
162 return VisibleSelection(*range, DOWNSTREAM, selection.isDirectional());
167 // Function considers Mac editing behavior a fallback when Page or Settings is not available.
168 EditingBehavior Editor::behavior() const
170 return EditingBehavior(m_frame.settings().editingBehaviorType());
173 EditorClient* Editor::client() const
175 if (Page* page = m_frame.page())
176 return &page->editorClient();
180 TextCheckerClient* Editor::textChecker() const
182 if (EditorClient* owner = client())
183 return owner->textChecker();
187 void Editor::handleKeyboardEvent(KeyboardEvent& event)
189 if (EditorClient* c = client())
190 c->handleKeyboardEvent(&event);
193 void Editor::handleInputMethodKeydown(KeyboardEvent& event)
195 if (EditorClient* c = client())
196 c->handleInputMethodKeydown(&event);
199 bool Editor::handleTextEvent(TextEvent& event)
201 LOG(Editing, "Editor %p handleTextEvent (data %s)", this, event.data().utf8().data());
203 // Default event handling for Drag and Drop will be handled by DragController
204 // so we leave the event for it.
208 if (event.isPaste()) {
209 if (event.pastingFragment())
212 if (client()->performsTwoStepPaste(event.pastingFragment()))
215 replaceSelectionWithFragment(event.pastingFragment(), false, event.shouldSmartReplace(), event.shouldMatchStyle(), EditActionPaste, event.mailBlockquoteHandling());
220 replaceSelectionWithText(event.data(), false, event.shouldSmartReplace(), EditActionPaste);
224 String data = event.data();
226 if (event.isLineBreak())
227 return insertLineBreak();
228 return insertParagraphSeparator();
231 return insertTextWithoutSendingTextEvent(data, false, &event);
234 bool Editor::canEdit() const
236 return m_frame.selection().selection().rootEditableElement();
239 bool Editor::canEditRichly() const
241 return m_frame.selection().selection().isContentRichlyEditable();
244 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
245 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
246 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
247 // normally selectable to implement copy/paste (like divs, or a document body).
249 bool Editor::canDHTMLCut()
251 return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, DataTransferAccessPolicy::Numb);
254 bool Editor::canDHTMLCopy()
256 return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, DataTransferAccessPolicy::Numb);
259 bool Editor::canDHTMLPaste()
261 return !dispatchCPPEvent(eventNames().beforepasteEvent, DataTransferAccessPolicy::Numb);
264 bool Editor::canCut() const
266 return canCopy() && canDelete();
269 static HTMLImageElement* imageElementFromImageDocument(Document& document)
271 if (!document.isImageDocument())
274 HTMLElement* body = document.bodyOrFrameset();
278 Node* node = body->firstChild();
279 if (!is<HTMLImageElement>(node))
281 return downcast<HTMLImageElement>(node);
284 bool Editor::canCopy() const
286 if (imageElementFromImageDocument(document()))
288 const VisibleSelection& selection = m_frame.selection().selection();
289 return selection.isRange() && !selection.isInPasswordField();
292 bool Editor::canPaste() const
297 bool Editor::canDelete() const
299 const VisibleSelection& selection = m_frame.selection().selection();
300 return selection.isRange() && selection.rootEditableElement();
303 bool Editor::canDeleteRange(Range* range) const
305 Node& startContainer = range->startContainer();
306 Node& endContainer = range->endContainer();
308 if (!startContainer.hasEditableStyle() || !endContainer.hasEditableStyle())
311 if (range->collapsed()) {
312 VisiblePosition start(range->startPosition(), DOWNSTREAM);
313 VisiblePosition previous = start.previous();
314 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
315 if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer.rootEditableElement())
321 bool Editor::smartInsertDeleteEnabled()
323 return client() && client()->smartInsertDeleteEnabled();
326 bool Editor::canSmartCopyOrDelete()
328 return client() && client()->smartInsertDeleteEnabled() && m_frame.selection().granularity() == WordGranularity;
331 bool Editor::isSelectTrailingWhitespaceEnabled()
333 return client() && client()->isSelectTrailingWhitespaceEnabled();
336 bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool shouldAddToKillRing, bool isTypingAction)
341 if (m_frame.selection().isRange()) {
342 if (isTypingAction) {
343 TypingCommand::deleteKeyPressed(document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity);
344 revealSelectionAfterEditingOperation();
346 if (shouldAddToKillRing)
347 addRangeToKillRing(*selectedRange().get(), KillRingInsertionMode::AppendText);
348 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
349 // Implicitly calls revealSelectionAfterEditingOperation().
352 TypingCommand::Options options = 0;
353 if (canSmartCopyOrDelete())
354 options |= TypingCommand::SmartDelete;
355 if (shouldAddToKillRing)
356 options |= TypingCommand::AddsToKillRing;
358 case DirectionForward:
360 TypingCommand::forwardDeleteKeyPressed(document(), options, granularity);
362 case DirectionBackward:
364 TypingCommand::deleteKeyPressed(document(), options, granularity);
367 revealSelectionAfterEditingOperation();
370 // FIXME: We should to move this down into deleteKeyPressed.
371 // clear the "start new kill ring sequence" setting, because it was set to true
372 // when the selection was updated by deleting the range
373 if (shouldAddToKillRing)
374 setStartNewKillRingSequence(false);
379 void Editor::deleteSelectionWithSmartDelete(bool smartDelete, EditAction editingAction)
381 if (m_frame.selection().isNone())
384 applyCommand(DeleteSelectionCommand::create(document(), smartDelete, true, false, false, true, editingAction));
387 void Editor::clearText()
389 ClearTextCommand::CreateAndApply(&m_frame);
394 void Editor::insertDictationPhrases(Vector<Vector<String>>&& dictationPhrases, RetainPtr<id> metadata)
396 if (m_frame.selection().isNone())
399 if (dictationPhrases.isEmpty())
402 applyCommand(DictationCommandIOS::create(document(), WTFMove(dictationPhrases), WTFMove(metadata)));
405 void Editor::setDictationPhrasesAsChildOfElement(const Vector<Vector<String>>& dictationPhrases, RetainPtr<id> metadata, Element& element)
407 // Clear the composition.
410 // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day
411 // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish.
412 clearUndoRedoOperations();
414 m_frame.selection().clear();
416 element.removeChildren();
418 if (dictationPhrases.isEmpty()) {
419 client()->respondToChangedContents();
424 RefPtr<Range> context = document().createRange();
425 context->selectNodeContents(element, ec);
427 StringBuilder dictationPhrasesBuilder;
428 for (auto& interpretations : dictationPhrases)
429 dictationPhrasesBuilder.append(interpretations[0]);
431 element.appendChild(createFragmentFromText(*context, dictationPhrasesBuilder.toString()), ec);
433 // We need a layout in order to add markers below.
434 document().updateLayout();
436 if (!element.firstChild()->isTextNode()) {
438 ASSERT(element.firstChild()->isTextNode());
442 Text& textNode = downcast<Text>(*element.firstChild());
443 int previousDictationPhraseStart = 0;
444 for (auto& interpretations : dictationPhrases) {
445 int dictationPhraseLength = interpretations[0].length();
446 int dictationPhraseEnd = previousDictationPhraseStart + dictationPhraseLength;
447 if (interpretations.size() > 1) {
448 auto dictationPhraseRange = Range::create(document(), &textNode, previousDictationPhraseStart, &textNode, dictationPhraseEnd);
449 document().markers().addDictationPhraseWithAlternativesMarker(dictationPhraseRange.ptr(), interpretations);
451 previousDictationPhraseStart = dictationPhraseEnd;
454 auto resultRange = Range::create(document(), &textNode, 0, &textNode, textNode.length());
455 document().markers().addDictationResultMarker(resultRange.ptr(), metadata);
457 client()->respondToChangedContents();
462 void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace)
464 Node* target = findEventTargetFromSelection();
467 target->dispatchEvent(TextEvent::createForPlainTextPaste(document().domWindow(), pastingText, smartReplace));
470 void Editor::pasteAsFragment(Ref<DocumentFragment>&& pastingFragment, bool smartReplace, bool matchStyle, MailBlockquoteHandling respectsMailBlockquote)
472 Node* target = findEventTargetFromSelection();
475 target->dispatchEvent(TextEvent::createForFragmentPaste(document().domWindow(), WTFMove(pastingFragment), smartReplace, matchStyle, respectsMailBlockquote));
478 void Editor::pasteAsPlainTextBypassingDHTML()
480 pasteAsPlainTextWithPasteboard(*Pasteboard::createForCopyAndPaste());
483 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard& pasteboard)
485 String text = readPlainTextFromPasteboard(pasteboard);
486 if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
487 pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
490 String Editor::readPlainTextFromPasteboard(Pasteboard& pasteboard)
492 PasteboardPlainText text;
493 pasteboard.read(text);
494 return plainTextFromPasteboard(text);
499 String Editor::plainTextFromPasteboard(const PasteboardPlainText& text)
506 bool Editor::canSmartReplaceWithPasteboard(Pasteboard& pasteboard)
508 return client() && client()->smartInsertDeleteEnabled() && pasteboard.canSmartReplace();
511 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
517 Node* child = fragment->firstChild();
518 if (is<CharacterData>(child) && fragment->lastChild() == child)
519 return client()->shouldInsertText(downcast<CharacterData>(*child).data(), replacingDOMRange.get(), givenAction);
522 return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
525 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, EditAction editingAction, MailBlockquoteHandling mailBlockquoteHandling)
527 VisibleSelection selection = m_frame.selection().selection();
528 if (selection.isNone() || !selection.isContentEditable() || !fragment)
531 AccessibilityReplacedText replacedText;
532 if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste)
533 replacedText = AccessibilityReplacedText(selection);
535 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting | ReplaceSelectionCommand::SanitizeFragment;
536 if (selectReplacement)
537 options |= ReplaceSelectionCommand::SelectReplacement;
539 options |= ReplaceSelectionCommand::SmartReplace;
541 options |= ReplaceSelectionCommand::MatchStyle;
542 if (mailBlockquoteHandling == MailBlockquoteHandling::IgnoreBlockquote)
543 options |= ReplaceSelectionCommand::IgnoreMailBlockquote;
545 RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document(), fragment, options, editingAction);
546 applyCommand(command);
547 revealSelectionAfterEditingOperation();
549 selection = m_frame.selection().selection();
550 if (selection.isInPasswordField())
553 if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste) {
554 String text = AccessibilityObject::stringForVisiblePositionRange(command->visibleSelectionForInsertedText());
555 replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypePaste, text, m_frame.selection().selection());
556 command->composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
559 if (!isContinuousSpellCheckingEnabled())
562 Node* nodeToCheck = selection.rootEditableElement();
566 RefPtr<Range> rangeToCheck = Range::create(document(), firstPositionInNode(nodeToCheck), lastPositionInNode(nodeToCheck));
567 m_spellChecker->requestCheckingFor(SpellCheckRequest::create(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
570 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace, EditAction editingAction)
572 RefPtr<Range> range = selectedRange();
576 replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true, editingAction);
579 RefPtr<Range> Editor::selectedRange()
581 return m_frame.selection().toNormalizedRange();
585 void Editor::confirmMarkedText()
587 // FIXME: This is a hacky workaround for the keyboard calling this method too late -
588 // after the selection and focus have already changed. See <rdar://problem/5975559>
589 Element* focused = document().focusedElement();
590 Node* composition = compositionNode();
592 if (composition && focused && focused != composition && !composition->isDescendantOrShadowDescendantOf(focused)) {
594 document().setFocusedElement(focused);
596 confirmComposition();
599 void Editor::setTextAsChildOfElement(const String& text, Element& element)
601 // Clear the composition
604 // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day
605 // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish.
606 clearUndoRedoOperations();
608 // If the element is empty already and we're not adding text, we can early return and avoid clearing/setting
609 // a selection at [0, 0] and the expense involved in creation VisiblePositions.
610 if (!element.firstChild() && text.isEmpty())
613 // As a side effect this function sets a caret selection after the inserted content. Much of what
614 // follows is more expensive if there is a selection, so clear it since it's going to change anyway.
615 m_frame.selection().clear();
617 // clear out all current children of element
618 element.removeChildren();
622 // remove element from tree while doing it
623 // FIXME: The element we're inserting into is often the body element. It seems strange to be removing it
624 // (even if it is only temporary). ReplaceSelectionCommand doesn't bother doing this when it inserts
625 // content, why should we here?
627 RefPtr<Node> parent = element.parentNode();
628 RefPtr<Node> siblingAfter = element.nextSibling();
632 auto context = document().createRange();
633 context->selectNodeContents(element, ec);
634 element.appendChild(createFragmentFromText(context, text), ec);
636 // restore element to document
639 parent->insertBefore(element, siblingAfter.get(), ec);
641 parent->appendChild(element, ec);
645 // set the selection to the end
646 VisibleSelection selection;
648 Position pos = createLegacyEditingPosition(&element, element.countChildNodes());
650 VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
651 if (visiblePos.isNull())
654 selection.setBase(visiblePos);
655 selection.setExtent(visiblePos);
657 m_frame.selection().setSelection(selection);
659 client()->respondToChangedContents();
663 bool Editor::shouldDeleteRange(Range* range) const
665 if (!range || range->collapsed())
668 if (!canDeleteRange(range))
671 return client() && client()->shouldDeleteRange(range);
674 bool Editor::tryDHTMLCopy()
676 if (m_frame.selection().selection().isInPasswordField())
679 return !dispatchCPPEvent(eventNames().copyEvent, DataTransferAccessPolicy::Writable);
682 bool Editor::tryDHTMLCut()
684 if (m_frame.selection().selection().isInPasswordField())
687 return !dispatchCPPEvent(eventNames().cutEvent, DataTransferAccessPolicy::Writable);
690 bool Editor::tryDHTMLPaste()
692 return !dispatchCPPEvent(eventNames().pasteEvent, DataTransferAccessPolicy::Readable);
695 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
697 return client() && client()->shouldInsertText(text, range, action);
700 void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
702 if (AXObjectCache::accessibilityEnabled()) {
703 Node* node = endingSelection.start().deprecatedNode();
704 if (AXObjectCache* cache = document().existingAXObjectCache())
705 cache->postNotification(node, AXObjectCache::AXValueChanged, TargetObservableParent);
708 updateMarkersForWordsAffectedByEditing(true);
711 client()->respondToChangedContents();
714 bool Editor::hasBidiSelection() const
716 if (m_frame.selection().isNone())
720 if (m_frame.selection().isRange()) {
721 startNode = m_frame.selection().selection().start().downstream().deprecatedNode();
722 Node* endNode = m_frame.selection().selection().end().upstream().deprecatedNode();
723 if (enclosingBlock(startNode) != enclosingBlock(endNode))
726 startNode = m_frame.selection().selection().visibleStart().deepEquivalent().deprecatedNode();
731 auto renderer = startNode->renderer();
732 while (renderer && !is<RenderBlockFlow>(*renderer))
733 renderer = renderer->parent();
738 if (!renderer->style().isLeftToRightDirection())
741 return downcast<RenderBlockFlow>(*renderer).containsNonZeroBidiLevel();
744 TriState Editor::selectionUnorderedListState() const
746 if (m_frame.selection().isCaret()) {
747 if (enclosingElementWithTag(m_frame.selection().selection().start(), ulTag))
749 } else if (m_frame.selection().isRange()) {
750 auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), ulTag);
751 auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), ulTag);
752 if (startNode && endNode && startNode == endNode)
756 return FalseTriState;
759 TriState Editor::selectionOrderedListState() const
761 if (m_frame.selection().isCaret()) {
762 if (enclosingElementWithTag(m_frame.selection().selection().start(), olTag))
764 } else if (m_frame.selection().isRange()) {
765 auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), olTag);
766 auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), olTag);
767 if (startNode && endNode && startNode == endNode)
771 return FalseTriState;
774 PassRefPtr<Node> Editor::insertOrderedList()
776 if (!canEditRichly())
779 RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::OrderedList);
780 revealSelectionAfterEditingOperation();
784 PassRefPtr<Node> Editor::insertUnorderedList()
786 if (!canEditRichly())
789 RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::UnorderedList);
790 revealSelectionAfterEditingOperation();
794 bool Editor::canIncreaseSelectionListLevel()
796 return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(&document());
799 bool Editor::canDecreaseSelectionListLevel()
801 return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(&document());
804 RefPtr<Node> Editor::increaseSelectionListLevel()
806 if (!canEditRichly() || m_frame.selection().isNone())
809 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(&document());
810 revealSelectionAfterEditingOperation();
814 RefPtr<Node> Editor::increaseSelectionListLevelOrdered()
816 if (!canEditRichly() || m_frame.selection().isNone())
819 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(&document());
820 revealSelectionAfterEditingOperation();
824 RefPtr<Node> Editor::increaseSelectionListLevelUnordered()
826 if (!canEditRichly() || m_frame.selection().isNone())
829 RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(&document());
830 revealSelectionAfterEditingOperation();
834 void Editor::decreaseSelectionListLevel()
836 if (!canEditRichly() || m_frame.selection().isNone())
839 DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(&document());
840 revealSelectionAfterEditingOperation();
843 void Editor::removeFormattingAndStyle()
845 applyCommand(RemoveFormatCommand::create(document()));
848 void Editor::clearLastEditCommand()
850 m_lastEditCommand = nullptr;
853 // If the selection is adjusted from UIKit without closing the typing, the typing command may
854 // have a stale selection.
855 void Editor::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping()
857 TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(&m_frame, m_frame.selection().selection());
861 // Returns whether caller should continue with "the default processing", which is the same as
862 // the event handler NOT setting the return value to false
863 bool Editor::dispatchCPPEvent(const AtomicString& eventType, DataTransferAccessPolicy policy)
865 Node* target = findEventTargetFromSelection();
869 RefPtr<DataTransfer> dataTransfer = DataTransfer::createForCopyAndPaste(policy);
871 Ref<Event> event = ClipboardEvent::create(eventType, true, true, dataTransfer.get());
872 target->dispatchEvent(event);
873 bool noDefaultProcessing = event->defaultPrevented();
874 if (noDefaultProcessing && policy == DataTransferAccessPolicy::Writable) {
875 auto pasteboard = Pasteboard::createForCopyAndPaste();
877 pasteboard->writePasteboard(dataTransfer->pasteboard());
880 // invalidate dataTransfer here for security
881 dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb);
883 return !noDefaultProcessing;
886 Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const
888 Node* target = selection.start().element();
890 target = document().bodyOrFrameset();
897 Node* Editor::findEventTargetFromSelection() const
899 return findEventTargetFrom(m_frame.selection().selection());
902 void Editor::applyStyle(StyleProperties* style, EditAction editingAction)
904 switch (m_frame.selection().selection().selectionType()) {
905 case VisibleSelection::NoSelection:
907 case VisibleSelection::CaretSelection:
908 computeAndSetTypingStyle(EditingStyle::create(style), editingAction);
910 case VisibleSelection::RangeSelection:
912 applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).ptr(), editingAction));
915 client()->didApplyStyle();
918 void Editor::applyStyle(RefPtr<EditingStyle>&& style, EditAction editingAction)
920 switch (m_frame.selection().selection().selectionType()) {
921 case VisibleSelection::NoSelection:
923 case VisibleSelection::CaretSelection:
924 computeAndSetTypingStyle(*style, editingAction);
926 case VisibleSelection::RangeSelection:
928 applyCommand(ApplyStyleCommand::create(document(), style.get(), editingAction));
931 client()->didApplyStyle();
934 bool Editor::shouldApplyStyle(StyleProperties* style, Range* range)
936 return client()->shouldApplyStyle(style, range);
939 void Editor::applyParagraphStyle(StyleProperties* style, EditAction editingAction)
941 switch (m_frame.selection().selection().selectionType()) {
942 case VisibleSelection::NoSelection:
944 case VisibleSelection::CaretSelection:
945 case VisibleSelection::RangeSelection:
947 applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).ptr(), editingAction, ApplyStyleCommand::ForceBlockProperties));
950 client()->didApplyStyle();
953 void Editor::applyStyleToSelection(StyleProperties* style, EditAction editingAction)
955 if (!style || style->isEmpty() || !canEditRichly())
958 if (!client() || !client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get()))
960 applyStyle(style, editingAction);
963 void Editor::applyStyleToSelection(Ref<EditingStyle>&& style, EditAction editingAction)
965 if (style->isEmpty() || !canEditRichly())
968 // FIXME: This is wrong for text decorations since m_mutableStyle is empty.
969 if (!client() || !client()->shouldApplyStyle(style->styleWithResolvedTextDecorations().ptr(), m_frame.selection().toNormalizedRange().get()))
972 applyStyle(WTFMove(style), editingAction);
975 void Editor::applyParagraphStyleToSelection(StyleProperties* style, EditAction editingAction)
977 if (!style || style->isEmpty() || !canEditRichly())
980 if (client() && client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get()))
981 applyParagraphStyle(style, editingAction);
984 bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const
986 return EditingStyle::create(propertyID, value)->triStateOfStyle(
987 EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor).get());
990 TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const
992 return EditingStyle::create(propertyID, value)->triStateOfStyle(m_frame.selection().selection());
995 String Editor::selectionStartCSSPropertyValue(CSSPropertyID propertyID)
997 RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection(),
998 propertyID == CSSPropertyBackgroundColor);
999 if (!selectionStyle || !selectionStyle->style())
1002 if (propertyID == CSSPropertyFontSize)
1003 return String::number(selectionStyle->legacyFontSize(&document()));
1004 return selectionStyle->style()->getPropertyValue(propertyID);
1007 void Editor::indent()
1009 applyCommand(IndentOutdentCommand::create(document(), IndentOutdentCommand::Indent));
1012 void Editor::outdent()
1014 applyCommand(IndentOutdentCommand::create(document(), IndentOutdentCommand::Outdent));
1017 static void notifyTextFromControls(Element* startRoot, Element* endRoot)
1019 HTMLTextFormControlElement* startingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(startRoot));
1020 HTMLTextFormControlElement* endingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(endRoot));
1021 if (startingTextControl)
1022 startingTextControl->didEditInnerTextValue();
1023 if (endingTextControl && startingTextControl != endingTextControl)
1024 endingTextControl->didEditInnerTextValue();
1027 static void dispatchEditableContentChangedEvents(PassRefPtr<Element> prpStartRoot, PassRefPtr<Element> prpEndRoot)
1029 RefPtr<Element> startRoot = prpStartRoot;
1030 RefPtr<Element> endRoot = prpEndRoot;
1032 startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false));
1033 if (endRoot && endRoot != startRoot)
1034 endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false));
1037 void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
1039 LOG(Editing, "Editor %p appliedEditing", this);
1041 document().updateLayout();
1043 EditCommandComposition* composition = cmd->composition();
1044 ASSERT(composition);
1045 VisibleSelection newSelection(cmd->endingSelection());
1047 notifyTextFromControls(composition->startingRootEditableElement(), composition->endingRootEditableElement());
1049 // Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
1050 FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0;
1052 changeSelectionAfterCommand(newSelection, options);
1053 dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
1055 updateEditorUINowIfScheduled();
1057 m_alternativeTextController->respondToAppliedEditing(cmd.get());
1059 if (!cmd->preservesTypingStyle())
1060 m_frame.selection().clearTypingStyle();
1062 // Command will be equal to last edit command only in the case of typing
1063 if (m_lastEditCommand.get() == cmd)
1064 ASSERT(cmd->isTypingCommand());
1066 // Only register a new undo command if the command passed in is
1067 // different from the last command
1068 m_lastEditCommand = cmd;
1070 client()->registerUndoStep(m_lastEditCommand->ensureComposition());
1073 respondToChangedContents(newSelection);
1076 void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
1078 document().updateLayout();
1080 notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
1082 VisibleSelection newSelection(cmd->startingSelection());
1083 changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
1084 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
1086 updateEditorUINowIfScheduled();
1088 m_alternativeTextController->respondToUnappliedEditing(cmd.get());
1090 m_lastEditCommand = nullptr;
1092 client()->registerRedoStep(cmd);
1093 respondToChangedContents(newSelection);
1096 void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd)
1098 document().updateLayout();
1100 notifyTextFromControls(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
1102 VisibleSelection newSelection(cmd->endingSelection());
1103 changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
1104 dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
1106 updateEditorUINowIfScheduled();
1108 m_lastEditCommand = nullptr;
1110 client()->registerUndoStep(cmd);
1111 respondToChangedContents(newSelection);
1114 Editor::Editor(Frame& frame)
1116 , m_killRing(std::make_unique<KillRing>())
1117 , m_spellChecker(std::make_unique<SpellChecker>(frame))
1118 , m_alternativeTextController(std::make_unique<AlternativeTextController>(frame))
1119 , m_editorUIUpdateTimer(*this, &Editor::editorUIUpdateTimerFired)
1120 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
1121 , m_telephoneNumberDetectionUpdateTimer(*this, &Editor::scanSelectionForTelephoneNumbers)
1130 void Editor::clear()
1132 if (m_compositionNode) {
1133 m_compositionNode = nullptr;
1134 if (EditorClient* client = this->client())
1135 client->discardedComposition(&m_frame);
1137 m_customCompositionUnderlines.clear();
1138 m_shouldStyleWithCSS = false;
1139 m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv;
1142 bool Editor::insertText(const String& text, Event* triggeringEvent)
1144 return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent);
1147 bool Editor::insertTextForConfirmedComposition(const String& text)
1149 return m_frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition);
1152 bool Editor::insertDictatedText(const String& text, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent)
1154 return m_alternativeTextController->insertDictatedText(text, dictationAlternatives, triggeringEvent);
1157 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
1162 VisibleSelection selection = selectionForCommand(triggeringEvent);
1163 if (!selection.isContentEditable())
1165 RefPtr<Range> range = selection.toNormalizedRange();
1167 if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
1170 updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
1172 bool shouldConsiderApplyingAutocorrection = false;
1173 if (text == " " || text == "\t")
1174 shouldConsiderApplyingAutocorrection = true;
1176 if (text.length() == 1 && u_ispunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0]))
1177 shouldConsiderApplyingAutocorrection = true;
1179 bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
1181 // Get the selection to use for the event that triggered this insertText.
1182 // If the event handler changed the selection, we may want to use a different selection
1183 // that is contained in the event target.
1184 selection = selectionForCommand(triggeringEvent);
1185 if (selection.isContentEditable()) {
1186 if (Node* selectionStart = selection.start().deprecatedNode()) {
1187 Ref<Document> document(selectionStart->document());
1190 if (triggeringEvent && triggeringEvent->isDictation())
1191 DictationCommand::insertText(document, text, triggeringEvent->dictationAlternatives(), selection);
1193 TypingCommand::Options options = 0;
1194 if (selectInsertedText)
1195 options |= TypingCommand::SelectInsertedText;
1196 if (autocorrectionWasApplied)
1197 options |= TypingCommand::RetainAutocorrectionIndicator;
1198 TypingCommand::insertText(document, text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
1201 // Reveal the current selection
1202 if (Frame* editedFrame = document->frame())
1203 if (Page* page = editedFrame->page()) {
1205 SelectionRevealMode revealMode = SelectionRevealMode::RevealUpToMainFrame;
1207 SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
1209 page->focusController().focusedOrMainFrame().selection().revealSelection(revealMode, ScrollAlignment::alignCenterIfNeeded);
1217 bool Editor::insertLineBreak()
1222 if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped))
1225 VisiblePosition caret = m_frame.selection().selection().visibleStart();
1226 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret);
1227 bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
1228 TypingCommand::insertLineBreak(document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1229 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded);
1234 bool Editor::insertParagraphSeparator()
1239 if (!canEditRichly())
1240 return insertLineBreak();
1242 if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped))
1245 VisiblePosition caret = m_frame.selection().selection().visibleStart();
1246 bool alignToEdge = isEndOfEditableOrNonEditableContent(caret);
1247 bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
1248 TypingCommand::insertParagraphSeparator(document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1249 revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded);
1254 bool Editor::insertParagraphSeparatorInQuotedContent()
1256 // FIXME: Why is this missing calls to canEdit, canEditRichly, etc.?
1257 TypingCommand::insertParagraphSeparatorInQuotedContent(document());
1258 revealSelectionAfterEditingOperation();
1265 return; // DHTML did the whole operation
1271 performCutOrCopy(CutAction);
1277 return; // DHTML did the whole operation
1283 performCutOrCopy(CopyAction);
1286 void Editor::postTextStateChangeNotificationForCut(const String& text, const VisibleSelection& selection)
1288 if (!AXObjectCache::accessibilityEnabled())
1292 AXObjectCache* cache = document().existingAXObjectCache();
1295 cache->postTextStateChangeNotification(selection.start().anchorNode(), AXTextEditTypeCut, text, selection.start());
1298 void Editor::performCutOrCopy(EditorActionSpecifier action)
1300 RefPtr<Range> selection = selectedRange();
1301 willWriteSelectionToPasteboard(selection);
1302 if (action == CutAction) {
1303 if (!shouldDeleteRange(selection.get()))
1306 updateMarkersForWordsAffectedByEditing(true);
1309 if (enclosingTextFormControl(m_frame.selection().selection().start()))
1310 Pasteboard::createForCopyAndPaste()->writePlainText(selectedTextForDataTransfer(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
1312 HTMLImageElement* imageElement = nullptr;
1313 if (action == CopyAction)
1314 imageElement = imageElementFromImageDocument(document());
1317 #if PLATFORM(COCOA) || PLATFORM(EFL) || PLATFORM(GTK)
1318 writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *imageElement, document().url(), document().title());
1320 Pasteboard::createForCopyAndPaste()->writeImage(*imageElement, document().url(), document().title());
1323 #if PLATFORM(COCOA) || PLATFORM(EFL) || PLATFORM(GTK)
1324 writeSelectionToPasteboard(*Pasteboard::createForCopyAndPaste());
1326 // FIXME: Convert all other platforms to match Mac and delete this.
1327 Pasteboard::createForCopyAndPaste()->writeSelection(*selection, canSmartCopyOrDelete(), m_frame, IncludeImageAltTextForDataTransfer);
1332 didWriteSelectionToPasteboard();
1333 if (action == CutAction) {
1335 if (AXObjectCache::accessibilityEnabled())
1336 text = AccessibilityObject::stringForVisiblePositionRange(m_frame.selection().selection());
1337 deleteSelectionWithSmartDelete(canSmartCopyOrDelete(), EditActionCut);
1338 if (AXObjectCache::accessibilityEnabled())
1339 postTextStateChangeNotificationForCut(text, m_frame.selection().selection());
1343 void Editor::paste()
1345 paste(*Pasteboard::createForCopyAndPaste());
1348 void Editor::paste(Pasteboard& pasteboard)
1350 if (tryDHTMLPaste())
1351 return; // DHTML did the whole operation
1354 updateMarkersForWordsAffectedByEditing(false);
1355 ResourceCacheValidationSuppressor validationSuppressor(document().cachedResourceLoader());
1356 if (m_frame.selection().selection().isContentRichlyEditable())
1357 pasteWithPasteboard(&pasteboard, true);
1359 pasteAsPlainTextWithPasteboard(pasteboard);
1362 void Editor::pasteAsPlainText()
1364 if (tryDHTMLPaste())
1368 updateMarkersForWordsAffectedByEditing(false);
1369 pasteAsPlainTextWithPasteboard(*Pasteboard::createForCopyAndPaste());
1372 void Editor::performDelete()
1379 addRangeToKillRing(*selectedRange().get(), KillRingInsertionMode::AppendText);
1380 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1382 // clear the "start new kill ring sequence" setting, because it was set to true
1383 // when the selection was updated by deleting the range
1384 setStartNewKillRingSequence(false);
1387 void Editor::simplifyMarkup(Node* startNode, Node* endNode)
1392 if (&startNode->document() != &endNode->document())
1394 // check if start node is before endNode
1395 Node* node = startNode;
1396 while (node && node != endNode)
1397 node = NodeTraversal::next(*node);
1402 applyCommand(SimplifyMarkupCommand::create(document(), startNode, endNode ? NodeTraversal::next(*endNode) : nullptr));
1405 void Editor::copyURL(const URL& url, const String& title)
1407 copyURL(url, title, *Pasteboard::createForCopyAndPaste());
1410 void Editor::copyURL(const URL& url, const String& title, Pasteboard& pasteboard)
1412 PasteboardURL pasteboardURL;
1413 pasteboardURL.url = url;
1414 pasteboardURL.title = title;
1417 fillInUserVisibleForm(pasteboardURL);
1420 pasteboard.write(pasteboardURL);
1424 void Editor::copyImage(const HitTestResult& result)
1426 Element* element = result.innerNonSharedElement();
1430 URL url = result.absoluteLinkURL();
1432 url = result.absoluteImageURL();
1434 #if PLATFORM(COCOA) || PLATFORM(EFL) || PLATFORM(GTK)
1435 writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *element, url, result.altDisplayString());
1437 Pasteboard::createForCopyAndPaste()->writeImage(*element, url, result.altDisplayString());
1442 bool Editor::isContinuousSpellCheckingEnabled() const
1444 return client() && client()->isContinuousSpellCheckingEnabled();
1447 void Editor::toggleContinuousSpellChecking()
1450 client()->toggleContinuousSpellChecking();
1453 bool Editor::isGrammarCheckingEnabled()
1455 return client() && client()->isGrammarCheckingEnabled();
1458 void Editor::toggleGrammarChecking()
1461 client()->toggleGrammarChecking();
1464 int Editor::spellCheckerDocumentTag()
1466 return client() ? client()->spellCheckerDocumentTag() : 0;
1471 void Editor::uppercaseWord()
1474 client()->uppercaseWord();
1477 void Editor::lowercaseWord()
1480 client()->lowercaseWord();
1483 void Editor::capitalizeWord()
1486 client()->capitalizeWord();
1491 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
1493 void Editor::showSubstitutionsPanel()
1496 LOG_ERROR("No NSSpellChecker");
1500 if (client()->substitutionsPanelIsShowing()) {
1501 client()->showSubstitutionsPanel(false);
1504 client()->showSubstitutionsPanel(true);
1507 bool Editor::substitutionsPanelIsShowing()
1511 return client()->substitutionsPanelIsShowing();
1514 void Editor::toggleSmartInsertDelete()
1517 client()->toggleSmartInsertDelete();
1520 bool Editor::isAutomaticQuoteSubstitutionEnabled()
1522 return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1525 void Editor::toggleAutomaticQuoteSubstitution()
1528 client()->toggleAutomaticQuoteSubstitution();
1531 bool Editor::isAutomaticLinkDetectionEnabled()
1533 return client() && client()->isAutomaticLinkDetectionEnabled();
1536 void Editor::toggleAutomaticLinkDetection()
1539 client()->toggleAutomaticLinkDetection();
1542 bool Editor::isAutomaticDashSubstitutionEnabled()
1544 return client() && client()->isAutomaticDashSubstitutionEnabled();
1547 void Editor::toggleAutomaticDashSubstitution()
1550 client()->toggleAutomaticDashSubstitution();
1553 bool Editor::isAutomaticTextReplacementEnabled()
1555 return client() && client()->isAutomaticTextReplacementEnabled();
1558 void Editor::toggleAutomaticTextReplacement()
1561 client()->toggleAutomaticTextReplacement();
1564 bool Editor::isAutomaticSpellingCorrectionEnabled()
1566 return m_alternativeTextController->isAutomaticSpellingCorrectionEnabled();
1569 void Editor::toggleAutomaticSpellingCorrection()
1572 client()->toggleAutomaticSpellingCorrection();
1577 bool Editor::shouldEndEditing(Range* range)
1579 return client() && client()->shouldEndEditing(range);
1582 bool Editor::shouldBeginEditing(Range* range)
1584 return client() && client()->shouldBeginEditing(range);
1587 void Editor::clearUndoRedoOperations()
1590 client()->clearUndoRedoOperations();
1593 bool Editor::canUndo()
1595 return client() && client()->canUndo();
1604 bool Editor::canRedo()
1606 return client() && client()->canRedo();
1615 void Editor::didBeginEditing()
1618 client()->didBeginEditing();
1621 void Editor::didEndEditing()
1624 client()->didEndEditing();
1627 void Editor::willWriteSelectionToPasteboard(PassRefPtr<Range> range)
1630 client()->willWriteSelectionToPasteboard(range.get());
1633 void Editor::didWriteSelectionToPasteboard()
1636 client()->didWriteSelectionToPasteboard();
1639 void Editor::toggleBold()
1641 command("ToggleBold").execute();
1644 void Editor::toggleUnderline()
1646 command("ToggleUnderline").execute();
1649 void Editor::setBaseWritingDirection(WritingDirection direction)
1652 if (inSameParagraph(m_frame.selection().selection().visibleStart(), m_frame.selection().selection().visibleEnd()) &&
1653 baseWritingDirectionForSelectionStart() == direction)
1657 Element* focusedElement = document().focusedElement();
1658 if (is<HTMLTextFormControlElement>(focusedElement)) {
1659 if (direction == NaturalWritingDirection)
1661 downcast<HTMLTextFormControlElement>(*focusedElement).setAttributeWithoutSynchronization(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1662 focusedElement->dispatchInputEvent();
1663 document().updateStyleIfNeeded();
1667 RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
1668 style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1669 applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1672 WritingDirection Editor::baseWritingDirectionForSelectionStart() const
1674 WritingDirection result = LeftToRightWritingDirection;
1676 Position pos = m_frame.selection().selection().visibleStart().deepEquivalent();
1677 Node* node = pos.deprecatedNode();
1681 auto renderer = node->renderer();
1685 if (!renderer->isRenderBlockFlow()) {
1686 renderer = renderer->containingBlock();
1691 switch (renderer->style().direction()) {
1693 return LeftToRightWritingDirection;
1695 return RightToLeftWritingDirection;
1701 void Editor::selectComposition()
1703 RefPtr<Range> range = compositionRange();
1707 // The composition can start inside a composed character sequence, so we have to override checks.
1708 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1709 VisibleSelection selection;
1710 selection.setWithoutValidation(range->startPosition(), range->endPosition());
1711 m_frame.selection().setSelection(selection, 0);
1714 void Editor::confirmComposition()
1716 if (!m_compositionNode)
1718 setComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition);
1721 void Editor::cancelComposition()
1723 if (!m_compositionNode)
1725 setComposition(emptyString(), CancelComposition);
1728 bool Editor::cancelCompositionIfSelectionIsInvalid()
1732 if (!hasComposition() || ignoreCompositionSelectionChange() || getCompositionSelection(start, end))
1735 cancelComposition();
1739 void Editor::confirmComposition(const String& text)
1741 setComposition(text, ConfirmComposition);
1744 void Editor::setComposition(const String& text, SetCompositionMode mode)
1746 ASSERT(mode == ConfirmComposition || mode == CancelComposition);
1747 UserTypingGestureIndicator typingGestureIndicator(m_frame);
1749 setIgnoreCompositionSelectionChange(true);
1751 if (mode == CancelComposition)
1752 ASSERT(text == emptyString());
1754 selectComposition();
1756 if (m_frame.selection().isNone()) {
1757 setIgnoreCompositionSelectionChange(false);
1761 // Dispatch a compositionend event to the focused node.
1762 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
1763 // the DOM Event specification.
1764 if (Element* target = document().focusedElement()) {
1765 Ref<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text);
1766 target->dispatchEvent(event);
1769 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1770 // will delete the old composition with an optimized replace operation.
1771 if (text.isEmpty() && mode != CancelComposition)
1772 TypingCommand::deleteSelection(document(), 0);
1774 m_compositionNode = nullptr;
1775 m_customCompositionUnderlines.clear();
1777 insertTextForConfirmedComposition(text);
1779 if (mode == CancelComposition) {
1780 // An open typing command that disagrees about current selection would cause issues with typing later on.
1781 TypingCommand::closeTyping(&m_frame);
1784 setIgnoreCompositionSelectionChange(false);
1787 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1789 UserTypingGestureIndicator typingGestureIndicator(m_frame);
1791 setIgnoreCompositionSelectionChange(true);
1793 // Updates styles before setting selection for composition to prevent
1794 // inserting the previous composition text into text nodes oddly.
1795 // See https://bugs.webkit.org/show_bug.cgi?id=46868
1796 document().updateStyleIfNeeded();
1798 selectComposition();
1800 if (m_frame.selection().isNone()) {
1801 setIgnoreCompositionSelectionChange(false);
1806 client()->startDelayingAndCoalescingContentChangeNotifications();
1809 Element* target = document().focusedElement();
1811 // Dispatch an appropriate composition event to the focused node.
1812 // We check the composition status and choose an appropriate composition event since this
1813 // function is used for three purposes:
1814 // 1. Starting a new composition.
1815 // Send a compositionstart and a compositionupdate event when this function creates
1816 // a new composition node, i.e.
1817 // m_compositionNode == 0 && !text.isEmpty().
1818 // Sending a compositionupdate event at this time ensures that at least one
1819 // compositionupdate event is dispatched.
1820 // 2. Updating the existing composition node.
1821 // Send a compositionupdate event when this function updates the existing composition
1822 // node, i.e. m_compositionNode != 0 && !text.isEmpty().
1823 // 3. Canceling the ongoing composition.
1824 // Send a compositionend event when function deletes the existing composition node, i.e.
1825 // m_compositionNode != 0 && test.isEmpty().
1826 RefPtr<CompositionEvent> event;
1827 if (!m_compositionNode) {
1828 // We should send a compositionstart event only when the given text is not empty because this
1829 // function doesn't create a composition node when the text is empty.
1830 if (!text.isEmpty()) {
1831 target->dispatchEvent(CompositionEvent::create(eventNames().compositionstartEvent, document().domWindow(), selectedText()));
1832 event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text);
1835 if (!text.isEmpty())
1836 event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text);
1838 event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text);
1841 target->dispatchEvent(*event);
1844 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
1845 // will delete the old composition with an optimized replace operation.
1847 TypingCommand::deleteSelection(document(), TypingCommand::PreventSpellChecking);
1849 m_compositionNode = nullptr;
1850 m_customCompositionUnderlines.clear();
1852 if (!text.isEmpty()) {
1853 TypingCommand::insertText(document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
1855 // Find out what node has the composition now.
1856 Position base = m_frame.selection().selection().base().downstream();
1857 Position extent = m_frame.selection().selection().extent();
1858 Node* baseNode = base.deprecatedNode();
1859 unsigned baseOffset = base.deprecatedEditingOffset();
1860 Node* extentNode = extent.deprecatedNode();
1861 unsigned extentOffset = extent.deprecatedEditingOffset();
1863 if (is<Text>(baseNode) && baseNode == extentNode && baseOffset + text.length() == extentOffset) {
1864 m_compositionNode = downcast<Text>(baseNode);
1865 m_compositionStart = baseOffset;
1866 m_compositionEnd = extentOffset;
1867 m_customCompositionUnderlines = underlines;
1868 for (auto& underline : m_customCompositionUnderlines) {
1869 underline.startOffset += baseOffset;
1870 underline.endOffset += baseOffset;
1872 if (baseNode->renderer())
1873 baseNode->renderer()->repaint();
1875 unsigned start = std::min(baseOffset + selectionStart, extentOffset);
1876 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset);
1877 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1878 m_frame.selection().setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1882 setIgnoreCompositionSelectionChange(false);
1885 client()->stopDelayingAndCoalescingContentChangeNotifications();
1889 void Editor::ignoreSpelling()
1894 RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange();
1896 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1898 String text = selectedText();
1899 ASSERT(text.length());
1900 textChecker()->ignoreWordInSpellDocument(text);
1903 void Editor::learnSpelling()
1908 // 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>.
1910 RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange();
1912 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1914 String text = selectedText();
1915 ASSERT(text.length());
1916 textChecker()->learnWord(text);
1920 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1922 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1923 // then we wrap and search from the doc start to (approximately) where we started.
1925 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
1926 // repeated "check spelling" commands work.
1927 VisibleSelection selection(m_frame.selection().selection());
1928 RefPtr<Range> spellingSearchRange(rangeOfContents(document()));
1930 bool startedWithSelection = false;
1931 if (selection.start().deprecatedNode()) {
1932 startedWithSelection = true;
1933 if (startBeforeSelection) {
1934 VisiblePosition start(selection.visibleStart());
1935 // We match AppKit's rule: Start 1 character before the selection.
1936 VisiblePosition oneBeforeStart = start.previous();
1937 setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1939 setStart(spellingSearchRange.get(), selection.visibleEnd());
1942 Position position = spellingSearchRange->startPosition();
1943 if (!isEditablePosition(position)) {
1944 // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1945 // selection is editable.
1946 // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1947 // when spell checking the whole document before sending the message.
1948 // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1950 position = VisiblePosition(firstEditablePositionAfterPositionInRoot(position, document().documentElement())).deepEquivalent();
1951 if (position.isNull())
1954 Position rangeCompliantPosition = position.parentAnchoredEquivalent();
1955 if (rangeCompliantPosition.deprecatedNode())
1956 spellingSearchRange->setStart(*rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), IGNORE_EXCEPTION);
1957 startedWithSelection = false; // won't need to wrap
1960 // topNode defines the whole range we want to operate on
1961 auto* topNode = highestEditableRoot(position);
1962 // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
1964 spellingSearchRange->setEnd(*topNode, lastOffsetForEditing(*topNode), IGNORE_EXCEPTION);
1966 // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1967 // at a word boundary. Going back by one char and then forward by a word does the trick.
1968 if (startedWithSelection) {
1969 VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1970 if (oneBeforeStart.isNotNull())
1971 setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1972 // else we were already at the start of the editable node
1975 if (spellingSearchRange->collapsed())
1976 return; // nothing to search in
1978 // Get the spell checker if it is available
1982 // We go to the end of our first range instead of the start of it, just to be sure
1983 // we don't get foiled by any word boundary problems at the start. It means we might
1984 // do a tiny bit more searching.
1985 Node& searchEndNodeAfterWrap = spellingSearchRange->endContainer();
1986 int searchEndOffsetAfterWrap = spellingSearchRange->endOffset();
1988 int misspellingOffset = 0;
1989 GrammarDetail grammarDetail;
1990 int grammarPhraseOffset = 0;
1991 RefPtr<Range> grammarSearchRange;
1992 String badGrammarPhrase;
1993 String misspelledWord;
1995 bool isSpelling = true;
1996 int foundOffset = 0;
1998 RefPtr<Range> firstMisspellingRange;
1999 if (unifiedTextCheckerEnabled()) {
2000 grammarSearchRange = spellingSearchRange->cloneRange();
2001 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
2003 misspelledWord = foundItem;
2004 misspellingOffset = foundOffset;
2006 badGrammarPhrase = foundItem;
2007 grammarPhraseOffset = foundOffset;
2010 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
2012 #if USE(GRAMMAR_CHECKING)
2013 grammarSearchRange = spellingSearchRange->cloneRange();
2014 if (!misspelledWord.isEmpty()) {
2015 // Stop looking at start of next misspelled word
2016 CharacterIterator chars(*grammarSearchRange);
2017 chars.advance(misspellingOffset);
2018 grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
2021 if (isGrammarCheckingEnabled())
2022 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
2026 // 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
2027 // block rather than at a selection).
2028 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
2030 spellingSearchRange->setStart(*topNode, 0, IGNORE_EXCEPTION);
2031 // going until the end of the very first chunk we tested is far enough
2032 spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, IGNORE_EXCEPTION);
2034 if (unifiedTextCheckerEnabled()) {
2035 grammarSearchRange = spellingSearchRange->cloneRange();
2036 foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
2038 misspelledWord = foundItem;
2039 misspellingOffset = foundOffset;
2041 badGrammarPhrase = foundItem;
2042 grammarPhraseOffset = foundOffset;
2045 misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
2047 #if USE(GRAMMAR_CHECKING)
2048 grammarSearchRange = spellingSearchRange->cloneRange();
2049 if (!misspelledWord.isEmpty()) {
2050 // Stop looking at start of next misspelled word
2051 CharacterIterator chars(*grammarSearchRange);
2052 chars.advance(misspellingOffset);
2053 grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
2056 if (isGrammarCheckingEnabled())
2057 badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
2062 #if !USE(GRAMMAR_CHECKING)
2063 ASSERT(badGrammarPhrase.isEmpty());
2064 UNUSED_PARAM(grammarPhraseOffset);
2066 if (!badGrammarPhrase.isEmpty()) {
2067 // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
2068 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
2069 // panel, and store a marker so we draw the green squiggle later.
2071 ASSERT(badGrammarPhrase.length() > 0);
2072 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
2074 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
2075 RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
2076 m_frame.selection().setSelection(VisibleSelection(*badGrammarRange, SEL_DEFAULT_AFFINITY));
2077 m_frame.selection().revealSelection();
2079 client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
2080 document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
2083 if (!misspelledWord.isEmpty()) {
2084 // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
2085 // a marker so we draw the red squiggle later.
2087 RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
2088 m_frame.selection().setSelection(VisibleSelection(*misspellingRange, DOWNSTREAM));
2089 m_frame.selection().revealSelection();
2091 client()->updateSpellingUIWithMisspelledWord(misspelledWord);
2092 document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2095 #endif // !PLATFORM(IOS)
2097 String Editor::misspelledWordAtCaretOrRange(Node* clickedNode) const
2099 if (!isContinuousSpellCheckingEnabled() || !clickedNode || !isSpellCheckingEnabledFor(clickedNode))
2102 VisibleSelection selection = m_frame.selection().selection();
2103 if (!selection.isContentEditable() || selection.isNone())
2106 VisibleSelection wordSelection(selection.base());
2107 wordSelection.expandUsingGranularity(WordGranularity);
2108 RefPtr<Range> wordRange = wordSelection.toNormalizedRange();
2110 // In compliance with GTK+ applications, additionally allow to provide suggestions when the current
2111 // selection exactly match the word selection.
2112 if (selection.isRange() && !areRangesEqual(wordRange.get(), selection.toNormalizedRange().get()))
2115 String word = wordRange->text();
2116 if (word.isEmpty() || !client())
2119 int wordLength = word.length();
2120 int misspellingLocation = -1;
2121 int misspellingLength = 0;
2122 textChecker()->checkSpellingOfString(word, &misspellingLocation, &misspellingLength);
2124 return misspellingLength == wordLength ? word : String();
2127 String Editor::misspelledSelectionString() const
2129 String selectedString = selectedText();
2130 int length = selectedString.length();
2131 if (!length || !client())
2134 int misspellingLocation = -1;
2135 int misspellingLength = 0;
2136 textChecker()->checkSpellingOfString(selectedString, &misspellingLocation, &misspellingLength);
2138 // The selection only counts as misspelled if the selected text is exactly one misspelled word
2139 if (misspellingLength != length)
2142 // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
2143 // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
2144 // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
2145 // or a grammar error.
2146 client()->updateSpellingUIWithMisspelledWord(selectedString);
2148 return selectedString;
2151 bool Editor::isSelectionUngrammatical()
2153 #if USE(GRAMMAR_CHECKING)
2154 RefPtr<Range> range = m_frame.selection().toNormalizedRange();
2157 return TextCheckingHelper(client(), range).isUngrammatical();
2163 Vector<String> Editor::guessesForMisspelledWord(const String& word) const
2165 ASSERT(word.length());
2167 Vector<String> guesses;
2169 textChecker()->getGuessesForWord(word, String(), m_frame.selection().selection(), guesses);
2173 Vector<String> Editor::guessesForMisspelledOrUngrammatical(bool& misspelled, bool& ungrammatical)
2175 if (unifiedTextCheckerEnabled()) {
2176 RefPtr<Range> range;
2177 VisibleSelection selection = m_frame.selection().selection();
2178 if (selection.isCaret() && behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
2179 VisibleSelection wordSelection = VisibleSelection(selection.base());
2180 wordSelection.expandUsingGranularity(WordGranularity);
2181 range = wordSelection.toNormalizedRange();
2183 range = selection.toNormalizedRange();
2185 return Vector<String>();
2186 return TextCheckingHelper(client(), range).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical);
2189 String misspelledWord = behavior().shouldAllowSpellingSuggestionsWithoutSelection() ? misspelledWordAtCaretOrRange(document().focusedElement()) : misspelledSelectionString();
2190 misspelled = !misspelledWord.isEmpty();
2191 // Only unified text checker supports guesses for ungrammatical phrases.
2192 ungrammatical = false;
2195 return guessesForMisspelledWord(misspelledWord);
2196 return Vector<String>();
2199 void Editor::showSpellingGuessPanel()
2202 LOG_ERROR("No NSSpellChecker");
2206 if (client()->spellingUIIsShowing()) {
2207 client()->showSpellingUI(false);
2212 advanceToNextMisspelling(true);
2214 client()->showSpellingUI(true);
2217 bool Editor::spellingPanelIsShowing()
2221 return client()->spellingUIIsShowing();
2224 void Editor::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
2226 RefPtr<Range> selectedRange = movingSelection.toNormalizedRange();
2227 if (selectedRange) {
2228 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
2229 document().markers().removeMarkers(selectedRange.get(), DocumentMarker::Grammar);
2233 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
2235 markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled(), movingSelection);
2238 void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement)
2241 UNUSED_PARAM(selectionAfterTyping);
2242 UNUSED_PARAM(doReplacement);
2243 TextCheckingTypeMask textCheckingOptions = 0;
2244 if (isContinuousSpellCheckingEnabled())
2245 textCheckingOptions |= TextCheckingTypeSpelling;
2246 if (!(textCheckingOptions & TextCheckingTypeSpelling))
2249 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
2250 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
2252 #if !USE(AUTOMATIC_TEXT_REPLACEMENT)
2253 UNUSED_PARAM(doReplacement);
2256 if (unifiedTextCheckerEnabled()) {
2257 m_alternativeTextController->applyPendingCorrection(selectionAfterTyping);
2259 TextCheckingTypeMask textCheckingOptions = 0;
2261 if (isContinuousSpellCheckingEnabled())
2262 textCheckingOptions |= TextCheckingTypeSpelling;
2264 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
2266 && (isAutomaticQuoteSubstitutionEnabled()
2267 || isAutomaticLinkDetectionEnabled()
2268 || isAutomaticDashSubstitutionEnabled()
2269 || isAutomaticTextReplacementEnabled()
2270 || ((textCheckingOptions & TextCheckingTypeSpelling) && isAutomaticSpellingCorrectionEnabled())))
2271 textCheckingOptions |= TextCheckingTypeReplacement;
2273 if (!(textCheckingOptions & (TextCheckingTypeSpelling | TextCheckingTypeReplacement)))
2276 if (isGrammarCheckingEnabled())
2277 textCheckingOptions |= TextCheckingTypeGrammar;
2279 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
2280 if (textCheckingOptions & TextCheckingTypeGrammar) {
2281 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
2282 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
2284 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
2288 if (!isContinuousSpellCheckingEnabled())
2291 // Check spelling of one word
2292 RefPtr<Range> misspellingRange;
2293 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
2295 // Autocorrect the misspelled word.
2296 if (!misspellingRange)
2299 // Get the misspelled word.
2300 const String misspelledWord = plainText(misspellingRange.get());
2301 String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
2303 // If autocorrected word is non empty, replace the misspelled word by this word.
2304 if (!autocorrectedString.isEmpty()) {
2305 VisibleSelection newSelection(*misspellingRange, DOWNSTREAM);
2306 if (newSelection != m_frame.selection().selection()) {
2307 if (!m_frame.selection().shouldChangeSelection(newSelection))
2309 m_frame.selection().setSelection(newSelection);
2312 if (!m_frame.editor().shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
2314 m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false, EditActionInsert);
2316 // Reset the charet one character further.
2317 m_frame.selection().moveTo(m_frame.selection().selection().end());
2318 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
2321 if (!isGrammarCheckingEnabled())
2324 // Check grammar of entire sentence
2325 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
2329 void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
2332 // This function is called with a selection already expanded to word boundaries.
2333 // Might be nice to assert that here.
2335 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
2336 // grammar checking can only be on if spell checking is also on.
2337 if (!isContinuousSpellCheckingEnabled())
2340 RefPtr<Range> searchRange(selection.toNormalizedRange());
2344 // If we're not in an editable node, bail.
2345 Node& editableNode = searchRange->startContainer();
2346 if (!editableNode.hasEditableStyle())
2349 if (!isSpellCheckingEnabledFor(&editableNode))
2352 // Get the spell checker if it is available
2356 TextCheckingHelper checker(client(), searchRange);
2358 checker.markAllMisspellings(firstMisspellingRange);
2360 #if USE(GRAMMAR_CHECKING)
2361 if (isGrammarCheckingEnabled())
2362 checker.markAllBadGrammar();
2364 ASSERT_NOT_REACHED();
2368 UNUSED_PARAM(selection);
2369 UNUSED_PARAM(checkSpelling);
2370 UNUSED_PARAM(firstMisspellingRange);
2371 #endif // !PLATFORM(IOS)
2374 bool Editor::isSpellCheckingEnabledFor(Node* node) const
2378 Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
2381 if (element->isInUserAgentShadowTree()) {
2382 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(firstPositionInOrBeforeNode(element)))
2383 return textControl->isSpellCheckingEnabled();
2385 return element->isSpellCheckingEnabled();
2388 bool Editor::isSpellCheckingEnabledInFocusedNode() const
2390 return isSpellCheckingEnabledFor(m_frame.selection().selection().start().deprecatedNode());
2393 void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2395 markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
2398 void Editor::markBadGrammar(const VisibleSelection& selection)
2400 #if USE(GRAMMAR_CHECKING)
2401 RefPtr<Range> firstMisspellingRange;
2402 markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
2404 ASSERT_NOT_REACHED();
2408 void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
2410 ASSERT(unifiedTextCheckerEnabled());
2412 // There shouldn't be pending autocorrection at this moment.
2413 ASSERT(!m_alternativeTextController->hasPendingCorrection());
2415 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
2416 bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel;
2418 // This function is called with selections already expanded to word boundaries.
2419 if (!client() || !spellingRange || (shouldMarkGrammar && !grammarRange))
2422 // If we're not in an editable node, bail.
2423 Node& editableNode = spellingRange->startContainer();
2424 if (!editableNode.hasEditableStyle())
2427 if (!isSpellCheckingEnabledFor(&editableNode))
2430 Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
2431 TextCheckingParagraph paragraphToCheck(rangeToCheck);
2432 if (paragraphToCheck.isEmpty())
2434 RefPtr<Range> paragraphRange = paragraphToCheck.paragraphRange();
2436 bool asynchronous = m_frame.settings().asynchronousSpellCheckingEnabled() && !shouldShowCorrectionPanel;
2438 // In asynchronous mode, we intentionally check paragraph-wide sentence.
2439 auto request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessIncremental, asynchronous ? paragraphRange : rangeToCheck, paragraphRange);
2442 m_spellChecker->requestCheckingFor(WTFMove(request));
2446 Vector<TextCheckingResult> results;
2447 checkTextOfParagraph(*textChecker(), paragraphToCheck.text(), resolveTextCheckingTypeMask(textCheckingOptions), results, m_frame.selection().selection());
2448 markAndReplaceFor(WTFMove(request), results);
2451 static bool isAutomaticTextReplacementType(TextCheckingType type)
2454 case TextCheckingTypeNone:
2455 case TextCheckingTypeSpelling:
2456 case TextCheckingTypeGrammar:
2458 case TextCheckingTypeLink:
2459 case TextCheckingTypeQuote:
2460 case TextCheckingTypeDash:
2461 case TextCheckingTypeReplacement:
2462 case TextCheckingTypeCorrection:
2463 case TextCheckingTypeShowCorrectionPanel:
2466 ASSERT_NOT_REACHED();
2470 static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, PassRefPtr<Range> rangeToReplace, const String& replacement, int resultLocation, int resultLength)
2472 ContainerNode* scope = downcast<ContainerNode>(paragraph.paragraphRange()->startContainer().rootNode());
2474 size_t paragraphLocation;
2475 size_t paragraphLength;
2476 TextIterator::getLocationAndLengthFromRange(scope, paragraph.paragraphRange().get(), paragraphLocation, paragraphLength);
2478 applyCommand(SpellingCorrectionCommand::create(rangeToReplace, replacement));
2480 // TextCheckingParagraph may be orphaned after SpellingCorrectionCommand mutated DOM.
2481 // See <rdar://10305315>, http://webkit.org/b/89526.
2483 RefPtr<Range> newParagraphRange = TextIterator::rangeFromLocationAndLength(scope, paragraphLocation, paragraphLength + replacement.length() - resultLength);
2485 paragraph = TextCheckingParagraph(TextIterator::subrange(newParagraphRange.get(), resultLocation, replacement.length()), newParagraphRange);
2488 void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results)
2492 TextCheckingTypeMask textCheckingOptions = request->data().mask();
2493 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraphRange());
2495 const bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
2496 const bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
2497 const bool shouldMarkLink = textCheckingOptions & TextCheckingTypeLink;
2498 const bool shouldPerformReplacement = textCheckingOptions & (TextCheckingTypeQuote | TextCheckingTypeDash | TextCheckingTypeReplacement);
2499 const bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel;
2500 const bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & TextCheckingTypeCorrection);
2501 #if !USE(AUTOCORRECTION_PANEL)
2502 ASSERT(!shouldShowCorrectionPanel);
2505 // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2506 int selectionOffset = 0;
2507 bool useAmbiguousBoundaryOffset = false;
2508 bool selectionChanged = false;
2509 bool restoreSelectionAfterChange = false;
2510 bool adjustSelectionForParagraphBoundaries = false;
2512 if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) {
2513 if (m_frame.selection().selection().selectionType() == VisibleSelection::CaretSelection) {
2514 // Attempt to save the caret position so we can restore it later if needed
2515 Position caretPosition = m_frame.selection().selection().end();
2516 selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION);
2517 restoreSelectionAfterChange = true;
2518 if (selectionOffset > 0 && (selectionOffset > paragraph.textLength() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter))
2519 adjustSelectionForParagraphBoundaries = true;
2520 if (selectionOffset > 0 && selectionOffset <= paragraph.textLength() && isAmbiguousBoundaryCharacter(paragraph.textCharAt(selectionOffset - 1)))
2521 useAmbiguousBoundaryOffset = true;
2525 int offsetDueToReplacement = 0;
2527 for (unsigned i = 0; i < results.size(); i++) {
2528 const int spellingRangeEndOffset = paragraph.checkingEnd() + offsetDueToReplacement;
2529 const TextCheckingType resultType = results[i].type;
2530 const int resultLocation = results[i].location + offsetDueToReplacement;
2531 const int resultLength = results[i].length;
2532 const int resultEndLocation = resultLocation + resultLength;
2533 const String& replacement = results[i].replacement;
2534 const bool resultEndsAtAmbiguousBoundary = useAmbiguousBoundaryOffset && selectionOffset - 1 <= resultEndLocation;
2536 // Only mark misspelling if:
2537 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
2538 // 2. Result falls within spellingRange.
2539 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
2540 // "wouldn'" as misspelled right after apostrophe is typed.
2541 if (shouldMarkSpelling && !shouldShowCorrectionPanel && resultType == TextCheckingTypeSpelling
2542 && resultLocation >= paragraph.checkingStart() && resultEndLocation <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
2543 ASSERT(resultLength > 0 && resultLocation >= 0);
2544 RefPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength);
2545 if (!m_alternativeTextController->isSpellingMarkerAllowed(misspellingRange))
2547 misspellingRange->startContainer().document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling, replacement);
2548 } else if (shouldMarkGrammar && resultType == TextCheckingTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
2549 ASSERT(resultLength > 0 && resultLocation >= 0);
2550 for (auto& detail : results[i].details) {
2551 ASSERT(detail.length > 0 && detail.location >= 0);
2552 if (paragraph.checkingRangeCovers(resultLocation + detail.location, detail.length)) {
2553 RefPtr<Range> badGrammarRange = paragraph.subrange(resultLocation + detail.location, detail.length);
2554 badGrammarRange->startContainer().document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail.userDescription);
2557 } else if (resultEndLocation <= spellingRangeEndOffset && resultEndLocation >= paragraph.checkingStart()
2558 && isAutomaticTextReplacementType(resultType)) {
2559 // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
2560 ASSERT(resultLength > 0 && resultLocation >= 0);
2562 if (shouldShowCorrectionPanel && (resultEndLocation < spellingRangeEndOffset
2563 || !(resultType & (TextCheckingTypeReplacement | TextCheckingTypeCorrection))))
2566 // Apply replacement if:
2567 // 1. The replacement length is non-zero.
2568 // 2. The result doesn't end at an ambiguous boundary.
2569 // (FIXME: this is required until 6853027 is fixed and text checking can do this for us
2570 bool doReplacement = replacement.length() > 0 && !resultEndsAtAmbiguousBoundary;
2571 RefPtr<Range> rangeToReplace = paragraph.subrange(resultLocation, resultLength);
2573 // adding links should be done only immediately after they are typed
2574 if (resultType == TextCheckingTypeLink && selectionOffset != resultEndLocation + 1)
2577 if (!(shouldPerformReplacement || shouldCheckForCorrection || shouldMarkLink) || !doReplacement)
2580 String replacedString = plainText(rangeToReplace.get());
2581 const bool existingMarkersPermitReplacement = m_alternativeTextController->processMarkersOnTextToBeReplacedByResult(&results[i], rangeToReplace.get(), replacedString);
2582 if (!existingMarkersPermitReplacement)
2585 if (shouldShowCorrectionPanel) {
2586 if (resultEndLocation == spellingRangeEndOffset) {
2587 // We only show the correction panel on the last word.
2588 m_alternativeTextController->show(rangeToReplace, replacement);
2591 // If this function is called for showing correction panel, we ignore other correction or replacement.
2595 VisibleSelection selectionToReplace(*rangeToReplace, DOWNSTREAM);
2596 if (selectionToReplace != m_frame.selection().selection()) {
2597 if (!m_frame.selection().shouldChangeSelection(selectionToReplace))
2601 if (resultType == TextCheckingTypeLink) {
2602 m_frame.selection().setSelection(selectionToReplace);
2603 selectionChanged = true;
2604 restoreSelectionAfterChange = false;
2605 if (canEditRichly())
2606 applyCommand(CreateLinkCommand::create(document(), replacement));
2607 } else if (canEdit() && shouldInsertText(replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
2608 correctSpellcheckingPreservingTextCheckingParagraph(paragraph, rangeToReplace, replacement, resultLocation, resultLength);
2610 if (AXObjectCache* cache = document().existingAXObjectCache()) {
2611 if (Element* root = m_frame.selection().selection().rootEditableElement())
2612 cache->postNotification(root, AXObjectCache::AXAutocorrectionOccured);
2615 // Skip all other results for the replaced text.
2616 while (i + 1 < results.size() && results[i + 1].location + offsetDueToReplacement <= resultLocation)
2619 selectionChanged = true;
2620 offsetDueToReplacement += replacement.length() - resultLength;
2621 if (resultLocation < selectionOffset)
2622 selectionOffset += replacement.length() - resultLength;
2624 // Add a marker so that corrections can easily be undone and won't be re-corrected.
2625 if (resultType == TextCheckingTypeCorrection)
2626 m_alternativeTextController->markCorrection(paragraph.subrange(resultLocation, replacement.length()), replacedString);
2631 if (selectionChanged) {
2632 TextCheckingParagraph extendedParagraph(paragraph);
2633 // Restore the caret position if we have made any replacements
2634 extendedParagraph.expandRangeToNextEnd();
2635 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= extendedParagraph.rangeLength()) {
2636 RefPtr<Range> selectionRange = extendedParagraph.subrange(0, selectionOffset);
2637 m_frame.selection().moveTo(selectionRange->endPosition(), DOWNSTREAM);
2638 if (adjustSelectionForParagraphBoundaries)
2639 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
2641 // If this fails for any reason, the fallback is to go one position beyond the last replacement
2642 m_frame.selection().moveTo(m_frame.selection().selection().end());
2643 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
2648 void Editor::changeBackToReplacedString(const String& replacedString)
2651 ASSERT(unifiedTextCheckerEnabled());
2653 if (replacedString.isEmpty())
2656 RefPtr<Range> selection = selectedRange();
2657 if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
2660 m_alternativeTextController->recordAutocorrectionResponseReversed(replacedString, selection);
2661 TextCheckingParagraph paragraph(selection);
2662 replaceSelectionWithText(replacedString, false, false, EditActionInsert);
2663 RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
2664 changedRange->startContainer().document().markers().addMarker(changedRange.get(), DocumentMarker::Replacement, String());
2665 m_alternativeTextController->markReversed(changedRange.get());
2667 ASSERT_NOT_REACHED();
2668 UNUSED_PARAM(replacedString);
2669 #endif // !PLATFORM(IOS)
2673 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
2675 if (unifiedTextCheckerEnabled()) {
2676 if (!isContinuousSpellCheckingEnabled())
2679 // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
2680 TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
2681 if (markGrammar && isGrammarCheckingEnabled())
2682 textCheckingOptions |= TextCheckingTypeGrammar;
2683 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
2687 RefPtr<Range> firstMisspellingRange;
2688 markMisspellings(spellingSelection, firstMisspellingRange);
2690 markBadGrammar(grammarSelection);
2693 void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
2695 m_alternativeTextController->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
2698 void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
2700 if (!document().markers().hasMarkers())
2703 if (!m_alternativeTextController->shouldRemoveMarkersUponEditing() && (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)))
2706 // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
2707 // several scenarios:
2708 // 1. Insert in the middle of a word.
2709 // 2. Appending non whitespace at the beginning of word.
2710 // 3. Appending non whitespace at the end of word.
2711 // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
2712 // remove the markers on that word.
2713 // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
2714 // selection, and remove words between the selection boundaries.
2716 VisiblePosition startOfSelection = m_frame.selection().selection().start();
2717 VisiblePosition endOfSelection = m_frame.selection().selection().end();
2718 if (startOfSelection.isNull())
2720 // First word is the word that ends after or on the start of selection.
2721 VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
2722 VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
2723 // Last word is the word that begins before or on the end of selection
2724 VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
2725 VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
2727 if (startOfFirstWord.isNull()) {
2728 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
2729 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
2732 if (endOfLastWord.isNull()) {
2733 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
2734 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
2737 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
2738 // we choose next word as the first word.
2739 if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) {
2740 startOfFirstWord = nextWordPosition(startOfFirstWord);
2741 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
2742 if (startOfFirstWord == endOfSelection)
2746 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
2747 // we choose previous word as the last word.
2748 if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) {
2749 startOfLastWord = previousWordPosition(startOfLastWord);
2750 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
2751 if (endOfLastWord == startOfSelection)
2755 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
2758 // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
2759 // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
2760 // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
2761 // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
2762 // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
2763 // of marker that contains the word in question, and remove marker on that whole range.
2764 RefPtr<Range> wordRange = Range::create(document(), startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
2766 Vector<RenderedDocumentMarker*> markers = document().markers().markersInRange(wordRange.get(), DocumentMarker::DictationAlternatives);
2767 for (auto* marker : markers)
2768 m_alternativeTextController->removeDictationAlternativesForMarker(marker);
2771 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives | DocumentMarker::DictationPhraseWithAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker);
2773 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::Grammar | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker);
2775 document().markers().clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement);
2778 void Editor::deletedAutocorrectionAtPosition(const Position& position, const String& originalString)
2780 m_alternativeTextController->deletedAutocorrectionAtPosition(position, originalString);
2783 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
2785 Document* document = m_frame.documentAtPoint(windowPoint);
2789 Frame* frame = document->frame();
2791 FrameView* frameView = frame->view();
2794 IntPoint framePoint = frameView->windowToContents(windowPoint);
2795 VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
2797 return selection.toNormalizedRange();
2800 void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
2802 if (m_ignoreCompositionSelectionChange)
2806 SelectionRevealMode revealMode = SelectionRevealMode::RevealUpToMainFrame;
2808 SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
2811 m_frame.selection().revealSelection(revealMode, alignment, revealExtentOption);
2814 void Editor::setIgnoreCompositionSelectionChange(bool ignore, RevealSelection shouldRevealExistingSelection)
2816 if (m_ignoreCompositionSelectionChange == ignore)
2819 m_ignoreCompositionSelectionChange = ignore;
2821 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
2823 respondToChangedSelection(m_frame.selection().selection(), 0);
2825 if (!ignore && shouldRevealExistingSelection == RevealSelection::Yes)
2826 revealSelectionAfterEditingOperation(ScrollAlignment::alignToEdgeIfNeeded, RevealExtent);
2829 RefPtr<Range> Editor::compositionRange() const
2831 if (!m_compositionNode)
2833 unsigned length = m_compositionNode->length();
2834 unsigned start = std::min(m_compositionStart, length);
2835 unsigned end = std::min(std::max(start, m_compositionEnd), length);
2838 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
2841 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
2843 if (!m_compositionNode)
2845 const VisibleSelection& selection = m_frame.selection().selection();
2846 Position start = selection.start();
2847 if (start.deprecatedNode() != m_compositionNode)
2849 Position end = selection.end();
2850 if (end.deprecatedNode() != m_compositionNode)
2853 if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart)
2855 if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd)
2858 selectionStart = start.deprecatedEditingOffset() - m_compositionStart;
2859 selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd;
2863 void Editor::transpose()
2868 VisibleSelection selection = m_frame.selection().selection();
2869 if (!selection.isCaret())
2872 // Make a selection that goes back one character and forward two characters.
2873 VisiblePosition caret = selection.visibleStart();
2874 VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
2875 VisiblePosition previous = next.previous();
2876 if (next == previous)
2878 previous = previous.previous();
2879 if (!inSameParagraph(next, previous))
2881 RefPtr<Range> range = makeRange(previous, next);
2884 VisibleSelection newSelection(*range, DOWNSTREAM);
2886 // Transpose the two characters.
2887 String text = plainText(range.get());
2888 if (text.length() != 2)
2890 String transposed = text.right(1) + text.left(1);
2892 // Select the two characters.
2893 if (newSelection != m_frame.selection().selection()) {
2894 if (!m_frame.selection().shouldChangeSelection(newSelection))
2896 m_frame.selection().setSelection(newSelection);
2899 // Insert the transposed characters.
2900 if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2902 replaceSelectionWithText(transposed, false, false, EditActionInsert);
2905 void Editor::addRangeToKillRing(const Range& range, KillRingInsertionMode mode)
2907 addTextToKillRing(plainText(&range), mode);
2910 void Editor::addTextToKillRing(const String& text, KillRingInsertionMode mode)
2912 if (m_shouldStartNewKillRingSequence)
2913 killRing().startNewSequence();
2915 m_shouldStartNewKillRingSequence = false;
2917 // If the kill was from a backwards motion, prepend to the kill ring.
2918 // This will ensure that alternating forward and backward kills will
2919 // build up the original string in the kill ring without permuting it.
2921 case KillRingInsertionMode::PrependText:
2922 killRing().prepend(text);
2924 case KillRingInsertionMode::AppendText:
2925 killRing().append(text);
2930 void Editor::startAlternativeTextUITimer()
2932 m_alternativeTextController->startAlternativeTextUITimer(AlternativeTextTypeCorrection);
2935 void Editor::handleAlternativeTextUIResult(const String& correction)
2937 m_alternativeTextController->handleAlternativeTextUIResult(correction);
2941 void Editor::dismissCorrectionPanelAsIgnored()
2943 m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored);
2946 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options)
2948 // If the new selection is orphaned, then don't update the selection.
2949 if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
2952 // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
2953 // because there is work that it must do in this situation.
2954 // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
2955 // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
2956 bool selectionDidNotChangeDOMPosition = newSelection == m_frame.selection().selection();
2957 if (selectionDidNotChangeDOMPosition || m_frame.selection().shouldChangeSelection(newSelection))
2958 m_frame.selection().setSelection(newSelection, options);
2960 // Some editing operations change the selection visually without affecting its position within the DOM.
2961 // For example when you press return in the following (the caret is marked by ^):
2962 // <div contentEditable="true"><div>^Hello</div></div>
2963 // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
2964 // change the caret's DOM position (["hello", 0]). In these situations the above FrameSelection::setSelection call
2965 // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
2966 // starts a new kill ring sequence, but we want to do these things (matches AppKit).
2968 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
2969 if (m_ignoreCompositionSelectionChange)
2972 if (selectionDidNotChangeDOMPosition && client())
2973 client()->respondToChangedSelection(&m_frame);
2976 String Editor::selectedText() const
2978 return selectedText(TextIteratorDefaultBehavior);
2981 String Editor::selectedTextForDataTransfer() const
2983 if (m_frame.settings().selectionIncludesAltImageText())
2984 return selectedText(TextIteratorEmitsImageAltText);
2985 return selectedText();
2988 String Editor::selectedText(TextIteratorBehavior behavior) const
2990 // We remove '\0' characters because they are not visibly rendered to the user.
2991 return plainText(m_frame.selection().toNormalizedRange().get(), behavior).replaceWithLiteral('\0', "");
2994 static inline void collapseCaretWidth(IntRect& rect)
2996 // FIXME: Width adjustment doesn't work for rotated text.
2997 if (rect.width() == caretWidth)
2999 else if (rect.height() == caretWidth)
3003 IntRect Editor::firstRectForRange(Range* range) const
3005 VisiblePosition startVisiblePosition(range->startPosition(), DOWNSTREAM);
3007 if (range->collapsed()) {
3008 // FIXME: Getting caret rect and removing caret width is a very roundabout way to get collapsed range location.
3009 // In particular, width adjustment doesn't work for rotated text.
3010 IntRect startCaretRect = RenderedPosition(startVisiblePosition).absoluteRect();
3011 collapseCaretWidth(startCaretRect);
3012 return startCaretRect;
3015 VisiblePosition endVisiblePosition(range->endPosition(), UPSTREAM);
3017 if (inSameLine(startVisiblePosition, endVisiblePosition))
3018 return enclosingIntRect(RenderObject::absoluteBoundingBoxRectForRange(range));
3020 LayoutUnit extraWidthToEndOfLine = 0;
3021 IntRect startCaretRect = RenderedPosition(startVisiblePosition).absoluteRect(&extraWidthToEndOfLine);
3022 if (startCaretRect == IntRect())
3025 // When start and end aren't on the same line, we want to go from start to the end of its line.
3026 bool textIsHorizontal = startCaretRect.width() == caretWidth;
3027 return textIsHorizontal ?
3028 IntRect(startCaretRect.x(),
3030 startCaretRect.width() + extraWidthToEndOfLine,
3031 startCaretRect.height()) :
3032 IntRect(startCaretRect.x(),
3034 startCaretRect.width(),
3035 startCaretRect.height() + extraWidthToEndOfLine);
3038 bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const
3041 if (m_frame.selectionChangeCallbacksDisabled())
3044 return client() && client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting);
3047 void Editor::computeAndSetTypingStyle(EditingStyle& style, EditAction editingAction)
3049 if (style.isEmpty()) {
3050 m_frame.selection().clearTypingStyle();
3054 // Calculate the current typing style.
3055 RefPtr<EditingStyle> typingStyle;
3056 if (auto existingTypingStyle = m_frame.selection().typingStyle())
3057 typingStyle = existingTypingStyle->copy();
3059 typingStyle = EditingStyle::create();
3060 typingStyle->overrideTypingStyleAt(style, m_frame.selection().selection().visibleStart().deepEquivalent());
3062 // Handle block styles, substracting these from the typing style.
3063 RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties();
3064 if (!blockStyle->isEmpty())
3065 applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction));
3067 // Set the remaining style as the typing style.
3068 m_frame.selection().setTypingStyle(typingStyle);
3071 void Editor::computeAndSetTypingStyle(StyleProperties& properties, EditAction editingAction)
3073 return computeAndSetTypingStyle(EditingStyle::create(&properties), editingAction);
3076 void Editor::textFieldDidBeginEditing(Element* e)
3079 client()->textFieldDidBeginEditing(e);
3082 void Editor::textFieldDidEndEditing(Element* e)
3084 dismissCorrectionPanelAsIgnored();
3086 client()->textFieldDidEndEditing(e);
3089 void Editor::textDidChangeInTextField(Element* e)
3092 client()->textDidChangeInTextField(e);
3095 bool Editor::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
3098 return client()->doTextFieldCommandFromEvent(e, ke);
3103 void Editor::textWillBeDeletedInTextField(Element* input)
3106 client()->textWillBeDeletedInTextField(input);
3109 void Editor::textDidChangeInTextArea(Element* e)
3112 client()->textDidChangeInTextArea(e);
3115 void Editor::applyEditingStyleToBodyElement() const
3117 auto collection = document().getElementsByTagName(HTMLNames::bodyTag.localName());
3118 unsigned length = collection->length();
3119 for (unsigned i = 0; i < length; ++i)
3120 applyEditingStyleToElement(collection->item(i));
3123 void Editor::applyEditingStyleToElement(Element* element) const
3125 ASSERT(!element || is<StyledElement>(*element));
3126 if (!is<StyledElement>(element))
3129 // Mutate using the CSSOM wrapper so we get the same event behavior as a script.
3130 CSSStyleDeclaration* style = downcast<StyledElement>(*element).cssomStyle();
3131 style->setPropertyInternal(CSSPropertyWordWrap, "break-word", false, IGNORE_EXCEPTION);
3132 style->setPropertyInternal(CSSPropertyWebkitNbspMode, "space", false, IGNORE_EXCEPTION);
3133 style->setPropertyInternal(CSSPropertyWebkitLineBreak, "after-white-space", false, IGNORE_EXCEPTION);
3136 bool Editor::findString(const String& target, FindOptions options)
3138 VisibleSelection selection = m_frame.selection().selection();
3140 RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options);
3145 m_frame.selection().setSelection(VisibleSelection(*resultRange, DOWNSTREAM));
3147 if (!(options & DoNotRevealSelection))
3148 m_frame.selection().revealSelection();
3153 RefPtr<Range> Editor::findStringAndScrollToVisible(const String& target, Range* previousMatch, FindOptions options)
3155 RefPtr<Range> nextMatch = rangeOfString(target, previousMatch, options);
3159 nextMatch->firstNode()->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, nextMatch->absoluteBoundingBox(),
3160 ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded);
3165 RefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
3167 if (target.isEmpty())
3170 // Start from an edge of the reference range, if there's a reference range that's not in shadow content. Which edge
3171 // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
3172 RefPtr<Range> searchRange(rangeOfContents(document()));
3174 bool forward = !(options & Backwards);
3175 bool startInReferenceRange = referenceRange && (options & StartInSelection);
3176 if (referenceRange) {
3178 searchRange->setStart(startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition());
3180 searchRange->setEnd(startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition());
3183 RefPtr<Node> shadowTreeRoot = referenceRange ? referenceRange->startContainer().nonBoundaryShadowTreeRootNode() : nullptr;
3184 if (shadowTreeRoot) {
3186 searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes());
3188 searchRange->setStart(*shadowTreeRoot, 0);
3191 RefPtr<Range> resultRange = findPlainText(*searchRange, target, options);
3192 // If we started in the reference range and the found range exactly matches the reference range, find again.
3193 // Build a selection with the found range to remove collapsed whitespace.
3194 // Compare ranges instead of selection objects to ignore the way that the current selection was made.
3195 if (startInReferenceRange && areRangesEqual(VisibleSelection(*resultRange).toNormalizedRange().get(), referenceRange)) {
3196 searchRange = rangeOfContents(document());
3198 searchRange->setStart(referenceRange->endPosition());
3200 searchRange->setEnd(referenceRange->startPosition());
3202 if (shadowTreeRoot) {
3204 searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes());
3206 searchRange->setStart(*shadowTreeRoot, 0);
3209 resultRange = findPlainText(*searchRange, target, options);
3212 // If nothing was found in the shadow tree, search in main content following the shadow tree.
3213 if (resultRange->collapsed() && shadowTreeRoot) {
3214 searchRange = rangeOfContents(document());
3215 if (shadowTreeRoot->shadowHost()) {
3217 searchRange->setStartAfter(*shadowTreeRoot->shadowHost());
3219 searchRange->setEndBefore(*shadowTreeRoot->shadowHost());
3222 resultRange = findPlainText(*searchRange, target, options);
3225 // If we didn't find anything and we're wrapping, search again in the entire document (this will
3226 // redundantly re-search the area already searched in some cases).
3227 if (resultRange->collapsed() && options & WrapAround) {
3228 searchRange = rangeOfContents(document());
3229 resultRange = findPlainText(*searchRange, target, options);
3230 // We used to return false here if we ended up with the same range that we started with
3231 // (e.g., the reference range was already the only instance of this text). But we decided that
3232 // this should be a success case instead, so we'll just fall through in that case.
3235 return resultRange->collapsed() ? nullptr : resultRange;
3238 static bool isFrameInRange(Frame* frame, Range* range)
3240 bool inRange = false;
3241 for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document().ownerElement()) {
3242 if (&ownerElement->document() == &range->ownerDocument()) {
3243 inRange = range->intersectsNode(*ownerElement, IGNORE_EXCEPTION);
3250 unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches, Vector<RefPtr<Range>>* matches)
3252 if (target.isEmpty())
3255 RefPtr<Range> searchRange;
3257 if (&range->ownerDocument() == &document())
3258 searchRange = range;
3259 else if (!isFrameInRange(&m_frame, range))
3263 searchRange = rangeOfContents(document());
3265 Node& originalEndContainer = searchRange->endContainer();
3266 int originalEndOffset = searchRange->endOffset();
3268 unsigned matchCount = 0;
3270 RefPtr<Range> resultRange(findPlainText(*searchRange, target, options & ~Backwards));
3271 if (resultRange->collapsed()) {
3272 if (!resultRange->startContainer().isInShadowTree())
3275 searchRange->setStartAfter(*resultRange->startContainer().shadowHost(), IGNORE_EXCEPTION);
3276 searchRange->setEnd(originalEndContainer, originalEndOffset, IGNORE_EXCEPTION);
3282 matches->append(resultRange);
3285 document().markers().addMarker(resultRange.get(), DocumentMarker::TextMatch);
3287 // Stop looking if we hit the specified limit. A limit of 0 means no limit.
3288 if (limit > 0 && matchCount >= limit)
3291 // Set the new start for the search range to be the end of the previous
3292 // result range. There is no need to use a VisiblePosition here,
3293 // since findPlainText will use a TextIterator to go over the visible
3295 searchRange->setStart(resultRange->endContainer(), resultRange->endOffset(), IGNORE_EXCEPTION);
3297 Node* shadowTreeRoot = searchRange->shadowRoot();
3298 if (searchRange->collapsed() && shadowTreeRoot)
3299 searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes(), IGNORE_EXCEPTION);
3305 void Editor::setMarkedTextMatchesAreHighlighted(bool flag)
3307 if (flag == m_areMarkedTextMatchesHighlighted)
3310 m_areMarkedTextMatchesHighlighted = flag;
3311 document().markers().repaintMarkers(DocumentMarker::TextMatch);
3314 void Editor::respondToChangedSelection(const VisibleSelection&, FrameSelection::SetSelectionOptions options)
3317 // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
3318 if (m_ignoreCompositionSelectionChange)
3323 client()->respondToChangedSelection(&m_frame);
3325 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
3326 if (shouldDetectTelephoneNumbers())
3327 m_telephoneNumberDetectionUpdateTimer.startOneShot(0);
3330 setStartNewKillRingSequence(true);
3332 if (m_editorUIUpdateTimer.isActive())
3335 // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
3336 m_editorUIUpdateTimerShouldCheckSpellingAndGrammar = options & FrameSelection::CloseTyping
3337 && !(options & FrameSelection::SpellCorrectionTriggered);
3338 m_editorUIUpdateTimerWasTriggeredByDictation = options & FrameSelection::DictationTriggered;
3339 m_editorUIUpdateTimer.startOneShot(0);
3342 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
3344 bool Editor::shouldDetectTelephoneNumbers()
3346 if (!m_frame.document())
3348 return document().isTelephoneNumberParsingEnabled() && TelephoneNumberDetector::isSupported();
3351 void Editor::scanSelectionForTelephoneNumbers()
3353 if (!shouldDetectTelephoneNumbers() || !client())
3356 m_detectedTelephoneNumberRanges.clear();
3358 Vector<RefPtr<Range>> markedRanges;
3360 FrameSelection& frameSelection = m_frame.selection();
3361 if (!frameSelection.isRange()) {
3362 m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
3365 RefPtr<Range> selectedRange = frameSelection.toNormalizedRange();
3367 // Extend the range a few characters in each direction to detect incompletely selected phone numbers.
3368 static const int charactersToExtend = 15;
3369 const VisibleSelection& visibleSelection = frameSelection.selection();
3370 Position start = visibleSelection.start();
3371 Position end = visibleSelection.end();
3372 for (int i = 0; i < charactersToExtend; ++i) {
3373 start = start.previous(Character);
3374 end = end.next(Character);
3377 FrameSelection extendedSelection;
3378 extendedSelection.setStart(start);
3379 extendedSelection.setEnd(end);
3380 RefPtr<Range> extendedRange = extendedSelection.toNormalizedRange();
3382 if (!extendedRange) {
3383 m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
3387 scanRangeForTelephoneNumbers(*extendedRange, extendedRange->text(), markedRanges);
3389 // Only consider ranges with a detected telephone number if they overlap with the actual selection range.
3390 for (auto& range : markedRanges) {
3391 if (rangesOverlap(range.get(), selectedRange.get()))
3392 m_detectedTelephoneNumberRanges.append(range);
3395 m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
3398 void Editor::scanRangeForTelephoneNumbers(Range& range, const StringView& stringView, Vector<RefPtr<Range>>& markedRanges)
3400 // Don't scan for phone numbers inside editable regions.
3401 Node& startNode = range.startContainer();
3402 if (startNode.hasEditableStyle())
3405 // relativeStartPosition and relativeEndPosition are the endpoints of the phone number range,
3406 // relative to the scannerPosition
3407 unsigned length = stringView.length();
3408 unsigned scannerPosition = 0;
3409 int relativeStartPosition = 0;
3410 int relativeEndPosition = 0;
3412 auto characters = stringView.upconvertedCharacters();
3414 while (scannerPosition < length && TelephoneNumberDetector::find(&characters[scannerPosition], length - scannerPosition, &relativeStartPosition, &relativeEndPosition)) {
3415 // The convention in the Data Detectors framework is that the end position is the first character NOT in the phone number
3416 // (that is, the length of the range is relativeEndPosition - relativeStartPosition). So subtract 1 to get the same
3417 // convention as the old WebCore phone number parser (so that the rest of the code is still valid if we want to go back
3418 // to the old parser).
3419 --relativeEndPosition;
3421 ASSERT(scannerPosition + relativeEndPosition < length);
3423 unsigned subrangeOffset = scannerPosition + relativeStartPosition;
3424 unsigned subrangeLength = relativeEndPosition - relativeStartPosition + 1;
3426 RefPtr<Range> subrange = TextIterator::subrange(&range, subrangeOffset, subrangeLength);
3428 markedRanges.append(subrange);
3429 range.ownerDocument().markers().addMarker(subrange.get(), DocumentMarker::TelephoneNumber);
3431 scannerPosition += relativeEndPosition + 1;
3435 #endif // ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
3437 void Editor::updateEditorUINowIfScheduled()
3439 if (!m_editorUIUpdateTimer.isActive())
3441 m_editorUIUpdateTimer.stop();
3442 editorUIUpdateTimerFired();
3445 void Editor::editorUIUpdateTimerFired()
3447 VisibleSelection oldSelection = m_oldSelectionForEditorUIUpdate;
3449 m_alternativeTextController->stopPendingCorrection(oldSelection);
3451 bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
3452 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
3453 if (isContinuousSpellCheckingEnabled) {
3454 VisibleSelection newAdjacentWords;
3455 VisibleSelection newSelectedSentence;
3456 bool caretBrowsing = m_frame.settings().caretBrowsingEnabled();
3457 if (m_frame.selection().selection().isContentEditable() || caretBrowsing) {
3458 VisiblePosition newStart(m_frame.selection().selection().visibleStart());
3460 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3462 // If this bug gets fixed, this PLATFORM(IOS) code could be removed:
3463 // <rdar://problem/7259611> Word boundary code on iPhone gives different results than desktop
3464 EWordSide startWordSide = LeftWordIfOnBoundary;
3465 UChar32 c = newStart.characterBefore();
3466 // FIXME: VisiblePosition::characterAfter() and characterBefore() do not emit newlines the same
3467 // way as TextIterator, so we do an isStartOfParagraph check here.
3468 if (isSpaceOrNewline(c) || c == 0xA0 || isStartOfParagraph(newStart)) {
3469 startWordSide = RightWordIfOnBoundary;
3471 newAdjacentWords = VisibleSelection(startOfWord(newStart, startWordSide), endOfWord(newStart, RightWordIfOnBoundary));
3472 #endif // !PLATFORM(IOS)
3473 if (isContinuousGrammarCheckingEnabled)
3474 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
3477 // When typing we check spelling elsewhere, so don't redo it here.
3478 // If this is a change in selection resulting from a delete operation,
3479 // oldSelection may no longer be in the document.
3480 if (m_editorUIUpdateTimerShouldCheckSpellingAndGrammar && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) {
3481 VisiblePosition oldStart(oldSelection.visibleStart());
3482 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
3483 if (oldAdjacentWords != newAdjacentWords) {
3484 if (isContinuousGrammarCheckingEnabled) {
3485 VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
3486 markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence);
3488 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
3492 if (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)) {
3493 if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
3494 document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling);
3496 if (!textChecker() || textChecker()->shouldEraseMarkersAfterChangeSelection(TextCheckingTypeGrammar)) {
3497 if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
3498 document().markers().removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
3502 // When continuous spell checking is off, existing markers disappear after the selection changes.
3503 if (!isContinuousSpellCheckingEnabled)
3504 document().markers().removeMarkers(DocumentMarker::Spelling);
3505 if (!isContinuousGrammarCheckingEnabled)
3506 document().markers().removeMarkers(DocumentMarker::Grammar);
3508 if (!m_editorUIUpdateTimerWasTriggeredByDictation)
3509 m_alternativeTextController->respondToChangedSelection(oldSelection);
3511 m_oldSelectionForEditorUIUpdate = m_frame.selection().selection();
3514 static Node* findFirstMarkable(Node* node)
3517 if (!node->renderer())
3519 if (node->renderer()->isTextOrLineBreak())
3521 if (is<HTMLTextFormControlElement>(*node))
3522 node = downcast<HTMLTextFormControlElement>(*node).visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
3523 else if (node->firstChild())
3524 node = node->firstChild();
3526 node = node->nextSibling();
3532 bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
3534 Node* node = findFirstMarkable(m_frame.selection().selection().start().deprecatedNode());
3538 unsigned int startOffset = static_cast<unsigned int>(from);
3539 unsigned int endOffset = static_cast<unsigned int>(from + length);
3540 Vector<RenderedDocumentMarker*> markers = document().markers().markersFor(node);
3541 for (auto* marker : markers) {
3542 if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType)
3549 TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions)
3551 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
3552 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
3554 bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel;
3555 bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & TextCheckingTypeCorrection);
3558 TextCheckingTypeMask checkingTypes = 0;
3559 if (shouldMarkSpelling)
3560 checkingTypes |= TextCheckingTypeSpelling;
3561 if (shouldMarkGrammar)
3562 checkingTypes |= TextCheckingTypeGrammar;
3564 if (shouldCheckForCorrection)
3565 checkingTypes |= TextCheckingTypeCorrection;
3566 if (shouldShowCorrectionPanel)
3567 checkingTypes |= TextCheckingTypeShowCorrectionPanel;
3569 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
3570 bool shouldPerformReplacement = textCheckingOptions & TextCheckingTypeReplacement;
3571 if (shouldPerformReplacement) {
3572 if (isAutomaticLinkDetectionEnabled())
3573 checkingTypes |= TextCheckingTypeLink;
3574 if (isAutomaticQuoteSubstitutionEnabled())
3575 checkingTypes |= TextCheckingTypeQuote;
3576 if (isAutomaticDashSubstitutionEnabled())
3577 checkingTypes |= TextCheckingTypeDash;
3578 if (isAutomaticTextReplacementEnabled())
3579 checkingTypes |= TextCheckingTypeReplacement;
3580 if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled())
3581 checkingTypes |= TextCheckingTypeCorrection;
3584 #endif // !PLATFORM(IOS)
3586 return checkingTypes;
3589 static RefPtr<Range> candidateRangeForSelection(Frame& frame)
3591 const VisibleSelection& selection = frame.selection().selection();
3592 return selection.isCaret() ? wordRangeFromPosition(selection.start()) : frame.selection().toNormalizedRange();
3595 static bool candidateWouldReplaceText(const VisibleSelection& selection)
3597 // If the character behind the caret in the current selection is anything but a space or a newline then we should
3598 // replace the whole current word with the candidate.
3599 UChar32 characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection = 0;
3600 charactersAroundPosition(selection.visibleStart(), characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection);
3601 return !(characterBeforeSelection == '\0' || characterBeforeSelection == '\n' || characterBeforeSelection == ' ');
3604 String Editor::stringForCandidateRequest() const
3606 const VisibleSelection& selection = m_frame.selection().selection();
3607 RefPtr<Range> rangeForCurrentlyTypedString = candidateRangeForSelection(m_frame);
3608 if (rangeForCurrentlyTypedString && candidateWouldReplaceText(selection))
3609 return plainText(rangeForCurrentlyTypedString.get());
3614 RefPtr<Range> Editor::contextRangeForCandidateRequest() const
3616 const VisibleSelection& selection = m_frame.selection().selection();
3617 return makeRange(startOfParagraph(selection.visibleStart()), endOfParagraph(selection.visibleEnd()));
3620 void Editor::selectTextCheckingResult(const TextCheckingResult& result)
3625 RefPtr<Range> contextRange = contextRangeForCandidateRequest();
3629 RefPtr<Range> replacementRange = TextIterator::subrange(contextRange.get(), result.location, result.length);
3630 if (!replacementRange)
3633 m_frame.selection().setSelectedRange(replacementRange.get(), UPSTREAM, true);
3636 void Editor::handleAcceptedCandidate(TextCheckingResult acceptedCandidate)
3638 const VisibleSelection& selection = m_frame.selection().selection();
3639 int candidateLength = acceptedCandidate.length;
3641 m_isHandlingAcceptedCandidate = true;
3643 selectTextCheckingResult(acceptedCandidate);
3644 insertText(acceptedCandidate.replacement, 0);
3646 // Some candidates come with a space built in, and we do not need to add another space in that case.
3647 if (!acceptedCandidate.replacement.endsWith(' ')) {
3648 insertText(ASCIILiteral(" "), 0);
3652 RefPtr<Range> insertedCandidateRange = rangeExpandedAroundPositionByCharacters(selection.visibleStart(), candidateLength);
3653 if (insertedCandidateRange)
3654 insertedCandidateRange->startContainer().document().markers().addMarker(insertedCandidateRange.get(), DocumentMarker::AcceptedCandidate, acceptedCandidate.replacement);
3656 m_isHandlingAcceptedCandidate = false;
3659 bool Editor::unifiedTextCheckerEnabled() const
3661 return WebCore::unifiedTextCheckerEnabled(&m_frame);
3664 Vector<String> Editor::dictationAlternativesForMarker(const DocumentMarker* marker)
3666 return m_alternativeTextController->dictationAlternativesForMarker(marker);
3669 void Editor::applyDictationAlternativelternative(const String& alternativeString)
3671 m_alternativeTextController->applyDictationAlternative(alternativeString);
3674 void Editor::toggleOverwriteModeEnabled()
3676 m_overwriteModeEnabled = !m_overwriteModeEnabled;
3677 m_frame.selection().setShouldShowBlockCursor(m_overwriteModeEnabled);
3680 Document& Editor::document() const
3682 ASSERT(m_frame.document());
3683 return *m_frame.document();
3686 } // namespace WebCore