1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "Editor.h"
29 
30 #include "AXObjectCache.h"
31 #include "ApplyStyleCommand.h"
32 #include "CSSComputedStyleDeclaration.h"
33 #include "CSSMutableStyleDeclaration.h"
34 #include "CSSProperty.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSStyleSelector.h"
37 #include "CSSValueKeywords.h"
38 #include "CachedResourceLoader.h"
39 #include "ClipboardEvent.h"
40 #include "CompositionEvent.h"
41 #include "SpellingCorrectionController.h"
42 #include "CreateLinkCommand.h"
43 #include "DeleteButtonController.h"
44 #include "DeleteSelectionCommand.h"
45 #include "DocumentFragment.h"
46 #include "DocumentMarkerController.h"
47 #include "EditingText.h"
48 #include "EditorClient.h"
49 #include "EventHandler.h"
50 #include "EventNames.h"
51 #include "FocusController.h"
52 #include "Frame.h"
53 #include "FrameTree.h"
54 #include "FrameView.h"
55 #include "GraphicsContext.h"
56 #include "HTMLFrameOwnerElement.h"
57 #include "HTMLInputElement.h"
58 #include "HTMLTextAreaElement.h"
59 #include "HitTestResult.h"
60 #include "IndentOutdentCommand.h"
61 #include "InsertListCommand.h"
62 #include "KeyboardEvent.h"
63 #include "KillRing.h"
64 #include "ModifySelectionListLevel.h"
65 #include "NodeList.h"
66 #include "Page.h"
67 #include "Pasteboard.h"
68 #include "TextCheckingHelper.h"
69 #include "RemoveFormatCommand.h"
70 #include "RenderBlock.h"
71 #include "RenderPart.h"
72 #include "RenderTextControl.h"
73 #include "ReplaceSelectionCommand.h"
74 #include "Settings.h"
75 #include "Sound.h"
76 #include "SpellChecker.h"
77 #include "SpellingCorrectionCommand.h"
78 #include "Text.h"
79 #include "TextEvent.h"
80 #include "TextIterator.h"
81 #include "TypingCommand.h"
82 #include "UserTypingGestureIndicator.h"
83 #include "htmlediting.h"
84 #include "markup.h"
85 #include "visible_units.h"
86 #include <wtf/UnusedParam.h>
87 #include <wtf/unicode/CharacterNames.h>
88 #include <wtf/unicode/Unicode.h>
89 
90 namespace WebCore {
91 
92 using namespace std;
93 using namespace HTMLNames;
94 using namespace WTF;
95 using namespace Unicode;
96 
97 // When an event handler has moved the selection outside of a text control
98 // we should use the target control's selection for this editing operation.
selectionForCommand(Event * event)99 VisibleSelection Editor::selectionForCommand(Event* event)
100 {
101     VisibleSelection selection = m_frame->selection()->selection();
102     if (!event)
103         return selection;
104     // If the target is a text control, and the current selection is outside of its shadow tree,
105     // then use the saved selection for that text control.
106     Node* target = event->target()->toNode();
107     Node* selectionStart = selection.start().deprecatedNode();
108     if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
109         RefPtr<Range> range;
110         if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
111             range = static_cast<HTMLInputElement*>(target)->selection();
112         else if (target->hasTagName(textareaTag))
113             range = static_cast<HTMLTextAreaElement*>(target)->selection();
114 
115         if (range)
116             return VisibleSelection(range.get());
117     }
118     return selection;
119 }
120 
121 // Function considers Mac editing behavior a fallback when Page or Settings is not available.
behavior() const122 EditingBehavior Editor::behavior() const
123 {
124     if (!m_frame || !m_frame->settings())
125         return EditingBehavior(EditingMacBehavior);
126 
127     return EditingBehavior(m_frame->settings()->editingBehaviorType());
128 }
129 
client() const130 EditorClient* Editor::client() const
131 {
132     if (Page* page = m_frame->page())
133         return page->editorClient();
134     return 0;
135 }
136 
137 
textChecker() const138 TextCheckerClient* Editor::textChecker() const
139 {
140     if (EditorClient* owner = client())
141         return owner->textChecker();
142     return 0;
143 }
144 
handleKeyboardEvent(KeyboardEvent * event)145 void Editor::handleKeyboardEvent(KeyboardEvent* event)
146 {
147     if (EditorClient* c = client())
148         c->handleKeyboardEvent(event);
149 }
150 
handleInputMethodKeydown(KeyboardEvent * event)151 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
152 {
153     if (EditorClient* c = client())
154         c->handleInputMethodKeydown(event);
155 }
156 
handleTextEvent(TextEvent * event)157 bool Editor::handleTextEvent(TextEvent* event)
158 {
159     // Default event handling for Drag and Drop will be handled by DragController
160     // so we leave the event for it.
161     if (event->isDrop())
162         return false;
163 
164     if (event->isPaste()) {
165         if (event->pastingFragment())
166             replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle());
167         else
168             replaceSelectionWithText(event->data(), false, event->shouldSmartReplace());
169         return true;
170     }
171 
172     String data = event->data();
173     if (data == "\n") {
174         if (event->isLineBreak())
175             return insertLineBreak();
176         return insertParagraphSeparator();
177     }
178 
179     return insertTextWithoutSendingTextEvent(data, false, event);
180 }
181 
canEdit() const182 bool Editor::canEdit() const
183 {
184     return m_frame->selection()->rootEditableElement();
185 }
186 
canEditRichly() const187 bool Editor::canEditRichly() const
188 {
189     return m_frame->selection()->isContentRichlyEditable();
190 }
191 
192 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
193 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
194 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
195 // normally selectable to implement copy/paste (like divs, or a document body).
196 
canDHTMLCut()197 bool Editor::canDHTMLCut()
198 {
199     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
200 }
201 
canDHTMLCopy()202 bool Editor::canDHTMLCopy()
203 {
204     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
205 }
206 
canDHTMLPaste()207 bool Editor::canDHTMLPaste()
208 {
209     return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
210 }
211 
canCut() const212 bool Editor::canCut() const
213 {
214     return canCopy() && canDelete();
215 }
216 
imageElementFromImageDocument(Document * document)217 static HTMLImageElement* imageElementFromImageDocument(Document* document)
218 {
219     if (!document)
220         return 0;
221     if (!document->isImageDocument())
222         return 0;
223 
224     HTMLElement* body = document->body();
225     if (!body)
226         return 0;
227 
228     Node* node = body->firstChild();
229     if (!node)
230         return 0;
231     if (!node->hasTagName(imgTag))
232         return 0;
233     return static_cast<HTMLImageElement*>(node);
234 }
235 
canCopy() const236 bool Editor::canCopy() const
237 {
238     if (imageElementFromImageDocument(m_frame->document()))
239         return true;
240     SelectionController* selection = m_frame->selection();
241     return selection->isRange() && !selection->isInPasswordField();
242 }
243 
canPaste() const244 bool Editor::canPaste() const
245 {
246     return canEdit();
247 }
248 
canDelete() const249 bool Editor::canDelete() const
250 {
251     SelectionController* selection = m_frame->selection();
252     return selection->isRange() && selection->rootEditableElement();
253 }
254 
canDeleteRange(Range * range) const255 bool Editor::canDeleteRange(Range* range) const
256 {
257     ExceptionCode ec = 0;
258     Node* startContainer = range->startContainer(ec);
259     Node* endContainer = range->endContainer(ec);
260     if (!startContainer || !endContainer)
261         return false;
262 
263     if (!startContainer->rendererIsEditable() || !endContainer->rendererIsEditable())
264         return false;
265 
266     if (range->collapsed(ec)) {
267         VisiblePosition start(Position(startContainer, range->startOffset(ec), Position::PositionIsOffsetInAnchor), DOWNSTREAM);
268         VisiblePosition previous = start.previous();
269         // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
270         if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement())
271             return false;
272     }
273     return true;
274 }
275 
smartInsertDeleteEnabled()276 bool Editor::smartInsertDeleteEnabled()
277 {
278     return client() && client()->smartInsertDeleteEnabled();
279 }
280 
canSmartCopyOrDelete()281 bool Editor::canSmartCopyOrDelete()
282 {
283     return client() && client()->smartInsertDeleteEnabled() && m_frame->selection()->granularity() == WordGranularity;
284 }
285 
isSelectTrailingWhitespaceEnabled()286 bool Editor::isSelectTrailingWhitespaceEnabled()
287 {
288     return client() && client()->isSelectTrailingWhitespaceEnabled();
289 }
290 
deleteWithDirection(SelectionDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)291 bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
292 {
293     if (!canEdit())
294         return false;
295 
296     if (m_frame->selection()->isRange()) {
297         if (isTypingAction) {
298             TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity);
299             revealSelectionAfterEditingOperation();
300         } else {
301             if (killRing)
302                 addToKillRing(selectedRange().get(), false);
303             deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
304             // Implicitly calls revealSelectionAfterEditingOperation().
305         }
306     } else {
307         TypingCommand::Options options = 0;
308         if (canSmartCopyOrDelete())
309             options |= TypingCommand::SmartDelete;
310         if (killRing)
311             options |= TypingCommand::KillRing;
312         switch (direction) {
313         case DirectionForward:
314         case DirectionRight:
315             TypingCommand::forwardDeleteKeyPressed(m_frame->document(), options, granularity);
316             break;
317         case DirectionBackward:
318         case DirectionLeft:
319             TypingCommand::deleteKeyPressed(m_frame->document(), options, granularity);
320             break;
321         }
322         revealSelectionAfterEditingOperation();
323     }
324 
325     // FIXME: We should to move this down into deleteKeyPressed.
326     // clear the "start new kill ring sequence" setting, because it was set to true
327     // when the selection was updated by deleting the range
328     if (killRing)
329         setStartNewKillRingSequence(false);
330 
331     return true;
332 }
333 
deleteSelectionWithSmartDelete(bool smartDelete)334 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
335 {
336     if (m_frame->selection()->isNone())
337         return;
338 
339     applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
340 }
341 
pasteAsPlainText(const String & pastingText,bool smartReplace)342 void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace)
343 {
344     Node* target = findEventTargetFromSelection();
345     if (!target)
346         return;
347     ExceptionCode ec = 0;
348     target->dispatchEvent(TextEvent::createForPlainTextPaste(m_frame->domWindow(), pastingText, smartReplace), ec);
349 }
350 
pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment,bool smartReplace,bool matchStyle)351 void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle)
352 {
353     Node* target = findEventTargetFromSelection();
354     if (!target)
355         return;
356     ExceptionCode ec = 0;
357     target->dispatchEvent(TextEvent::createForFragmentPaste(m_frame->domWindow(), pastingFragment, smartReplace, matchStyle), ec);
358 }
359 
pasteAsPlainTextBypassingDHTML()360 void Editor::pasteAsPlainTextBypassingDHTML()
361 {
362     pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
363 }
364 
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)365 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
366 {
367     String text = pasteboard->plainText(m_frame);
368     if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
369         pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
370 }
371 
372 #if !PLATFORM(MAC)
pasteWithPasteboard(Pasteboard * pasteboard,bool allowPlainText)373 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
374 {
375     RefPtr<Range> range = selectedRange();
376     bool chosePlainText;
377     RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
378     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
379         pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
380 }
381 #endif
382 
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)383 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
384 {
385     return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
386 }
387 
shouldInsertFragment(PassRefPtr<DocumentFragment> fragment,PassRefPtr<Range> replacingDOMRange,EditorInsertAction givenAction)388 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
389 {
390     if (!client())
391         return false;
392 
393     if (fragment) {
394         Node* child = fragment->firstChild();
395         if (child && fragment->lastChild() == child && child->isCharacterDataNode())
396             return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
397     }
398 
399     return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
400 }
401 
replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)402 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
403 {
404     if (m_frame->selection()->isNone() || !fragment)
405         return;
406 
407     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting;
408     if (selectReplacement)
409         options |= ReplaceSelectionCommand::SelectReplacement;
410     if (smartReplace)
411         options |= ReplaceSelectionCommand::SmartReplace;
412     if (matchStyle)
413         options |= ReplaceSelectionCommand::MatchStyle;
414     applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, options, EditActionPaste));
415     revealSelectionAfterEditingOperation();
416 
417     Node* nodeToCheck = m_frame->selection()->rootEditableElement();
418     if (m_spellChecker->canCheckAsynchronously(nodeToCheck))
419         m_spellChecker->requestCheckingFor(textCheckingTypeMaskFor(MarkSpelling | MarkGrammar), nodeToCheck);
420 }
421 
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)422 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
423 {
424     replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
425 }
426 
selectedRange()427 PassRefPtr<Range> Editor::selectedRange()
428 {
429     if (!m_frame)
430         return 0;
431     return m_frame->selection()->toNormalizedRange();
432 }
433 
shouldDeleteRange(Range * range) const434 bool Editor::shouldDeleteRange(Range* range) const
435 {
436     ExceptionCode ec;
437     if (!range || range->collapsed(ec))
438         return false;
439 
440     if (!canDeleteRange(range))
441         return false;
442 
443     return client() && client()->shouldDeleteRange(range);
444 }
445 
tryDHTMLCopy()446 bool Editor::tryDHTMLCopy()
447 {
448     if (m_frame->selection()->isInPasswordField())
449         return false;
450 
451     if (canCopy())
452         // Must be done before oncopy adds types and data to the pboard,
453         // also done for security, as it erases data from the last copy/paste.
454         Pasteboard::generalPasteboard()->clear();
455 
456     return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
457 }
458 
tryDHTMLCut()459 bool Editor::tryDHTMLCut()
460 {
461     if (m_frame->selection()->isInPasswordField())
462         return false;
463 
464     if (canCut())
465         // Must be done before oncut adds types and data to the pboard,
466         // also done for security, as it erases data from the last copy/paste.
467         Pasteboard::generalPasteboard()->clear();
468 
469     return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
470 }
471 
tryDHTMLPaste()472 bool Editor::tryDHTMLPaste()
473 {
474     return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
475 }
476 
writeSelectionToPasteboard(Pasteboard * pasteboard)477 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
478 {
479     pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
480 }
481 
shouldInsertText(const String & text,Range * range,EditorInsertAction action) const482 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
483 {
484     return client() && client()->shouldInsertText(text, range, action);
485 }
486 
shouldShowDeleteInterface(HTMLElement * element) const487 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
488 {
489     return client() && client()->shouldShowDeleteInterface(element);
490 }
491 
respondToChangedSelection(const VisibleSelection & oldSelection)492 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
493 {
494     if (client())
495         client()->respondToChangedSelection();
496     m_deleteButtonController->respondToChangedSelection(oldSelection);
497     m_spellingCorrector->respondToChangedSelection(oldSelection);
498 }
499 
respondToChangedContents(const VisibleSelection & endingSelection)500 void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
501 {
502     if (AXObjectCache::accessibilityEnabled()) {
503         Node* node = endingSelection.start().deprecatedNode();
504         if (node)
505             m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false);
506     }
507 
508     updateMarkersForWordsAffectedByEditing(true);
509 
510     if (client())
511         client()->respondToChangedContents();
512 }
513 
fontForSelection(bool & hasMultipleFonts) const514 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
515 {
516 #if !PLATFORM(QT)
517     hasMultipleFonts = false;
518 
519     if (!m_frame->selection()->isRange()) {
520         Node* nodeToRemove;
521         RenderStyle* style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
522 
523         const SimpleFontData* result = 0;
524         if (style)
525             result = style->font().primaryFont();
526 
527         if (nodeToRemove) {
528             ExceptionCode ec;
529             nodeToRemove->remove(ec);
530             ASSERT(!ec);
531         }
532 
533         return result;
534     }
535 
536     const SimpleFontData* font = 0;
537 
538     RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
539     Node* startNode = range->editingStartPosition().deprecatedNode();
540     if (startNode) {
541         Node* pastEnd = range->pastLastNode();
542         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
543         // unreproducible case where this didn't happen, so check for nil also.
544         for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
545             RenderObject* renderer = n->renderer();
546             if (!renderer)
547                 continue;
548             // FIXME: Are there any node types that have renderers, but that we should be skipping?
549             const SimpleFontData* f = renderer->style()->font().primaryFont();
550             if (!font)
551                 font = f;
552             else if (font != f) {
553                 hasMultipleFonts = true;
554                 break;
555             }
556         }
557     }
558 
559     return font;
560 #else
561     return 0;
562 #endif
563 }
564 
textDirectionForSelection(bool & hasNestedOrMultipleEmbeddings) const565 WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const
566 {
567     hasNestedOrMultipleEmbeddings = true;
568 
569     if (m_frame->selection()->isNone())
570         return NaturalWritingDirection;
571 
572     Position position = m_frame->selection()->selection().start().downstream();
573 
574     Node* node = position.deprecatedNode();
575     if (!node)
576         return NaturalWritingDirection;
577 
578     Position end;
579     if (m_frame->selection()->isRange()) {
580         end = m_frame->selection()->selection().end().upstream();
581 
582         Node* pastLast = Range::create(m_frame->document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
583         for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
584             if (!n->isStyledElement())
585                 continue;
586 
587             RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n);
588             RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
589             if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
590                 continue;
591 
592             int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
593             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
594                 return NaturalWritingDirection;
595         }
596     }
597 
598     if (m_frame->selection()->isCaret()) {
599         RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle();
600         WritingDirection direction;
601         if (typingStyle && typingStyle->textDirection(direction)) {
602             hasNestedOrMultipleEmbeddings = false;
603             return direction;
604         }
605         node = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode();
606     }
607 
608     // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position
609     // to decide.
610     Node* block = enclosingBlock(node);
611     WritingDirection foundDirection = NaturalWritingDirection;
612 
613     for (; node != block; node = node->parentNode()) {
614         if (!node->isStyledElement())
615             continue;
616 
617         RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
618         RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
619         if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
620             continue;
621 
622         int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
623         if (unicodeBidiValue == CSSValueNormal)
624             continue;
625 
626         if (unicodeBidiValue == CSSValueBidiOverride)
627             return NaturalWritingDirection;
628 
629         ASSERT(unicodeBidiValue == CSSValueEmbed);
630         RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
631         if (!direction || !direction->isPrimitiveValue())
632             continue;
633 
634         int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
635         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
636             continue;
637 
638         if (foundDirection != NaturalWritingDirection)
639             return NaturalWritingDirection;
640 
641         // In the range case, make sure that the embedding element persists until the end of the range.
642         if (m_frame->selection()->isRange() && !end.deprecatedNode()->isDescendantOf(node))
643             return NaturalWritingDirection;
644 
645         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
646     }
647     hasNestedOrMultipleEmbeddings = false;
648     return foundDirection;
649 }
650 
hasBidiSelection() const651 bool Editor::hasBidiSelection() const
652 {
653     if (m_frame->selection()->isNone())
654         return false;
655 
656     Node* startNode;
657     if (m_frame->selection()->isRange()) {
658         startNode = m_frame->selection()->selection().start().downstream().deprecatedNode();
659         Node* endNode = m_frame->selection()->selection().end().upstream().deprecatedNode();
660         if (enclosingBlock(startNode) != enclosingBlock(endNode))
661             return false;
662     } else
663         startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode();
664 
665     RenderObject* renderer = startNode->renderer();
666     while (renderer && !renderer->isRenderBlock())
667         renderer = renderer->parent();
668 
669     if (!renderer)
670         return false;
671 
672     RenderStyle* style = renderer->style();
673     if (!style->isLeftToRightDirection())
674         return true;
675 
676     return toRenderBlock(renderer)->containsNonZeroBidiLevel();
677 }
678 
selectionUnorderedListState() const679 TriState Editor::selectionUnorderedListState() const
680 {
681     if (m_frame->selection()->isCaret()) {
682         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag))
683             return TrueTriState;
684     } else if (m_frame->selection()->isRange()) {
685         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag);
686         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag);
687         if (startNode && endNode && startNode == endNode)
688             return TrueTriState;
689     }
690 
691     return FalseTriState;
692 }
693 
selectionOrderedListState() const694 TriState Editor::selectionOrderedListState() const
695 {
696     if (m_frame->selection()->isCaret()) {
697         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag))
698             return TrueTriState;
699     } else if (m_frame->selection()->isRange()) {
700         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag);
701         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag);
702         if (startNode && endNode && startNode == endNode)
703             return TrueTriState;
704     }
705 
706     return FalseTriState;
707 }
708 
insertOrderedList()709 PassRefPtr<Node> Editor::insertOrderedList()
710 {
711     if (!canEditRichly())
712         return 0;
713 
714     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
715     revealSelectionAfterEditingOperation();
716     return newList;
717 }
718 
insertUnorderedList()719 PassRefPtr<Node> Editor::insertUnorderedList()
720 {
721     if (!canEditRichly())
722         return 0;
723 
724     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
725     revealSelectionAfterEditingOperation();
726     return newList;
727 }
728 
canIncreaseSelectionListLevel()729 bool Editor::canIncreaseSelectionListLevel()
730 {
731     return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
732 }
733 
canDecreaseSelectionListLevel()734 bool Editor::canDecreaseSelectionListLevel()
735 {
736     return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
737 }
738 
increaseSelectionListLevel()739 PassRefPtr<Node> Editor::increaseSelectionListLevel()
740 {
741     if (!canEditRichly() || m_frame->selection()->isNone())
742         return 0;
743 
744     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
745     revealSelectionAfterEditingOperation();
746     return newList;
747 }
748 
increaseSelectionListLevelOrdered()749 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
750 {
751     if (!canEditRichly() || m_frame->selection()->isNone())
752         return 0;
753 
754     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
755     revealSelectionAfterEditingOperation();
756     return newList.release();
757 }
758 
increaseSelectionListLevelUnordered()759 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
760 {
761     if (!canEditRichly() || m_frame->selection()->isNone())
762         return 0;
763 
764     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
765     revealSelectionAfterEditingOperation();
766     return newList.release();
767 }
768 
decreaseSelectionListLevel()769 void Editor::decreaseSelectionListLevel()
770 {
771     if (!canEditRichly() || m_frame->selection()->isNone())
772         return;
773 
774     DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
775     revealSelectionAfterEditingOperation();
776 }
777 
removeFormattingAndStyle()778 void Editor::removeFormattingAndStyle()
779 {
780     applyCommand(RemoveFormatCommand::create(m_frame->document()));
781 }
782 
clearLastEditCommand()783 void Editor::clearLastEditCommand()
784 {
785     m_lastEditCommand.clear();
786 }
787 
788 // Returns whether caller should continue with "the default processing", which is the same as
789 // the event handler NOT setting the return value to false
dispatchCPPEvent(const AtomicString & eventType,ClipboardAccessPolicy policy)790 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
791 {
792     Node* target = findEventTargetFromSelection();
793     if (!target)
794         return true;
795 
796     RefPtr<Clipboard> clipboard = newGeneralClipboard(policy, m_frame);
797 
798     ExceptionCode ec = 0;
799     RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
800     target->dispatchEvent(evt, ec);
801     bool noDefaultProcessing = evt->defaultPrevented();
802 
803     // invalidate clipboard here for security
804     clipboard->setAccessPolicy(ClipboardNumb);
805 
806     return !noDefaultProcessing;
807 }
808 
findEventTargetFrom(const VisibleSelection & selection) const809 Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const
810 {
811     Node* target = selection.start().element();
812     if (!target)
813         target = m_frame->document()->body();
814     if (!target)
815         return 0;
816     return target->shadowAncestorNode();
817 
818 }
819 
findEventTargetFromSelection() const820 Node* Editor::findEventTargetFromSelection() const
821 {
822     return findEventTargetFrom(m_frame->selection()->selection());
823 }
824 
applyStyle(CSSStyleDeclaration * style,EditAction editingAction)825 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
826 {
827     switch (m_frame->selection()->selectionType()) {
828     case VisibleSelection::NoSelection:
829         // do nothing
830         break;
831     case VisibleSelection::CaretSelection:
832         computeAndSetTypingStyle(style, editingAction);
833         break;
834     case VisibleSelection::RangeSelection:
835         if (style)
836             applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction));
837         break;
838     }
839 }
840 
shouldApplyStyle(CSSStyleDeclaration * style,Range * range)841 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
842 {
843     return client()->shouldApplyStyle(style, range);
844 }
845 
applyParagraphStyle(CSSStyleDeclaration * style,EditAction editingAction)846 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
847 {
848     switch (m_frame->selection()->selectionType()) {
849     case VisibleSelection::NoSelection:
850         // do nothing
851         break;
852     case VisibleSelection::CaretSelection:
853     case VisibleSelection::RangeSelection:
854         if (style)
855             applyCommand(ApplyStyleCommand::create(m_frame->document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties));
856         break;
857     }
858 }
859 
applyStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)860 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
861 {
862     if (!style || !style->length() || !canEditRichly())
863         return;
864 
865     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
866         applyStyle(style, editingAction);
867 }
868 
applyParagraphStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)869 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
870 {
871     if (!style || !style->length() || !canEditRichly())
872         return;
873 
874     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
875         applyParagraphStyle(style, editingAction);
876 }
877 
selectionStartHasStyle(int propertyID,const String & value) const878 bool Editor::selectionStartHasStyle(int propertyID, const String& value) const
879 {
880     RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value);
881     RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
882     if (!selectionStyle || !selectionStyle->style())
883         return false;
884     return style->triStateOfStyle(selectionStyle->style()) == TrueTriState;
885 }
886 
selectionHasStyle(int propertyID,const String & value) const887 TriState Editor::selectionHasStyle(int propertyID, const String& value) const
888 {
889     RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value);
890     if (!m_frame->selection()->isCaretOrRange())
891         return FalseTriState;
892 
893     if (m_frame->selection()->isCaret()) {
894         RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
895         if (!selectionStyle || !selectionStyle->style())
896             return FalseTriState;
897         return style->triStateOfStyle(selectionStyle->style());
898     }
899 
900     TriState state = FalseTriState;
901     for (Node* node = m_frame->selection()->start().deprecatedNode(); node; node = node->traverseNextNode()) {
902         RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
903         if (nodeStyle) {
904             TriState nodeState = style->triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
905             if (node == m_frame->selection()->start().deprecatedNode())
906                 state = nodeState;
907             else if (state != nodeState && node->isTextNode()) {
908                 state = MixedTriState;
909                 break;
910             }
911         }
912         if (node == m_frame->selection()->end().deprecatedNode())
913             break;
914     }
915 
916     return state;
917 }
918 
selectionStartCSSPropertyValue(int propertyID)919 String Editor::selectionStartCSSPropertyValue(int propertyID)
920 {
921     RefPtr<EditingStyle> selectionStyle = selectionStartStyle();
922     if (!selectionStyle || !selectionStyle->style())
923         return String();
924 
925     String value = selectionStyle->style()->getPropertyValue(propertyID);
926 
927     // If background color is transparent, traverse parent nodes until we hit a different value or document root
928     // Also, if the selection is a range, ignore the background color at the start of selection,
929     // and find the background color of the common ancestor.
930     if (propertyID == CSSPropertyBackgroundColor && (m_frame->selection()->isRange() || hasTransparentBackgroundColor(selectionStyle->style()))) {
931         RefPtr<Range> range(m_frame->selection()->toNormalizedRange());
932         ExceptionCode ec = 0;
933         if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(ec)))
934             return value->cssText();
935     }
936 
937     if (propertyID == CSSPropertyFontSize) {
938         RefPtr<CSSValue> cssValue = selectionStyle->style()->getPropertyCSSValue(CSSPropertyFontSize);
939         if (cssValue->isPrimitiveValue()) {
940             value = String::number(legacyFontSizeFromCSSValue(m_frame->document(), static_cast<CSSPrimitiveValue*>(cssValue.get()),
941                 selectionStyle->shouldUseFixedDefaultFontSize(), AlwaysUseLegacyFontSize));
942         }
943     }
944 
945     return value;
946 }
947 
indent()948 void Editor::indent()
949 {
950     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
951 }
952 
outdent()953 void Editor::outdent()
954 {
955     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
956 }
957 
dispatchEditableContentChangedEvents(const EditCommand & command)958 static void dispatchEditableContentChangedEvents(const EditCommand& command)
959 {
960     Element* startRoot = command.startingRootEditableElement();
961     Element* endRoot = command.endingRootEditableElement();
962     ExceptionCode ec;
963     if (startRoot)
964         startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
965     if (endRoot && endRoot != startRoot)
966         endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
967 }
968 
appliedEditing(PassRefPtr<EditCommand> cmd)969 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
970 {
971     m_frame->document()->updateLayout();
972 
973     dispatchEditableContentChangedEvents(*cmd);
974     VisibleSelection newSelection(cmd->endingSelection());
975 
976     m_spellingCorrector->respondToAppliedEditing(cmd.get());
977 
978     // Don't clear the typing style with this selection change.  We do those things elsewhere if necessary.
979     changeSelectionAfterCommand(newSelection, false, false);
980 
981     if (!cmd->preservesTypingStyle())
982         m_frame->selection()->clearTypingStyle();
983 
984     // Command will be equal to last edit command only in the case of typing
985     if (m_lastEditCommand.get() == cmd)
986         ASSERT(cmd->isTypingCommand());
987     else {
988         // Only register a new undo command if the command passed in is
989         // different from the last command
990         m_lastEditCommand = cmd;
991         if (client())
992             client()->registerCommandForUndo(m_lastEditCommand);
993     }
994     respondToChangedContents(newSelection);
995 }
996 
unappliedEditing(PassRefPtr<EditCommand> cmd)997 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
998 {
999     m_frame->document()->updateLayout();
1000 
1001     dispatchEditableContentChangedEvents(*cmd);
1002 
1003     VisibleSelection newSelection(cmd->startingSelection());
1004     changeSelectionAfterCommand(newSelection, true, true);
1005     m_spellingCorrector->respondToUnappliedEditing(cmd.get());
1006 
1007     m_lastEditCommand = 0;
1008     if (client())
1009         client()->registerCommandForRedo(cmd);
1010     respondToChangedContents(newSelection);
1011 }
1012 
reappliedEditing(PassRefPtr<EditCommand> cmd)1013 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
1014 {
1015     m_frame->document()->updateLayout();
1016 
1017     dispatchEditableContentChangedEvents(*cmd);
1018 
1019     VisibleSelection newSelection(cmd->endingSelection());
1020     changeSelectionAfterCommand(newSelection, true, true);
1021 
1022     m_lastEditCommand = 0;
1023     if (client())
1024         client()->registerCommandForUndo(cmd);
1025     respondToChangedContents(newSelection);
1026 }
1027 
Editor(Frame * frame)1028 Editor::Editor(Frame* frame)
1029     : m_frame(frame)
1030     , m_deleteButtonController(adoptPtr(new DeleteButtonController(frame)))
1031     , m_ignoreCompositionSelectionChange(false)
1032     , m_shouldStartNewKillRingSequence(false)
1033     // This is off by default, since most editors want this behavior (this matches IE but not FF).
1034     , m_shouldStyleWithCSS(false)
1035     , m_killRing(adoptPtr(new KillRing))
1036     , m_spellChecker(adoptPtr(new SpellChecker(frame)))
1037     , m_spellingCorrector(adoptPtr(new SpellingCorrectionController(frame)))
1038     , m_areMarkedTextMatchesHighlighted(false)
1039 {
1040 }
1041 
~Editor()1042 Editor::~Editor()
1043 {
1044 }
1045 
clear()1046 void Editor::clear()
1047 {
1048     m_compositionNode = 0;
1049     m_customCompositionUnderlines.clear();
1050     m_shouldStyleWithCSS = false;
1051 }
1052 
insertText(const String & text,Event * triggeringEvent)1053 bool Editor::insertText(const String& text, Event* triggeringEvent)
1054 {
1055     return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
1056 }
1057 
insertTextForConfirmedComposition(const String & text)1058 bool Editor::insertTextForConfirmedComposition(const String& text)
1059 {
1060     return m_frame->eventHandler()->handleTextInputEvent(text, 0, TextEventInputComposition);
1061 }
1062 
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,TextEvent * triggeringEvent)1063 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
1064 {
1065     if (text.isEmpty())
1066         return false;
1067 
1068     VisibleSelection selection = selectionForCommand(triggeringEvent);
1069     if (!selection.isContentEditable())
1070         return false;
1071     RefPtr<Range> range = selection.toNormalizedRange();
1072 
1073     if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
1074         return true;
1075 
1076     if (!text.isEmpty())
1077         updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
1078 
1079     bool shouldConsiderApplyingAutocorrection = false;
1080     if (text == " " || text == "\t")
1081         shouldConsiderApplyingAutocorrection = true;
1082 
1083     if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0]))
1084         shouldConsiderApplyingAutocorrection = true;
1085 
1086     bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1087 
1088     // Get the selection to use for the event that triggered this insertText.
1089     // If the event handler changed the selection, we may want to use a different selection
1090     // that is contained in the event target.
1091     selection = selectionForCommand(triggeringEvent);
1092     if (selection.isContentEditable()) {
1093         if (Node* selectionStart = selection.start().deprecatedNode()) {
1094             RefPtr<Document> document = selectionStart->document();
1095 
1096             // Insert the text
1097             TypingCommand::Options options = 0;
1098             if (selectInsertedText)
1099                 options |= TypingCommand::SelectInsertedText;
1100             if (autocorrectionWasApplied)
1101                 options |= TypingCommand::RetainAutocorrectionIndicator;
1102             TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
1103 
1104             // Reveal the current selection
1105             if (Frame* editedFrame = document->frame())
1106                 if (Page* page = editedFrame->page())
1107                     page->focusController()->focusedOrMainFrame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
1108         }
1109     }
1110 
1111     return true;
1112 }
1113 
insertLineBreak()1114 bool Editor::insertLineBreak()
1115 {
1116     if (!canEdit())
1117         return false;
1118 
1119     if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
1120         return true;
1121 
1122     bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1123     TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1124     revealSelectionAfterEditingOperation();
1125 
1126     return true;
1127 }
1128 
insertParagraphSeparator()1129 bool Editor::insertParagraphSeparator()
1130 {
1131     if (!canEdit())
1132         return false;
1133 
1134     if (!canEditRichly())
1135         return insertLineBreak();
1136 
1137     if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
1138         return true;
1139 
1140     bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
1141     TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
1142     revealSelectionAfterEditingOperation();
1143 
1144     return true;
1145 }
1146 
cut()1147 void Editor::cut()
1148 {
1149     if (tryDHTMLCut())
1150         return; // DHTML did the whole operation
1151     if (!canCut()) {
1152         systemBeep();
1153         return;
1154     }
1155     RefPtr<Range> selection = selectedRange();
1156     if (shouldDeleteRange(selection.get())) {
1157         updateMarkersForWordsAffectedByEditing(true);
1158         if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode()))
1159             Pasteboard::generalPasteboard()->writePlainText(selectedText());
1160         else
1161             Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
1162         didWriteSelectionToPasteboard();
1163         deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1164     }
1165 }
1166 
copy()1167 void Editor::copy()
1168 {
1169     if (tryDHTMLCopy())
1170         return; // DHTML did the whole operation
1171     if (!canCopy()) {
1172         systemBeep();
1173         return;
1174     }
1175 
1176     if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode()))
1177         Pasteboard::generalPasteboard()->writePlainText(selectedText());
1178     else {
1179         Document* document = m_frame->document();
1180         if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
1181             Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
1182         else
1183             Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
1184     }
1185 
1186     didWriteSelectionToPasteboard();
1187 }
1188 
paste()1189 void Editor::paste()
1190 {
1191     ASSERT(m_frame->document());
1192     if (tryDHTMLPaste())
1193         return; // DHTML did the whole operation
1194     if (!canPaste())
1195         return;
1196     updateMarkersForWordsAffectedByEditing(false);
1197     CachedResourceLoader* loader = m_frame->document()->cachedResourceLoader();
1198     loader->setAllowStaleResources(true);
1199     if (m_frame->selection()->isContentRichlyEditable())
1200         pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
1201     else
1202         pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1203     loader->setAllowStaleResources(false);
1204 }
1205 
pasteAsPlainText()1206 void Editor::pasteAsPlainText()
1207 {
1208     if (tryDHTMLPaste())
1209         return;
1210     if (!canPaste())
1211         return;
1212     updateMarkersForWordsAffectedByEditing(false);
1213     pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1214 }
1215 
performDelete()1216 void Editor::performDelete()
1217 {
1218     if (!canDelete()) {
1219         systemBeep();
1220         return;
1221     }
1222 
1223     addToKillRing(selectedRange().get(), false);
1224     deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1225 
1226     // clear the "start new kill ring sequence" setting, because it was set to true
1227     // when the selection was updated by deleting the range
1228     setStartNewKillRingSequence(false);
1229 }
1230 
copyURL(const KURL & url,const String & title)1231 void Editor::copyURL(const KURL& url, const String& title)
1232 {
1233     Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1234 }
1235 
copyImage(const HitTestResult & result)1236 void Editor::copyImage(const HitTestResult& result)
1237 {
1238     KURL url = result.absoluteLinkURL();
1239     if (url.isEmpty())
1240         url = result.absoluteImageURL();
1241 
1242     Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1243 }
1244 
isContinuousSpellCheckingEnabled()1245 bool Editor::isContinuousSpellCheckingEnabled()
1246 {
1247     return client() && client()->isContinuousSpellCheckingEnabled();
1248 }
1249 
toggleContinuousSpellChecking()1250 void Editor::toggleContinuousSpellChecking()
1251 {
1252     if (client())
1253         client()->toggleContinuousSpellChecking();
1254 }
1255 
isGrammarCheckingEnabled()1256 bool Editor::isGrammarCheckingEnabled()
1257 {
1258     return client() && client()->isGrammarCheckingEnabled();
1259 }
1260 
toggleGrammarChecking()1261 void Editor::toggleGrammarChecking()
1262 {
1263     if (client())
1264         client()->toggleGrammarChecking();
1265 }
1266 
spellCheckerDocumentTag()1267 int Editor::spellCheckerDocumentTag()
1268 {
1269     return client() ? client()->spellCheckerDocumentTag() : 0;
1270 }
1271 
1272 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
1273 
uppercaseWord()1274 void Editor::uppercaseWord()
1275 {
1276     if (client())
1277         client()->uppercaseWord();
1278 }
1279 
lowercaseWord()1280 void Editor::lowercaseWord()
1281 {
1282     if (client())
1283         client()->lowercaseWord();
1284 }
1285 
capitalizeWord()1286 void Editor::capitalizeWord()
1287 {
1288     if (client())
1289         client()->capitalizeWord();
1290 }
1291 
showSubstitutionsPanel()1292 void Editor::showSubstitutionsPanel()
1293 {
1294     if (!client()) {
1295         LOG_ERROR("No NSSpellChecker");
1296         return;
1297     }
1298 
1299     if (client()->substitutionsPanelIsShowing()) {
1300         client()->showSubstitutionsPanel(false);
1301         return;
1302     }
1303     client()->showSubstitutionsPanel(true);
1304 }
1305 
substitutionsPanelIsShowing()1306 bool Editor::substitutionsPanelIsShowing()
1307 {
1308     if (!client())
1309         return false;
1310     return client()->substitutionsPanelIsShowing();
1311 }
1312 
toggleSmartInsertDelete()1313 void Editor::toggleSmartInsertDelete()
1314 {
1315     if (client())
1316         client()->toggleSmartInsertDelete();
1317 }
1318 
isAutomaticQuoteSubstitutionEnabled()1319 bool Editor::isAutomaticQuoteSubstitutionEnabled()
1320 {
1321     return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1322 }
1323 
toggleAutomaticQuoteSubstitution()1324 void Editor::toggleAutomaticQuoteSubstitution()
1325 {
1326     if (client())
1327         client()->toggleAutomaticQuoteSubstitution();
1328 }
1329 
isAutomaticLinkDetectionEnabled()1330 bool Editor::isAutomaticLinkDetectionEnabled()
1331 {
1332     return client() && client()->isAutomaticLinkDetectionEnabled();
1333 }
1334 
toggleAutomaticLinkDetection()1335 void Editor::toggleAutomaticLinkDetection()
1336 {
1337     if (client())
1338         client()->toggleAutomaticLinkDetection();
1339 }
1340 
isAutomaticDashSubstitutionEnabled()1341 bool Editor::isAutomaticDashSubstitutionEnabled()
1342 {
1343     return client() && client()->isAutomaticDashSubstitutionEnabled();
1344 }
1345 
toggleAutomaticDashSubstitution()1346 void Editor::toggleAutomaticDashSubstitution()
1347 {
1348     if (client())
1349         client()->toggleAutomaticDashSubstitution();
1350 }
1351 
isAutomaticTextReplacementEnabled()1352 bool Editor::isAutomaticTextReplacementEnabled()
1353 {
1354     return client() && client()->isAutomaticTextReplacementEnabled();
1355 }
1356 
toggleAutomaticTextReplacement()1357 void Editor::toggleAutomaticTextReplacement()
1358 {
1359     if (client())
1360         client()->toggleAutomaticTextReplacement();
1361 }
1362 
isAutomaticSpellingCorrectionEnabled()1363 bool Editor::isAutomaticSpellingCorrectionEnabled()
1364 {
1365     return m_spellingCorrector->isAutomaticSpellingCorrectionEnabled();
1366 }
1367 
toggleAutomaticSpellingCorrection()1368 void Editor::toggleAutomaticSpellingCorrection()
1369 {
1370     if (client())
1371         client()->toggleAutomaticSpellingCorrection();
1372 }
1373 
1374 #endif
1375 
shouldEndEditing(Range * range)1376 bool Editor::shouldEndEditing(Range* range)
1377 {
1378     return client() && client()->shouldEndEditing(range);
1379 }
1380 
shouldBeginEditing(Range * range)1381 bool Editor::shouldBeginEditing(Range* range)
1382 {
1383     return client() && client()->shouldBeginEditing(range);
1384 }
1385 
clearUndoRedoOperations()1386 void Editor::clearUndoRedoOperations()
1387 {
1388     if (client())
1389         client()->clearUndoRedoOperations();
1390 }
1391 
canUndo()1392 bool Editor::canUndo()
1393 {
1394     return client() && client()->canUndo();
1395 }
1396 
undo()1397 void Editor::undo()
1398 {
1399     if (client())
1400         client()->undo();
1401 }
1402 
canRedo()1403 bool Editor::canRedo()
1404 {
1405     return client() && client()->canRedo();
1406 }
1407 
redo()1408 void Editor::redo()
1409 {
1410     if (client())
1411         client()->redo();
1412 }
1413 
didBeginEditing()1414 void Editor::didBeginEditing()
1415 {
1416     if (client())
1417         client()->didBeginEditing();
1418 }
1419 
didEndEditing()1420 void Editor::didEndEditing()
1421 {
1422     if (client())
1423         client()->didEndEditing();
1424 }
1425 
didWriteSelectionToPasteboard()1426 void Editor::didWriteSelectionToPasteboard()
1427 {
1428     if (client())
1429         client()->didWriteSelectionToPasteboard();
1430 }
1431 
toggleBold()1432 void Editor::toggleBold()
1433 {
1434     command("ToggleBold").execute();
1435 }
1436 
toggleUnderline()1437 void Editor::toggleUnderline()
1438 {
1439     command("ToggleUnderline").execute();
1440 }
1441 
setBaseWritingDirection(WritingDirection direction)1442 void Editor::setBaseWritingDirection(WritingDirection direction)
1443 {
1444     Node* focusedNode = frame()->document()->focusedNode();
1445     if (focusedNode && (focusedNode->hasTagName(textareaTag) || (focusedNode->hasTagName(inputTag) && static_cast<HTMLInputElement*>(focusedNode)->isTextField()))) {
1446         if (direction == NaturalWritingDirection)
1447             return;
1448         toHTMLElement(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1449         frame()->document()->updateStyleIfNeeded();
1450         return;
1451     }
1452 
1453     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
1454     style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1455     applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1456 }
1457 
selectComposition()1458 void Editor::selectComposition()
1459 {
1460     RefPtr<Range> range = compositionRange();
1461     if (!range)
1462         return;
1463 
1464     // The composition can start inside a composed character sequence, so we have to override checks.
1465     // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1466     VisibleSelection selection;
1467     selection.setWithoutValidation(range->startPosition(), range->endPosition());
1468     m_frame->selection()->setSelection(selection, 0);
1469 }
1470 
confirmComposition()1471 void Editor::confirmComposition()
1472 {
1473     if (!m_compositionNode)
1474         return;
1475     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1476 }
1477 
confirmCompositionWithoutDisturbingSelection()1478 void Editor::confirmCompositionWithoutDisturbingSelection()
1479 {
1480     if (!m_compositionNode)
1481         return;
1482     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1483 }
1484 
confirmComposition(const String & text)1485 void Editor::confirmComposition(const String& text)
1486 {
1487     confirmComposition(text, false);
1488 }
1489 
confirmComposition(const String & text,bool preserveSelection)1490 void Editor::confirmComposition(const String& text, bool preserveSelection)
1491 {
1492     UserTypingGestureIndicator typingGestureIndicator(m_frame);
1493 
1494     setIgnoreCompositionSelectionChange(true);
1495 
1496     VisibleSelection oldSelection = m_frame->selection()->selection();
1497 
1498     selectComposition();
1499 
1500     if (m_frame->selection()->isNone()) {
1501         setIgnoreCompositionSelectionChange(false);
1502         return;
1503     }
1504 
1505     // Dispatch a compositionend event to the focused node.
1506     // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
1507     // the DOM Event specification.
1508     Node* target = m_frame->document()->focusedNode();
1509     if (target) {
1510         RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1511         ExceptionCode ec = 0;
1512         target->dispatchEvent(event, ec);
1513     }
1514 
1515     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1516     // will delete the old composition with an optimized replace operation.
1517     if (text.isEmpty())
1518         TypingCommand::deleteSelection(m_frame->document(), 0);
1519 
1520     m_compositionNode = 0;
1521     m_customCompositionUnderlines.clear();
1522 
1523     insertTextForConfirmedComposition(text);
1524 
1525     if (preserveSelection) {
1526         m_frame->selection()->setSelection(oldSelection, 0);
1527         // An open typing command that disagrees about current selection would cause issues with typing later on.
1528         TypingCommand::closeTyping(m_lastEditCommand.get());
1529     }
1530 
1531     setIgnoreCompositionSelectionChange(false);
1532 }
1533 
setComposition(const String & text,const Vector<CompositionUnderline> & underlines,unsigned selectionStart,unsigned selectionEnd)1534 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1535 {
1536     UserTypingGestureIndicator typingGestureIndicator(m_frame);
1537 
1538     setIgnoreCompositionSelectionChange(true);
1539 
1540     // Updates styles before setting selection for composition to prevent
1541     // inserting the previous composition text into text nodes oddly.
1542     // See https://bugs.webkit.org/show_bug.cgi?id=46868
1543     m_frame->document()->updateStyleIfNeeded();
1544 
1545     selectComposition();
1546 
1547     if (m_frame->selection()->isNone()) {
1548         setIgnoreCompositionSelectionChange(false);
1549         return;
1550     }
1551 
1552     Node* target = m_frame->document()->focusedNode();
1553     if (target) {
1554         // Dispatch an appropriate composition event to the focused node.
1555         // We check the composition status and choose an appropriate composition event since this
1556         // function is used for three purposes:
1557         // 1. Starting a new composition.
1558         //    Send a compositionstart event when this function creates a new composition node, i.e.
1559         //    m_compositionNode == 0 && !text.isEmpty().
1560         // 2. Updating the existing composition node.
1561         //    Send a compositionupdate event when this function updates the existing composition
1562         //    node, i.e. m_compositionNode != 0 && !text.isEmpty().
1563         // 3. Canceling the ongoing composition.
1564         //    Send a compositionend event when function deletes the existing composition node, i.e.
1565         //    m_compositionNode != 0 && test.isEmpty().
1566         RefPtr<CompositionEvent> event;
1567         if (!m_compositionNode) {
1568             // We should send a compositionstart event only when the given text is not empty because this
1569             // function doesn't create a composition node when the text is empty.
1570             if (!text.isEmpty())
1571                 event = CompositionEvent::create(eventNames().compositionstartEvent, m_frame->domWindow(), text);
1572         } else {
1573             if (!text.isEmpty())
1574                 event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text);
1575             else
1576               event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text);
1577         }
1578         ExceptionCode ec = 0;
1579         if (event.get())
1580             target->dispatchEvent(event, ec);
1581     }
1582 
1583     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1584     // will delete the old composition with an optimized replace operation.
1585     if (text.isEmpty())
1586         TypingCommand::deleteSelection(m_frame->document(), TypingCommand::PreventSpellChecking);
1587 
1588     m_compositionNode = 0;
1589     m_customCompositionUnderlines.clear();
1590 
1591     if (!text.isEmpty()) {
1592         TypingCommand::insertText(m_frame->document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
1593 
1594         // Find out what node has the composition now.
1595         Position base = m_frame->selection()->base().downstream();
1596         Position extent = m_frame->selection()->extent();
1597         Node* baseNode = base.deprecatedNode();
1598         unsigned baseOffset = base.deprecatedEditingOffset();
1599         Node* extentNode = extent.deprecatedNode();
1600         unsigned extentOffset = extent.deprecatedEditingOffset();
1601 
1602         if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1603             m_compositionNode = static_cast<Text*>(baseNode);
1604             m_compositionStart = baseOffset;
1605             m_compositionEnd = extentOffset;
1606             m_customCompositionUnderlines = underlines;
1607             size_t numUnderlines = m_customCompositionUnderlines.size();
1608             for (size_t i = 0; i < numUnderlines; ++i) {
1609                 m_customCompositionUnderlines[i].startOffset += baseOffset;
1610                 m_customCompositionUnderlines[i].endOffset += baseOffset;
1611             }
1612             if (baseNode->renderer())
1613                 baseNode->renderer()->repaint();
1614 
1615             unsigned start = min(baseOffset + selectionStart, extentOffset);
1616             unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1617             RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1618             m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1619         }
1620     }
1621 
1622     setIgnoreCompositionSelectionChange(false);
1623 }
1624 
ignoreSpelling()1625 void Editor::ignoreSpelling()
1626 {
1627     if (!client())
1628         return;
1629 
1630     RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1631     if (selectedRange)
1632         frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1633 
1634     String text = selectedText();
1635     ASSERT(text.length());
1636     textChecker()->ignoreWordInSpellDocument(text);
1637 }
1638 
learnSpelling()1639 void Editor::learnSpelling()
1640 {
1641     if (!client())
1642         return;
1643 
1644     // 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>.
1645 
1646     RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1647     if (selectedRange)
1648         frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1649 
1650     String text = selectedText();
1651     ASSERT(text.length());
1652     textChecker()->learnWord(text);
1653 }
1654 
advanceToNextMisspelling(bool startBeforeSelection)1655 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1656 {
1657     ExceptionCode ec = 0;
1658 
1659     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1660     // then we wrap and search from the doc start to (approximately) where we started.
1661 
1662     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
1663     // repeated "check spelling" commands work.
1664     VisibleSelection selection(frame()->selection()->selection());
1665     RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1666 
1667     bool startedWithSelection = false;
1668     if (selection.start().deprecatedNode()) {
1669         startedWithSelection = true;
1670         if (startBeforeSelection) {
1671             VisiblePosition start(selection.visibleStart());
1672             // We match AppKit's rule: Start 1 character before the selection.
1673             VisiblePosition oneBeforeStart = start.previous();
1674             setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1675         } else
1676             setStart(spellingSearchRange.get(), selection.visibleEnd());
1677     }
1678 
1679     Position position = spellingSearchRange->startPosition();
1680     if (!isEditablePosition(position)) {
1681         // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1682         // selection is editable.
1683         // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1684         // when spell checking the whole document before sending the message.
1685         // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1686 
1687         position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1688         if (position.isNull())
1689             return;
1690 
1691         Position rangeCompliantPosition = position.parentAnchoredEquivalent();
1692         spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), ec);
1693         startedWithSelection = false; // won't need to wrap
1694     }
1695 
1696     // topNode defines the whole range we want to operate on
1697     Node* topNode = highestEditableRoot(position);
1698     // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
1699     spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec);
1700 
1701     // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1702     // at a word boundary. Going back by one char and then forward by a word does the trick.
1703     if (startedWithSelection) {
1704         VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1705         if (oneBeforeStart.isNotNull())
1706             setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1707         // else we were already at the start of the editable node
1708     }
1709 
1710     if (spellingSearchRange->collapsed(ec))
1711         return; // nothing to search in
1712 
1713     // Get the spell checker if it is available
1714     if (!client())
1715         return;
1716 
1717     // We go to the end of our first range instead of the start of it, just to be sure
1718     // we don't get foiled by any word boundary problems at the start.  It means we might
1719     // do a tiny bit more searching.
1720     Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1721     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1722 
1723     int misspellingOffset = 0;
1724     GrammarDetail grammarDetail;
1725     int grammarPhraseOffset = 0;
1726     RefPtr<Range> grammarSearchRange;
1727     String badGrammarPhrase;
1728     String misspelledWord;
1729 
1730 #if USE(UNIFIED_TEXT_CHECKING)
1731     grammarSearchRange = spellingSearchRange->cloneRange(ec);
1732     bool isSpelling = true;
1733     int foundOffset = 0;
1734     String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1735     if (isSpelling) {
1736         misspelledWord = foundItem;
1737         misspellingOffset = foundOffset;
1738     } else {
1739         badGrammarPhrase = foundItem;
1740         grammarPhraseOffset = foundOffset;
1741     }
1742 #else
1743     RefPtr<Range> firstMisspellingRange;
1744     misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
1745 
1746 #if USE(GRAMMAR_CHECKING)
1747     // Search for bad grammar that occurs prior to the next misspelled word (if any)
1748     grammarSearchRange = spellingSearchRange->cloneRange(ec);
1749     if (!misspelledWord.isEmpty()) {
1750         // Stop looking at start of next misspelled word
1751         CharacterIterator chars(grammarSearchRange.get());
1752         chars.advance(misspellingOffset);
1753         grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1754     }
1755 
1756     if (isGrammarCheckingEnabled())
1757         badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
1758 #endif
1759 #endif
1760 
1761     // 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
1762     // block rather than at a selection).
1763     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1764         spellingSearchRange->setStart(topNode, 0, ec);
1765         // going until the end of the very first chunk we tested is far enough
1766         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1767 
1768 #if USE(UNIFIED_TEXT_CHECKING)
1769         grammarSearchRange = spellingSearchRange->cloneRange(ec);
1770         foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1771         if (isSpelling) {
1772             misspelledWord = foundItem;
1773             misspellingOffset = foundOffset;
1774         } else {
1775             badGrammarPhrase = foundItem;
1776             grammarPhraseOffset = foundOffset;
1777         }
1778 #else
1779         misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
1780 
1781 #if USE(GRAMMAR_CHECKING)
1782         grammarSearchRange = spellingSearchRange->cloneRange(ec);
1783         if (!misspelledWord.isEmpty()) {
1784             // Stop looking at start of next misspelled word
1785             CharacterIterator chars(grammarSearchRange.get());
1786             chars.advance(misspellingOffset);
1787             grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1788         }
1789 
1790         if (isGrammarCheckingEnabled())
1791             badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
1792 #endif
1793 #endif
1794     }
1795 
1796     if (!badGrammarPhrase.isEmpty()) {
1797         ASSERT(WTF_USE_GRAMMAR_CHECKING);
1798         // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1799         // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1800         // panel, and store a marker so we draw the green squiggle later.
1801 
1802         ASSERT(badGrammarPhrase.length() > 0);
1803         ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1804 
1805         // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1806         RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1807         frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1808         frame()->selection()->revealSelection();
1809 
1810         client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1811         frame()->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1812     } else if (!misspelledWord.isEmpty()) {
1813         // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1814         // a marker so we draw the red squiggle later.
1815 
1816         RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1817         frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
1818         frame()->selection()->revealSelection();
1819 
1820         client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1821         frame()->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1822     }
1823 }
1824 
isSelectionMisspelled()1825 bool Editor::isSelectionMisspelled()
1826 {
1827     String selectedString = selectedText();
1828     int length = selectedString.length();
1829     if (!length)
1830         return false;
1831 
1832     if (!client())
1833         return false;
1834 
1835     int misspellingLocation = -1;
1836     int misspellingLength = 0;
1837     textChecker()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
1838 
1839     // The selection only counts as misspelled if the selected text is exactly one misspelled word
1840     if (misspellingLength != length)
1841         return false;
1842 
1843     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1844     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1845     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1846     // or a grammar error.
1847     client()->updateSpellingUIWithMisspelledWord(selectedString);
1848 
1849     return true;
1850 }
1851 
isSelectionUngrammatical()1852 bool Editor::isSelectionUngrammatical()
1853 {
1854 #if USE(GRAMMAR_CHECKING)
1855     Vector<String> ignoredGuesses;
1856     return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(ignoredGuesses);
1857 #else
1858     return false;
1859 #endif
1860 }
1861 
guessesForUngrammaticalSelection()1862 Vector<String> Editor::guessesForUngrammaticalSelection()
1863 {
1864 #if USE(GRAMMAR_CHECKING)
1865     Vector<String> guesses;
1866     // Ignore the result of isUngrammatical; we just want the guesses, whether or not there are any
1867     TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(guesses);
1868     return guesses;
1869 #else
1870     return Vector<String>();
1871 #endif
1872 }
1873 
guessesForMisspelledSelection()1874 Vector<String> Editor::guessesForMisspelledSelection()
1875 {
1876     String selectedString = selectedText();
1877     ASSERT(selectedString.length());
1878 
1879     Vector<String> guesses;
1880     if (client())
1881         textChecker()->getGuessesForWord(selectedString, String(), guesses);
1882     return guesses;
1883 }
1884 
guessesForMisspelledOrUngrammaticalSelection(bool & misspelled,bool & ungrammatical)1885 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
1886 {
1887 #if USE(UNIFIED_TEXT_CHECKING)
1888     return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical);
1889 #else
1890     misspelled = isSelectionMisspelled();
1891     if (misspelled) {
1892         ungrammatical = false;
1893         return guessesForMisspelledSelection();
1894     }
1895     if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) {
1896         ungrammatical = true;
1897         return guessesForUngrammaticalSelection();
1898     }
1899     ungrammatical = false;
1900     return Vector<String>();
1901 #endif
1902 }
1903 
showSpellingGuessPanel()1904 void Editor::showSpellingGuessPanel()
1905 {
1906     if (!client()) {
1907         LOG_ERROR("No NSSpellChecker");
1908         return;
1909     }
1910 
1911     if (client()->spellingUIIsShowing()) {
1912         client()->showSpellingUI(false);
1913         return;
1914     }
1915 
1916     advanceToNextMisspelling(true);
1917     client()->showSpellingUI(true);
1918 }
1919 
spellingPanelIsShowing()1920 bool Editor::spellingPanelIsShowing()
1921 {
1922     if (!client())
1923         return false;
1924     return client()->spellingUIIsShowing();
1925 }
1926 
clearMisspellingsAndBadGrammar(const VisibleSelection & movingSelection)1927 void Editor::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
1928 {
1929     RefPtr<Range> selectedRange = movingSelection.toNormalizedRange();
1930     if (selectedRange) {
1931         frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1932         frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Grammar);
1933     }
1934 }
1935 
markMisspellingsAndBadGrammar(const VisibleSelection & movingSelection)1936 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
1937 {
1938     bool markSpelling = isContinuousSpellCheckingEnabled();
1939     bool markGrammar = markSpelling && isGrammarCheckingEnabled();
1940 
1941     if (markSpelling) {
1942         RefPtr<Range> unusedFirstMisspellingRange;
1943         markMisspellings(movingSelection, unusedFirstMisspellingRange);
1944     }
1945 
1946     if (markGrammar)
1947         markBadGrammar(movingSelection);
1948 }
1949 
markMisspellingsAfterTypingToWord(const VisiblePosition & wordStart,const VisibleSelection & selectionAfterTyping,bool doReplacement)1950 void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement)
1951 {
1952 #if USE(UNIFIED_TEXT_CHECKING)
1953     m_spellingCorrector->applyPendingCorrection(selectionAfterTyping);
1954 
1955     TextCheckingOptions textCheckingOptions = 0;
1956     if (isContinuousSpellCheckingEnabled())
1957         textCheckingOptions |= MarkSpelling;
1958 
1959 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
1960     if (doReplacement
1961         && (isAutomaticQuoteSubstitutionEnabled()
1962             || isAutomaticLinkDetectionEnabled()
1963             || isAutomaticDashSubstitutionEnabled()
1964             || isAutomaticTextReplacementEnabled()
1965             || ((textCheckingOptions & MarkSpelling) && isAutomaticSpellingCorrectionEnabled())))
1966         textCheckingOptions |= PerformReplacement;
1967 #endif
1968     if (!textCheckingOptions & (MarkSpelling | PerformReplacement))
1969         return;
1970 
1971     if (isGrammarCheckingEnabled())
1972         textCheckingOptions |= MarkGrammar;
1973 
1974     VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
1975     if (textCheckingOptions & MarkGrammar) {
1976         VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
1977         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
1978     } else {
1979         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
1980     }
1981 
1982 #else
1983     UNUSED_PARAM(selectionAfterTyping);
1984     UNUSED_PARAM(doReplacement);
1985 
1986     if (!isContinuousSpellCheckingEnabled())
1987         return;
1988 
1989     // Check spelling of one word
1990     RefPtr<Range> misspellingRange;
1991     markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
1992 
1993     // Autocorrect the misspelled word.
1994     if (!misspellingRange)
1995         return;
1996 
1997     // Get the misspelled word.
1998     const String misspelledWord = plainText(misspellingRange.get());
1999     String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
2000 
2001     // If autocorrected word is non empty, replace the misspelled word by this word.
2002     if (!autocorrectedString.isEmpty()) {
2003         VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
2004         if (newSelection != frame()->selection()->selection()) {
2005             if (!frame()->selection()->shouldChangeSelection(newSelection))
2006                 return;
2007             frame()->selection()->setSelection(newSelection);
2008         }
2009 
2010         if (!frame()->editor()->shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
2011             return;
2012         frame()->editor()->replaceSelectionWithText(autocorrectedString, false, false);
2013 
2014         // Reset the charet one character further.
2015         frame()->selection()->moveTo(frame()->selection()->end());
2016         frame()->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2017     }
2018 
2019     if (!isGrammarCheckingEnabled())
2020         return;
2021 
2022     // Check grammar of entire sentence
2023     markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
2024 #endif
2025 }
2026 
markMisspellingsOrBadGrammar(const VisibleSelection & selection,bool checkSpelling,RefPtr<Range> & firstMisspellingRange)2027 void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
2028 {
2029     // This function is called with a selection already expanded to word boundaries.
2030     // Might be nice to assert that here.
2031 
2032     // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
2033     // grammar checking can only be on if spell checking is also on.
2034     if (!isContinuousSpellCheckingEnabled())
2035         return;
2036 
2037     RefPtr<Range> searchRange(selection.toNormalizedRange());
2038     if (!searchRange)
2039         return;
2040 
2041     // If we're not in an editable node, bail.
2042     Node* editableNode = searchRange->startContainer();
2043     if (!editableNode || !editableNode->rendererIsEditable())
2044         return;
2045 
2046     if (!isSpellCheckingEnabledFor(editableNode))
2047         return;
2048 
2049     // Get the spell checker if it is available
2050     if (!client())
2051         return;
2052 
2053     TextCheckingHelper checker(client(), searchRange);
2054     if (checkSpelling)
2055         checker.markAllMisspellings(firstMisspellingRange);
2056     else {
2057         ASSERT(WTF_USE_GRAMMAR_CHECKING);
2058         if (isGrammarCheckingEnabled())
2059             checker.markAllBadGrammar();
2060     }
2061 }
2062 
isSpellCheckingEnabledFor(Node * node) const2063 bool Editor::isSpellCheckingEnabledFor(Node* node) const
2064 {
2065     if (!node)
2066         return false;
2067     const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
2068     if (!focusedElement)
2069         return false;
2070     return focusedElement->isSpellCheckingEnabled();
2071 }
2072 
isSpellCheckingEnabledInFocusedNode() const2073 bool Editor::isSpellCheckingEnabledInFocusedNode() const
2074 {
2075     return isSpellCheckingEnabledFor(m_frame->selection()->start().deprecatedNode());
2076 }
2077 
markMisspellings(const VisibleSelection & selection,RefPtr<Range> & firstMisspellingRange)2078 void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2079 {
2080     markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
2081 }
2082 
markBadGrammar(const VisibleSelection & selection)2083 void Editor::markBadGrammar(const VisibleSelection& selection)
2084 {
2085     ASSERT(WTF_USE_GRAMMAR_CHECKING);
2086     RefPtr<Range> firstMisspellingRange;
2087     markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
2088 }
2089 
markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCheckingOptions,Range * spellingRange,Range * grammarRange)2090 void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCheckingOptions, Range* spellingRange, Range* grammarRange)
2091 {
2092 #if USE(UNIFIED_TEXT_CHECKING)
2093     // There shouldn't be pending autocorrection at this moment.
2094     ASSERT(!m_spellingCorrector->hasPendingCorrection());
2095 
2096     bool shouldMarkSpelling = textCheckingOptions & MarkSpelling;
2097     bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
2098     bool shouldPerformReplacement = textCheckingOptions & PerformReplacement;
2099     bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
2100     bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
2101 
2102     // This function is called with selections already expanded to word boundaries.
2103     ExceptionCode ec = 0;
2104     if (!client() || !spellingRange || (shouldMarkGrammar && !grammarRange))
2105         return;
2106 
2107     // If we're not in an editable node, bail.
2108     Node* editableNode = spellingRange->startContainer();
2109     if (!editableNode || !editableNode->rendererIsEditable())
2110         return;
2111 
2112     if (!isSpellCheckingEnabledFor(editableNode))
2113         return;
2114 
2115     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2116     int selectionOffset = 0;
2117     int ambiguousBoundaryOffset = -1;
2118     bool selectionChanged = false;
2119     bool restoreSelectionAfterChange = false;
2120     bool adjustSelectionForParagraphBoundaries = false;
2121 
2122     TextCheckingParagraph spellingParagraph(spellingRange);
2123     TextCheckingParagraph grammarParagraph(shouldMarkGrammar ? grammarRange : 0);
2124 
2125     if (shouldMarkGrammar ? (spellingParagraph.isRangeEmpty() && grammarParagraph.isEmpty()) : spellingParagraph.isEmpty())
2126         return;
2127 
2128     if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) {
2129         if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) {
2130             // Attempt to save the caret position so we can restore it later if needed
2131             Position caretPosition = m_frame->selection()->end();
2132             int offset = spellingParagraph.offsetTo(caretPosition, ec);
2133             if (!ec) {
2134                 selectionOffset = offset;
2135                 restoreSelectionAfterChange = true;
2136                 if (selectionOffset > 0 && (selectionOffset > spellingParagraph.textLength() || spellingParagraph.textCharAt(selectionOffset - 1) == newlineCharacter))
2137                     adjustSelectionForParagraphBoundaries = true;
2138                 if (selectionOffset > 0 && selectionOffset <= spellingParagraph.textLength() && isAmbiguousBoundaryCharacter(spellingParagraph.textCharAt(selectionOffset - 1)))
2139                     ambiguousBoundaryOffset = selectionOffset - 1;
2140             }
2141         }
2142     }
2143 
2144     Vector<TextCheckingResult> results;
2145     if (shouldMarkGrammar)
2146         textChecker()->checkTextOfParagraph(grammarParagraph.textCharacters(), grammarParagraph.textLength(),
2147                                             textCheckingTypeMaskFor(textCheckingOptions), results);
2148     else
2149         textChecker()->checkTextOfParagraph(spellingParagraph.textCharacters(), spellingParagraph.textLength(),
2150                                             textCheckingTypeMaskFor(textCheckingOptions), results);
2151 
2152 
2153     // If this checking is only for showing correction panel, we shouldn't bother to mark misspellings.
2154     if (shouldShowCorrectionPanel)
2155         shouldMarkSpelling = false;
2156 
2157     int offsetDueToReplacement = 0;
2158 
2159     for (unsigned i = 0; i < results.size(); i++) {
2160         int spellingRangeEndOffset = spellingParagraph.checkingEnd() + offsetDueToReplacement;
2161         const TextCheckingResult* result = &results[i];
2162         int resultLocation = result->location + offsetDueToReplacement;
2163         int resultLength = result->length;
2164         bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset;
2165 
2166         // Only mark misspelling if:
2167         // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
2168         // 2. Result falls within spellingRange.
2169         // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
2170         //    "wouldn'" as misspelled right after apostrophe is typed.
2171         if (shouldMarkSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingParagraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
2172             ASSERT(resultLength > 0 && resultLocation >= 0);
2173             RefPtr<Range> misspellingRange = spellingParagraph.subrange(resultLocation, resultLength);
2174             if (!m_spellingCorrector->isSpellingMarkerAllowed(misspellingRange))
2175                 continue;
2176             misspellingRange->startContainer(ec)->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2177         } else if (shouldMarkGrammar && result->type == TextCheckingTypeGrammar && grammarParagraph.checkingRangeCovers(resultLocation, resultLength)) {
2178             ASSERT(resultLength > 0 && resultLocation >= 0);
2179             for (unsigned j = 0; j < result->details.size(); j++) {
2180                 const GrammarDetail* detail = &result->details[j];
2181                 ASSERT(detail->length > 0 && detail->location >= 0);
2182                 if (grammarParagraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) {
2183                     RefPtr<Range> badGrammarRange = grammarParagraph.subrange(resultLocation + detail->location, detail->length);
2184                     grammarRange->startContainer(ec)->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
2185                 }
2186             }
2187         } else if (resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingParagraph.checkingStart()
2188                     && (result->type == TextCheckingTypeLink
2189                     || result->type == TextCheckingTypeQuote
2190                     || result->type == TextCheckingTypeDash
2191                     || result->type == TextCheckingTypeReplacement
2192                     || result->type == TextCheckingTypeCorrection)) {
2193             // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
2194             ASSERT(resultLength > 0 && resultLocation >= 0);
2195 
2196             if (shouldShowCorrectionPanel && (resultLocation + resultLength < spellingRangeEndOffset || result->type != TextCheckingTypeCorrection))
2197                 continue;
2198 
2199             int replacementLength = result->replacement.length();
2200 
2201             // Apply replacement if:
2202             // 1. The replacement length is non-zero.
2203             // 2. The result doesn't end at an ambiguous boundary.
2204             //    (FIXME: this is required until 6853027 is fixed and text checking can do this for us
2205             bool doReplacement = replacementLength > 0 && !resultEndsAtAmbiguousBoundary;
2206             RefPtr<Range> rangeToReplace = spellingParagraph.subrange(resultLocation, resultLength);
2207             VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM);
2208 
2209             // adding links should be done only immediately after they are typed
2210             if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1)
2211                 continue;
2212 
2213             String replacedString = plainText(rangeToReplace.get());
2214 
2215             // Don't correct spelling in an already-corrected word.
2216             DocumentMarkerController* markers = m_frame->document()->markers();
2217             if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::Replacement)) {
2218                 doReplacement = false;
2219                 if (result->type == TextCheckingTypeCorrection)
2220                     m_spellingCorrector->recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement);
2221             } else if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::RejectedCorrection))
2222                 doReplacement = false;
2223 
2224             if (!(shouldPerformReplacement || shouldShowCorrectionPanel) || !doReplacement)
2225                 continue;
2226 
2227             if (shouldShowCorrectionPanel) {
2228                 ASSERT(SUPPORT_AUTOCORRECTION_PANEL);
2229                 // shouldShowCorrectionPanel can be true only when the panel is available.
2230                 if (resultLocation + resultLength == spellingRangeEndOffset) {
2231                     // We only show the correction panel on the last word.
2232                     m_spellingCorrector->show(rangeToReplace, result->replacement);
2233                     break;
2234                 }
2235                 // If this function is called for showing correction panel, we ignore other correction or replacement.
2236                 continue;
2237             }
2238 
2239             if (selectionToReplace != m_frame->selection()->selection()) {
2240                 if (!m_frame->selection()->shouldChangeSelection(selectionToReplace))
2241                     continue;
2242             }
2243 
2244             if (result->type == TextCheckingTypeLink) {
2245                 m_frame->selection()->setSelection(selectionToReplace);
2246                 selectionChanged = true;
2247                 restoreSelectionAfterChange = false;
2248                 if (canEditRichly())
2249                     applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
2250             } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
2251                 applyCommand(SpellingCorrectionCommand::create(rangeToReplace, result->replacement));
2252 
2253                 if (AXObjectCache::accessibilityEnabled()) {
2254                     if (Element* root = m_frame->selection()->selection().rootEditableElement())
2255                         m_frame->document()->axObjectCache()->postNotification(root->renderer(), AXObjectCache::AXAutocorrectionOccured, true);
2256                 }
2257 
2258                 selectionChanged = true;
2259                 offsetDueToReplacement += replacementLength - resultLength;
2260                 if (resultLocation < selectionOffset) {
2261                     selectionOffset += replacementLength - resultLength;
2262                     if (ambiguousBoundaryOffset >= 0)
2263                         ambiguousBoundaryOffset = selectionOffset - 1;
2264                 }
2265 
2266                 // Add a marker so that corrections can easily be undone and won't be re-corrected.
2267                 if (result->type == TextCheckingTypeCorrection)
2268                     m_spellingCorrector->markCorrection(spellingParagraph.subrange(resultLocation, replacementLength), replacedString);
2269             }
2270         }
2271     }
2272 
2273     if (selectionChanged) {
2274         // Restore the caret position if we have made any replacements
2275         spellingParagraph.expandRangeToNextEnd();
2276         if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= spellingParagraph.rangeLength()) {
2277             RefPtr<Range> selectionRange = spellingParagraph.subrange(0, selectionOffset);
2278             m_frame->selection()->moveTo(selectionRange->endPosition(), DOWNSTREAM);
2279             if (adjustSelectionForParagraphBoundaries)
2280                 m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2281         } else {
2282             // If this fails for any reason, the fallback is to go one position beyond the last replacement
2283             m_frame->selection()->moveTo(m_frame->selection()->end());
2284             m_frame->selection()->modify(SelectionController::AlterationMove, DirectionForward, CharacterGranularity);
2285         }
2286     }
2287 #else
2288     ASSERT_NOT_REACHED();
2289     UNUSED_PARAM(textCheckingOptions);
2290     UNUSED_PARAM(spellingRange);
2291     UNUSED_PARAM(grammarRange);
2292 #endif // USE(UNIFIED_TEXT_CHECKING)
2293 }
2294 
changeBackToReplacedString(const String & replacedString)2295 void Editor::changeBackToReplacedString(const String& replacedString)
2296 {
2297 #if USE(UNIFIED_TEXT_CHECKING)
2298     if (replacedString.isEmpty())
2299         return;
2300 
2301     RefPtr<Range> selection = selectedRange();
2302     if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
2303         return;
2304 
2305     m_spellingCorrector->recordAutocorrectionResponseReversed(replacedString, selection);
2306     TextCheckingParagraph paragraph(selection);
2307     replaceSelectionWithText(replacedString, false, false);
2308     RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
2309     changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
2310     m_spellingCorrector->markReversed(changedRange.get());
2311 #else
2312     ASSERT_NOT_REACHED();
2313     UNUSED_PARAM(replacedString);
2314 #endif // USE(UNIFIED_TEXT_CHECKING)
2315 }
2316 
2317 
markMisspellingsAndBadGrammar(const VisibleSelection & spellingSelection,bool markGrammar,const VisibleSelection & grammarSelection)2318 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
2319 {
2320 #if USE(UNIFIED_TEXT_CHECKING)
2321     if (!isContinuousSpellCheckingEnabled())
2322         return;
2323     TextCheckingOptions textCheckingOptions = MarkSpelling | CheckForCorrection;
2324     if (markGrammar && isGrammarCheckingEnabled())
2325         textCheckingOptions |= MarkGrammar;
2326     markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
2327 #else
2328     RefPtr<Range> firstMisspellingRange;
2329     markMisspellings(spellingSelection, firstMisspellingRange);
2330     if (markGrammar)
2331         markBadGrammar(grammarSelection);
2332 #endif
2333 }
2334 
unappliedSpellCorrection(const VisibleSelection & selectionOfCorrected,const String & corrected,const String & correction)2335 void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
2336 {
2337     m_spellingCorrector->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
2338 }
2339 
updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)2340 void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
2341 {
2342     if (!m_spellingCorrector->shouldRemoveMarkersUponEditing())
2343         return;
2344 
2345     // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
2346     // several scenarios:
2347     // 1. Insert in the middle of a word.
2348     // 2. Appending non whitespace at the beginning of word.
2349     // 3. Appending non whitespace at the end of word.
2350     // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
2351     // remove the markers on that word.
2352     // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
2353     // selection, and remove words between the selection boundaries.
2354     //
2355     VisiblePosition startOfSelection = frame()->selection()->selection().start();
2356     VisiblePosition endOfSelection = frame()->selection()->selection().end();
2357     if (startOfSelection.isNull())
2358         return;
2359     // First word is the word that ends after or on the start of selection.
2360     VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
2361     VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
2362     // Last word is the word that begins before or on the end of selection
2363     VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
2364     VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
2365 
2366     if (startOfFirstWord.isNull()) {
2367         startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
2368         endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
2369     }
2370 
2371     if (endOfLastWord.isNull()) {
2372         startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
2373         endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
2374     }
2375 
2376     // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
2377     // we choose next word as the first word.
2378     if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) {
2379         startOfFirstWord = nextWordPosition(startOfFirstWord);
2380         endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
2381         if (startOfFirstWord == endOfSelection)
2382             return;
2383     }
2384 
2385     // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
2386     // we choose previous word as the last word.
2387     if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) {
2388         startOfLastWord = previousWordPosition(startOfLastWord);
2389         endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
2390         if (endOfLastWord == startOfSelection)
2391             return;
2392     }
2393 
2394     if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
2395         return;
2396 
2397     // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
2398     // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
2399     // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
2400     // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
2401     // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
2402     // of marker that contains the word in question, and remove marker on that whole range.
2403     Document* document = m_frame->document();
2404     RefPtr<Range> wordRange = Range::create(document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
2405 
2406     document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption, DocumentMarkerController::RemovePartiallyOverlappingMarker);
2407     document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement);
2408 }
2409 
rangeForPoint(const IntPoint & windowPoint)2410 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
2411 {
2412     Document* document = m_frame->documentAtPoint(windowPoint);
2413     if (!document)
2414         return 0;
2415 
2416     Frame* frame = document->frame();
2417     ASSERT(frame);
2418     FrameView* frameView = frame->view();
2419     if (!frameView)
2420         return 0;
2421     IntPoint framePoint = frameView->windowToContents(windowPoint);
2422     VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
2423     return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
2424 }
2425 
revealSelectionAfterEditingOperation()2426 void Editor::revealSelectionAfterEditingOperation()
2427 {
2428     if (m_ignoreCompositionSelectionChange)
2429         return;
2430 
2431     m_frame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2432 }
2433 
setIgnoreCompositionSelectionChange(bool ignore)2434 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
2435 {
2436     if (m_ignoreCompositionSelectionChange == ignore)
2437         return;
2438 
2439     m_ignoreCompositionSelectionChange = ignore;
2440     if (!ignore)
2441         revealSelectionAfterEditingOperation();
2442 }
2443 
compositionRange() const2444 PassRefPtr<Range> Editor::compositionRange() const
2445 {
2446     if (!m_compositionNode)
2447         return 0;
2448     unsigned length = m_compositionNode->length();
2449     unsigned start = min(m_compositionStart, length);
2450     unsigned end = min(max(start, m_compositionEnd), length);
2451     if (start >= end)
2452         return 0;
2453     return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
2454 }
2455 
getCompositionSelection(unsigned & selectionStart,unsigned & selectionEnd) const2456 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
2457 {
2458     if (!m_compositionNode)
2459         return false;
2460     Position start = m_frame->selection()->start();
2461     if (start.deprecatedNode() != m_compositionNode)
2462         return false;
2463     Position end = m_frame->selection()->end();
2464     if (end.deprecatedNode() != m_compositionNode)
2465         return false;
2466 
2467     if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart)
2468         return false;
2469     if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd)
2470         return false;
2471 
2472     selectionStart = start.deprecatedEditingOffset() - m_compositionStart;
2473     selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd;
2474     return true;
2475 }
2476 
transpose()2477 void Editor::transpose()
2478 {
2479     if (!canEdit())
2480         return;
2481 
2482      VisibleSelection selection = m_frame->selection()->selection();
2483      if (!selection.isCaret())
2484          return;
2485 
2486     // Make a selection that goes back one character and forward two characters.
2487     VisiblePosition caret = selection.visibleStart();
2488     VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
2489     VisiblePosition previous = next.previous();
2490     if (next == previous)
2491         return;
2492     previous = previous.previous();
2493     if (!inSameParagraph(next, previous))
2494         return;
2495     RefPtr<Range> range = makeRange(previous, next);
2496     if (!range)
2497         return;
2498     VisibleSelection newSelection(range.get(), DOWNSTREAM);
2499 
2500     // Transpose the two characters.
2501     String text = plainText(range.get());
2502     if (text.length() != 2)
2503         return;
2504     String transposed = text.right(1) + text.left(1);
2505 
2506     // Select the two characters.
2507     if (newSelection != m_frame->selection()->selection()) {
2508         if (!m_frame->selection()->shouldChangeSelection(newSelection))
2509             return;
2510         m_frame->selection()->setSelection(newSelection);
2511     }
2512 
2513     // Insert the transposed characters.
2514     if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2515         return;
2516     replaceSelectionWithText(transposed, false, false);
2517 }
2518 
addToKillRing(Range * range,bool prepend)2519 void Editor::addToKillRing(Range* range, bool prepend)
2520 {
2521     if (m_shouldStartNewKillRingSequence)
2522         killRing()->startNewSequence();
2523 
2524     String text = plainText(range);
2525     if (prepend)
2526         killRing()->prepend(text);
2527     else
2528         killRing()->append(text);
2529     m_shouldStartNewKillRingSequence = false;
2530 }
2531 
startCorrectionPanelTimer()2532 void Editor::startCorrectionPanelTimer()
2533 {
2534     m_spellingCorrector->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
2535 }
2536 
handleCorrectionPanelResult(const String & correction)2537 void Editor::handleCorrectionPanelResult(const String& correction)
2538 {
2539     m_spellingCorrector->handleCorrectionPanelResult(correction);
2540 }
2541 
2542 
dismissCorrectionPanelAsIgnored()2543 void Editor::dismissCorrectionPanelAsIgnored()
2544 {
2545     m_spellingCorrector->dismiss(ReasonForDismissingCorrectionPanelIgnored);
2546 }
2547 
insideVisibleArea(const IntPoint & point) const2548 bool Editor::insideVisibleArea(const IntPoint& point) const
2549 {
2550     if (m_frame->excludeFromTextSearch())
2551         return false;
2552 
2553     // Right now, we only check the visibility of a point for disconnected frames. For all other
2554     // frames, we assume visibility.
2555     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2556     if (!frame->isDisconnected())
2557         return true;
2558 
2559     RenderPart* renderer = frame->ownerRenderer();
2560     if (!renderer)
2561         return false;
2562 
2563     RenderBlock* container = renderer->containingBlock();
2564     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2565         return true;
2566 
2567     IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2568     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2569                                     rectInPageCoords.width(), rectInPageCoords.height());
2570 
2571     return rectInFrameCoords.contains(point);
2572 }
2573 
insideVisibleArea(Range * range) const2574 bool Editor::insideVisibleArea(Range* range) const
2575 {
2576     if (!range)
2577         return true;
2578 
2579     if (m_frame->excludeFromTextSearch())
2580         return false;
2581 
2582     // Right now, we only check the visibility of a range for disconnected frames. For all other
2583     // frames, we assume visibility.
2584     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2585     if (!frame->isDisconnected())
2586         return true;
2587 
2588     RenderPart* renderer = frame->ownerRenderer();
2589     if (!renderer)
2590         return false;
2591 
2592     RenderBlock* container = renderer->containingBlock();
2593     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2594         return true;
2595 
2596     IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2597     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2598                                     rectInPageCoords.width(), rectInPageCoords.height());
2599     IntRect resultRect = range->boundingBox();
2600 
2601     return rectInFrameCoords.contains(resultRect);
2602 }
2603 
firstVisibleRange(const String & target,FindOptions options)2604 PassRefPtr<Range> Editor::firstVisibleRange(const String& target, FindOptions options)
2605 {
2606     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2607     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
2608     ExceptionCode ec = 0;
2609 
2610     while (!insideVisibleArea(resultRange.get())) {
2611         searchRange->setStartAfter(resultRange->endContainer(), ec);
2612         if (searchRange->startContainer() == searchRange->endContainer())
2613             return Range::create(m_frame->document());
2614         resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
2615     }
2616 
2617     return resultRange;
2618 }
2619 
lastVisibleRange(const String & target,FindOptions options)2620 PassRefPtr<Range> Editor::lastVisibleRange(const String& target, FindOptions options)
2621 {
2622     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2623     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options | Backwards);
2624     ExceptionCode ec = 0;
2625 
2626     while (!insideVisibleArea(resultRange.get())) {
2627         searchRange->setEndBefore(resultRange->startContainer(), ec);
2628         if (searchRange->startContainer() == searchRange->endContainer())
2629             return Range::create(m_frame->document());
2630         resultRange = findPlainText(searchRange.get(), target, options | Backwards);
2631     }
2632 
2633     return resultRange;
2634 }
2635 
nextVisibleRange(Range * currentRange,const String & target,FindOptions options)2636 PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, FindOptions options)
2637 {
2638     if (m_frame->excludeFromTextSearch())
2639         return Range::create(m_frame->document());
2640 
2641     RefPtr<Range> resultRange = currentRange;
2642     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2643     ExceptionCode ec = 0;
2644     bool forward = !(options & Backwards);
2645     for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, options)) {
2646         if (resultRange->collapsed(ec)) {
2647             if (!resultRange->startContainer()->isInShadowTree())
2648                 break;
2649             searchRange = rangeOfContents(m_frame->document());
2650             if (forward)
2651                 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
2652             else
2653                 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec);
2654             continue;
2655         }
2656 
2657         if (forward)
2658             searchRange->setStartAfter(resultRange->endContainer(), ec);
2659         else
2660             searchRange->setEndBefore(resultRange->startContainer(), ec);
2661 
2662         Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
2663         if (searchRange->collapsed(ec) && shadowTreeRoot) {
2664             if (forward)
2665                 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
2666             else
2667                 searchRange->setStartBefore(shadowTreeRoot, ec);
2668         }
2669 
2670         if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode())
2671             break;
2672     }
2673 
2674     if (insideVisibleArea(resultRange.get()))
2675         return resultRange;
2676 
2677     if (!(options & WrapAround))
2678         return Range::create(m_frame->document());
2679 
2680     if (options & Backwards)
2681         return lastVisibleRange(target, options);
2682 
2683     return firstVisibleRange(target, options);
2684 }
2685 
changeSelectionAfterCommand(const VisibleSelection & newSelection,bool closeTyping,bool clearTypingStyle)2686 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle)
2687 {
2688     // If the new selection is orphaned, then don't update the selection.
2689     if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
2690         return;
2691 
2692     // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
2693     // because there is work that it must do in this situation.
2694     // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
2695     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
2696     bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection();
2697     if (selectionDidNotChangeDOMPosition || m_frame->selection()->shouldChangeSelection(newSelection)) {
2698         SelectionController::SetSelectionOptions options = 0;
2699         if (closeTyping)
2700             options |= SelectionController::CloseTyping;
2701         if (clearTypingStyle)
2702             options |= SelectionController::ClearTypingStyle;
2703         m_frame->selection()->setSelection(newSelection, options);
2704     }
2705 
2706     // Some editing operations change the selection visually without affecting its position within the DOM.
2707     // For example when you press return in the following (the caret is marked by ^):
2708     // <div contentEditable="true"><div>^Hello</div></div>
2709     // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
2710     // change the caret's DOM position (["hello", 0]).  In these situations the above SelectionController::setSelection call
2711     // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
2712     // starts a new kill ring sequence, but we want to do these things (matches AppKit).
2713     if (selectionDidNotChangeDOMPosition)
2714         client()->respondToChangedSelection();
2715 }
2716 
selectedText() const2717 String Editor::selectedText() const
2718 {
2719     // We remove '\0' characters because they are not visibly rendered to the user.
2720     return plainText(m_frame->selection()->toNormalizedRange().get()).replace(0, "");
2721 }
2722 
firstRectForRange(Range * range) const2723 IntRect Editor::firstRectForRange(Range* range) const
2724 {
2725     int extraWidthToEndOfLine = 0;
2726     ASSERT(range->startContainer());
2727     ASSERT(range->endContainer());
2728 
2729     InlineBox* startInlineBox;
2730     int startCaretOffset;
2731     Position startPosition = VisiblePosition(range->startPosition()).deepEquivalent();
2732     if (startPosition.isNull())
2733         return IntRect();
2734     startPosition.getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
2735 
2736     RenderObject* startRenderer = startPosition.deprecatedNode()->renderer();
2737     ASSERT(startRenderer);
2738     IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
2739     if (startCaretRect != IntRect())
2740         startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
2741 
2742     InlineBox* endInlineBox;
2743     int endCaretOffset;
2744     Position endPosition = VisiblePosition(range->endPosition()).deepEquivalent();
2745     if (endPosition.isNull())
2746         return IntRect();
2747     endPosition.getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
2748 
2749     RenderObject* endRenderer = endPosition.deprecatedNode()->renderer();
2750     ASSERT(endRenderer);
2751     IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset);
2752     if (endCaretRect != IntRect())
2753         endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
2754 
2755     if (startCaretRect.y() == endCaretRect.y()) {
2756         // start and end are on the same line
2757         return IntRect(min(startCaretRect.x(), endCaretRect.x()),
2758             startCaretRect.y(),
2759             abs(endCaretRect.x() - startCaretRect.x()),
2760             max(startCaretRect.height(), endCaretRect.height()));
2761     }
2762 
2763     // start and end aren't on the same line, so go from start to the end of its line
2764     return IntRect(startCaretRect.x(),
2765         startCaretRect.y(),
2766         startCaretRect.width() + extraWidthToEndOfLine,
2767         startCaretRect.height());
2768 }
2769 
shouldChangeSelection(const VisibleSelection & oldSelection,const VisibleSelection & newSelection,EAffinity affinity,bool stillSelecting) const2770 bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const
2771 {
2772     return client() && client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting);
2773 }
2774 
computeAndSetTypingStyle(CSSStyleDeclaration * style,EditAction editingAction)2775 void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction editingAction)
2776 {
2777     if (!style || !style->length()) {
2778         m_frame->selection()->clearTypingStyle();
2779         return;
2780     }
2781 
2782     // Calculate the current typing style.
2783     RefPtr<EditingStyle> typingStyle;
2784     if (m_frame->selection()->typingStyle()) {
2785         typingStyle = m_frame->selection()->typingStyle()->copy();
2786         typingStyle->overrideWithStyle(style->makeMutable().get());
2787     } else
2788         typingStyle = EditingStyle::create(style);
2789 
2790     typingStyle->prepareToApplyAt(m_frame->selection()->selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection);
2791 
2792     // Handle block styles, substracting these from the typing style.
2793     RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties();
2794     if (!blockStyle->isEmpty())
2795         applyCommand(ApplyStyleCommand::create(m_frame->document(), blockStyle.get(), editingAction));
2796 
2797     // Set the remaining style as the typing style.
2798     m_frame->selection()->setTypingStyle(typingStyle);
2799 }
2800 
selectionStartStyle() const2801 PassRefPtr<EditingStyle> Editor::selectionStartStyle() const
2802 {
2803     if (m_frame->selection()->isNone())
2804         return 0;
2805 
2806     RefPtr<Range> range(m_frame->selection()->toNormalizedRange());
2807     Position position = range->editingStartPosition();
2808 
2809     // If the pos is at the end of a text node, then this node is not fully selected.
2810     // Move it to the next deep equivalent position to avoid removing the style from this node.
2811     // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
2812     // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold.
2813     Node* positionNode = position.containerNode();
2814     if (m_frame->selection()->isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset())
2815         position = nextVisuallyDistinctCandidate(position);
2816 
2817     Element* element = position.element();
2818     if (!element)
2819         return 0;
2820 
2821     RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
2822     style->mergeTypingStyle(m_frame->document());
2823     return style;
2824 }
2825 
textFieldDidBeginEditing(Element * e)2826 void Editor::textFieldDidBeginEditing(Element* e)
2827 {
2828     if (client())
2829         client()->textFieldDidBeginEditing(e);
2830 }
2831 
textFieldDidEndEditing(Element * e)2832 void Editor::textFieldDidEndEditing(Element* e)
2833 {
2834     if (client())
2835         client()->textFieldDidEndEditing(e);
2836 }
2837 
textDidChangeInTextField(Element * e)2838 void Editor::textDidChangeInTextField(Element* e)
2839 {
2840     if (client())
2841         client()->textDidChangeInTextField(e);
2842 }
2843 
doTextFieldCommandFromEvent(Element * e,KeyboardEvent * ke)2844 bool Editor::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
2845 {
2846     if (client())
2847         return client()->doTextFieldCommandFromEvent(e, ke);
2848 
2849     return false;
2850 }
2851 
textWillBeDeletedInTextField(Element * input)2852 void Editor::textWillBeDeletedInTextField(Element* input)
2853 {
2854     if (client())
2855         client()->textWillBeDeletedInTextField(input);
2856 }
2857 
textDidChangeInTextArea(Element * e)2858 void Editor::textDidChangeInTextArea(Element* e)
2859 {
2860     if (client())
2861         client()->textDidChangeInTextArea(e);
2862 }
2863 
applyEditingStyleToBodyElement() const2864 void Editor::applyEditingStyleToBodyElement() const
2865 {
2866     RefPtr<NodeList> list = m_frame->document()->getElementsByTagName("body");
2867     unsigned len = list->length();
2868     for (unsigned i = 0; i < len; i++)
2869         applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
2870 }
2871 
applyEditingStyleToElement(Element * element) const2872 void Editor::applyEditingStyleToElement(Element* element) const
2873 {
2874     if (!element)
2875         return;
2876 
2877     CSSStyleDeclaration* style = element->style();
2878     ASSERT(style);
2879 
2880     ExceptionCode ec = 0;
2881     style->setProperty(CSSPropertyWordWrap, "break-word", false, ec);
2882     ASSERT(!ec);
2883     style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec);
2884     ASSERT(!ec);
2885     style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec);
2886     ASSERT(!ec);
2887 }
2888 
styleForSelectionStart(Node * & nodeToRemove) const2889 RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const
2890 {
2891     nodeToRemove = 0;
2892 
2893     if (m_frame->selection()->isNone())
2894         return 0;
2895 
2896     Position position = m_frame->selection()->selection().visibleStart().deepEquivalent();
2897     if (!position.isCandidate())
2898         return 0;
2899     if (!position.deprecatedNode())
2900         return 0;
2901 
2902     RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle();
2903     if (!typingStyle || !typingStyle->style())
2904         return position.deprecatedNode()->renderer()->style();
2905 
2906     RefPtr<Element> styleElement = m_frame->document()->createElement(spanTag, false);
2907 
2908     ExceptionCode ec = 0;
2909     String styleText = typingStyle->style()->cssText() + " display: inline";
2910     styleElement->setAttribute(styleAttr, styleText.impl(), ec);
2911     ASSERT(!ec);
2912 
2913     styleElement->appendChild(m_frame->document()->createEditingTextNode(""), ec);
2914     ASSERT(!ec);
2915 
2916     position.deprecatedNode()->parentNode()->appendChild(styleElement, ec);
2917     ASSERT(!ec);
2918 
2919     nodeToRemove = styleElement.get();
2920     return styleElement->renderer() ? styleElement->renderer()->style() : 0;
2921 }
2922 
2923 // Searches from the beginning of the document if nothing is selected.
findString(const String & target,bool forward,bool caseFlag,bool wrapFlag,bool startInSelection)2924 bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
2925 {
2926     FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0);
2927     return findString(target, options);
2928 }
2929 
findString(const String & target,FindOptions options)2930 bool Editor::findString(const String& target, FindOptions options)
2931 {
2932     if (target.isEmpty())
2933         return false;
2934 
2935     if (m_frame->excludeFromTextSearch())
2936         return false;
2937 
2938     // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
2939     // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
2940     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2941     VisibleSelection selection = m_frame->selection()->selection();
2942 
2943     bool forward = !(options & Backwards);
2944     bool startInSelection = options & StartInSelection;
2945     if (forward)
2946         setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
2947     else
2948         setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
2949 
2950     RefPtr<Node> shadowTreeRoot = selection.shadowTreeRootNode();
2951     if (shadowTreeRoot) {
2952         ExceptionCode ec = 0;
2953         if (forward)
2954             searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
2955         else
2956             searchRange->setStart(shadowTreeRoot.get(), 0, ec);
2957     }
2958 
2959     RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options));
2960     // If we started in the selection and the found range exactly matches the existing selection, find again.
2961     // Build a selection with the found range to remove collapsed whitespace.
2962     // Compare ranges instead of selection objects to ignore the way that the current selection was made.
2963     if (startInSelection && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), selection.toNormalizedRange().get())) {
2964         searchRange = rangeOfContents(m_frame->document());
2965         if (forward)
2966             setStart(searchRange.get(), selection.visibleEnd());
2967         else
2968             setEnd(searchRange.get(), selection.visibleStart());
2969 
2970         if (shadowTreeRoot) {
2971             ExceptionCode ec = 0;
2972             if (forward)
2973                 searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
2974             else
2975                 searchRange->setStart(shadowTreeRoot.get(), 0, ec);
2976         }
2977 
2978         resultRange = findPlainText(searchRange.get(), target, options);
2979     }
2980 
2981     ExceptionCode exception = 0;
2982 
2983     // If nothing was found in the shadow tree, search in main content following the shadow tree.
2984     if (resultRange->collapsed(exception) && shadowTreeRoot) {
2985         searchRange = rangeOfContents(m_frame->document());
2986         if (forward)
2987             searchRange->setStartAfter(shadowTreeRoot->shadowHost(), exception);
2988         else
2989             searchRange->setEndBefore(shadowTreeRoot->shadowHost(), exception);
2990 
2991         resultRange = findPlainText(searchRange.get(), target, options);
2992     }
2993 
2994     if (!insideVisibleArea(resultRange.get())) {
2995         resultRange = nextVisibleRange(resultRange.get(), target, options);
2996         if (!resultRange)
2997             return false;
2998     }
2999 
3000     // If we didn't find anything and we're wrapping, search again in the entire document (this will
3001     // redundantly re-search the area already searched in some cases).
3002     if (resultRange->collapsed(exception) && options & WrapAround) {
3003         searchRange = rangeOfContents(m_frame->document());
3004         resultRange = findPlainText(searchRange.get(), target, options);
3005         // We used to return false here if we ended up with the same range that we started with
3006         // (e.g., the selection was already the only instance of this text). But we decided that
3007         // this should be a success case instead, so we'll just fall through in that case.
3008     }
3009 
3010     if (resultRange->collapsed(exception))
3011         return false;
3012 
3013     m_frame->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
3014     m_frame->selection()->revealSelection();
3015     return true;
3016 }
3017 
isFrameInRange(Frame * frame,Range * range)3018 static bool isFrameInRange(Frame* frame, Range* range)
3019 {
3020     bool inRange = false;
3021     for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document()->ownerElement()) {
3022         if (ownerElement->document() == range->ownerDocument()) {
3023             ExceptionCode ec = 0;
3024             inRange = range->intersectsNode(ownerElement, ec);
3025             break;
3026         }
3027     }
3028     return inRange;
3029 }
3030 
countMatchesForText(const String & target,FindOptions options,unsigned limit,bool markMatches)3031 unsigned Editor::countMatchesForText(const String& target, FindOptions options, unsigned limit, bool markMatches)
3032 {
3033     return countMatchesForText(target, 0, options, limit, markMatches);
3034 }
3035 
countMatchesForText(const String & target,Range * range,FindOptions options,unsigned limit,bool markMatches)3036 unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches)
3037 {
3038     if (target.isEmpty())
3039         return 0;
3040 
3041     RefPtr<Range> searchRange;
3042     if (range) {
3043         if (range->ownerDocument() == m_frame->document())
3044             searchRange = range;
3045         else if (!isFrameInRange(m_frame, range))
3046             return 0;
3047     }
3048     if (!searchRange)
3049         searchRange = rangeOfContents(m_frame->document());
3050 
3051     Node* originalEndContainer = searchRange->endContainer();
3052     int originalEndOffset = searchRange->endOffset();
3053 
3054     ExceptionCode exception = 0;
3055     unsigned matchCount = 0;
3056     do {
3057         RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options & ~Backwards));
3058         if (resultRange->collapsed(exception)) {
3059             if (!resultRange->startContainer()->isInShadowTree())
3060                 break;
3061 
3062             searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception);
3063             searchRange->setEnd(originalEndContainer, originalEndOffset, exception);
3064             continue;
3065         }
3066 
3067         // Only treat the result as a match if it is visible
3068         if (insideVisibleArea(resultRange.get())) {
3069             ++matchCount;
3070             if (markMatches)
3071                 m_frame->document()->markers()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
3072         }
3073 
3074         // Stop looking if we hit the specified limit. A limit of 0 means no limit.
3075         if (limit > 0 && matchCount >= limit)
3076             break;
3077 
3078         // Set the new start for the search range to be the end of the previous
3079         // result range. There is no need to use a VisiblePosition here,
3080         // since findPlainText will use a TextIterator to go over the visible
3081         // text nodes.
3082         searchRange->setStart(resultRange->endContainer(exception), resultRange->endOffset(exception), exception);
3083 
3084         Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
3085         if (searchRange->collapsed(exception) && shadowTreeRoot)
3086             searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception);
3087     } while (true);
3088 
3089     if (markMatches) {
3090         // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match.
3091         if (m_frame->view() && m_frame->contentRenderer()) {
3092             m_frame->document()->updateLayout(); // Ensure layout is up to date.
3093             IntRect visibleRect = m_frame->view()->visibleContentRect();
3094             if (!visibleRect.isEmpty()) {
3095                 GraphicsContext context((PlatformGraphicsContext*)0);
3096                 context.setPaintingDisabled(true);
3097 
3098                 PaintBehavior oldBehavior = m_frame->view()->paintBehavior();
3099                 m_frame->view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
3100                 m_frame->view()->paintContents(&context, visibleRect);
3101                 m_frame->view()->setPaintBehavior(oldBehavior);
3102             }
3103         }
3104     }
3105 
3106     return matchCount;
3107 }
3108 
setMarkedTextMatchesAreHighlighted(bool flag)3109 void Editor::setMarkedTextMatchesAreHighlighted(bool flag)
3110 {
3111     if (flag == m_areMarkedTextMatchesHighlighted)
3112         return;
3113 
3114     m_areMarkedTextMatchesHighlighted = flag;
3115     m_frame->document()->markers()->repaintMarkers(DocumentMarker::TextMatch);
3116 }
3117 
respondToChangedSelection(const VisibleSelection & oldSelection,SelectionController::SetSelectionOptions options)3118 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions options)
3119 {
3120     m_spellingCorrector->stopPendingCorrection(oldSelection);
3121 
3122     bool closeTyping = options & SelectionController::CloseTyping;
3123     bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
3124     bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
3125     if (isContinuousSpellCheckingEnabled) {
3126         VisibleSelection newAdjacentWords;
3127         VisibleSelection newSelectedSentence;
3128         bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
3129         if (m_frame->selection()->selection().isContentEditable() || caretBrowsing) {
3130             VisiblePosition newStart(m_frame->selection()->selection().visibleStart());
3131             newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3132             if (isContinuousGrammarCheckingEnabled)
3133                 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
3134         }
3135 
3136         // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
3137         bool shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered);
3138 
3139         // When typing we check spelling elsewhere, so don't redo it here.
3140         // If this is a change in selection resulting from a delete operation,
3141         // oldSelection may no longer be in the document.
3142         if (shouldCheckSpellingAndGrammar && closeTyping && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) {
3143             VisiblePosition oldStart(oldSelection.visibleStart());
3144             VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
3145             if (oldAdjacentWords != newAdjacentWords) {
3146                 if (isContinuousGrammarCheckingEnabled) {
3147                     VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
3148                     markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence);
3149                 } else
3150                     markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
3151             }
3152         }
3153 
3154 #if !PLATFORM(MAC) || (PLATFORM(MAC) && (defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)))
3155         // This only erases markers that are in the first unit (word or sentence) of the selection.
3156         // Perhaps peculiar, but it matches AppKit on these Mac OS X versions.
3157         if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
3158             m_frame->document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
3159 #endif
3160         if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
3161             m_frame->document()->markers()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
3162     }
3163 
3164     // When continuous spell checking is off, existing markers disappear after the selection changes.
3165     if (!isContinuousSpellCheckingEnabled)
3166         m_frame->document()->markers()->removeMarkers(DocumentMarker::Spelling);
3167     if (!isContinuousGrammarCheckingEnabled)
3168         m_frame->document()->markers()->removeMarkers(DocumentMarker::Grammar);
3169 
3170     respondToChangedSelection(oldSelection);
3171 }
3172 
findFirstMarkable(Node * node)3173 static Node* findFirstMarkable(Node* node)
3174 {
3175     while (node) {
3176         if (!node->renderer())
3177             return 0;
3178         if (node->renderer()->isText())
3179             return node;
3180         if (node->renderer()->isTextControl())
3181             node = toRenderTextControl(node->renderer())->visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
3182         else if (node->firstChild())
3183             node = node->firstChild();
3184         else
3185             node = node->nextSibling();
3186     }
3187 
3188     return 0;
3189 }
3190 
selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType,int from,int length) const3191 bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
3192 {
3193     Node* node = findFirstMarkable(m_frame->selection()->start().deprecatedNode());
3194     if (!node)
3195         return false;
3196 
3197     unsigned int startOffset = static_cast<unsigned int>(from);
3198     unsigned int endOffset = static_cast<unsigned int>(from + length);
3199     Vector<DocumentMarker> markers = m_frame->document()->markers()->markersForNode(node);
3200     for (size_t i = 0; i < markers.size(); ++i) {
3201         DocumentMarker marker = markers[i];
3202         if (marker.startOffset <= startOffset && endOffset <= marker.endOffset && marker.type == markerType)
3203             return true;
3204     }
3205 
3206     return false;
3207 }
3208 
textCheckingTypeMaskFor(TextCheckingOptions textCheckingOptions)3209 TextCheckingTypeMask Editor::textCheckingTypeMaskFor(TextCheckingOptions textCheckingOptions)
3210 {
3211     bool shouldMarkSpelling = textCheckingOptions & MarkSpelling;
3212     bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
3213     bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
3214     bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
3215 
3216     TextCheckingTypeMask checkingTypes = 0;
3217     if (shouldMarkSpelling)
3218         checkingTypes |= TextCheckingTypeSpelling;
3219     if (shouldMarkGrammar)
3220         checkingTypes |= TextCheckingTypeGrammar;
3221     if (shouldCheckForCorrection)
3222         checkingTypes |= TextCheckingTypeCorrection;
3223 
3224 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
3225     bool shouldPerformReplacement = textCheckingOptions & PerformReplacement;
3226     if (shouldPerformReplacement) {
3227         if (isAutomaticLinkDetectionEnabled())
3228             checkingTypes |= TextCheckingTypeLink;
3229         if (isAutomaticQuoteSubstitutionEnabled())
3230             checkingTypes |= TextCheckingTypeQuote;
3231         if (isAutomaticDashSubstitutionEnabled())
3232             checkingTypes |= TextCheckingTypeDash;
3233         if (isAutomaticTextReplacementEnabled())
3234             checkingTypes |= TextCheckingTypeReplacement;
3235         if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled())
3236             checkingTypes |= TextCheckingTypeCorrection;
3237     }
3238 #endif
3239 
3240     return checkingTypes;
3241 }
3242 
3243 } // namespace WebCore
3244