• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CharacterNames.h"
33 #include "CreateLinkCommand.h"
34 #include "CSSComputedStyleDeclaration.h"
35 #include "CSSMutableStyleDeclaration.h"
36 #include "CSSProperty.h"
37 #include "CSSPropertyNames.h"
38 #include "CSSValueKeywords.h"
39 #include "ClipboardEvent.h"
40 #include "DeleteButtonController.h"
41 #include "DeleteSelectionCommand.h"
42 #include "DocLoader.h"
43 #include "DocumentFragment.h"
44 #include "EditorClient.h"
45 #include "EventHandler.h"
46 #include "EventNames.h"
47 #include "FocusController.h"
48 #include "Frame.h"
49 #include "FrameTree.h"
50 #include "FrameView.h"
51 #include "HTMLInputElement.h"
52 #include "HTMLTextAreaElement.h"
53 #include "HitTestResult.h"
54 #include "IndentOutdentCommand.h"
55 #include "InsertListCommand.h"
56 #include "KeyboardEvent.h"
57 #include "ModifySelectionListLevel.h"
58 #include "Page.h"
59 #include "Pasteboard.h"
60 #include "RemoveFormatCommand.h"
61 #include "RenderBlock.h"
62 #include "RenderPart.h"
63 #include "ReplaceSelectionCommand.h"
64 #include "Sound.h"
65 #include "Text.h"
66 #include "TextIterator.h"
67 #include "TypingCommand.h"
68 #include "htmlediting.h"
69 #include "markup.h"
70 #include "visible_units.h"
71 #include <wtf/UnusedParam.h>
72 
73 namespace WebCore {
74 
75 using namespace std;
76 using namespace HTMLNames;
77 
78 // When an event handler has moved the selection outside of a text control
79 // we should use the target control's selection for this editing operation.
selectionForCommand(Event * event)80 VisibleSelection Editor::selectionForCommand(Event* event)
81 {
82     VisibleSelection selection = m_frame->selection()->selection();
83     if (!event)
84         return selection;
85     // If the target is a text control, and the current selection is outside of its shadow tree,
86     // then use the saved selection for that text control.
87     Node* target = event->target()->toNode();
88     Node* selectionStart = selection.start().node();
89     if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) {
90         if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField())
91             return static_cast<HTMLInputElement*>(target)->selection();
92         if (target->hasTagName(textareaTag))
93             return static_cast<HTMLTextAreaElement*>(target)->selection();
94     }
95     return selection;
96 }
97 
client() const98 EditorClient* Editor::client() const
99 {
100     if (Page* page = m_frame->page())
101         return page->editorClient();
102     return 0;
103 }
104 
handleKeyboardEvent(KeyboardEvent * event)105 void Editor::handleKeyboardEvent(KeyboardEvent* event)
106 {
107     if (EditorClient* c = client())
108         c->handleKeyboardEvent(event);
109 }
110 
handleInputMethodKeydown(KeyboardEvent * event)111 void Editor::handleInputMethodKeydown(KeyboardEvent* event)
112 {
113     if (EditorClient* c = client())
114         c->handleInputMethodKeydown(event);
115 }
116 
canEdit() const117 bool Editor::canEdit() const
118 {
119     return m_frame->selection()->isContentEditable();
120 }
121 
canEditRichly() const122 bool Editor::canEditRichly() const
123 {
124     return m_frame->selection()->isContentRichlyEditable();
125 }
126 
127 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
128 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
129 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
130 // normally selectable to implement copy/paste (like divs, or a document body).
131 
canDHTMLCut()132 bool Editor::canDHTMLCut()
133 {
134     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
135 }
136 
canDHTMLCopy()137 bool Editor::canDHTMLCopy()
138 {
139     return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
140 }
141 
canDHTMLPaste()142 bool Editor::canDHTMLPaste()
143 {
144     return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
145 }
146 
canCut() const147 bool Editor::canCut() const
148 {
149     return canCopy() && canDelete();
150 }
151 
imageElementFromImageDocument(Document * document)152 static HTMLImageElement* imageElementFromImageDocument(Document* document)
153 {
154     if (!document)
155         return 0;
156     if (!document->isImageDocument())
157         return 0;
158 
159     HTMLElement* body = document->body();
160     if (!body)
161         return 0;
162 
163     Node* node = body->firstChild();
164     if (!node)
165         return 0;
166     if (!node->hasTagName(imgTag))
167         return 0;
168     return static_cast<HTMLImageElement*>(node);
169 }
170 
canCopy() const171 bool Editor::canCopy() const
172 {
173     if (imageElementFromImageDocument(m_frame->document()))
174         return true;
175     SelectionController* selection = m_frame->selection();
176     return selection->isRange() && !selection->isInPasswordField();
177 }
178 
canPaste() const179 bool Editor::canPaste() const
180 {
181     return canEdit();
182 }
183 
canDelete() const184 bool Editor::canDelete() const
185 {
186     SelectionController* selection = m_frame->selection();
187     return selection->isRange() && selection->isContentEditable();
188 }
189 
canDeleteRange(Range * range) const190 bool Editor::canDeleteRange(Range* range) const
191 {
192     ExceptionCode ec = 0;
193     Node* startContainer = range->startContainer(ec);
194     Node* endContainer = range->endContainer(ec);
195     if (!startContainer || !endContainer)
196         return false;
197 
198     if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
199         return false;
200 
201     if (range->collapsed(ec)) {
202         VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
203         VisiblePosition previous = start.previous();
204         // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
205         if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
206             return false;
207     }
208     return true;
209 }
210 
smartInsertDeleteEnabled()211 bool Editor::smartInsertDeleteEnabled()
212 {
213     return client() && client()->smartInsertDeleteEnabled();
214 }
215 
canSmartCopyOrDelete()216 bool Editor::canSmartCopyOrDelete()
217 {
218     return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
219 }
220 
isSelectTrailingWhitespaceEnabled()221 bool Editor::isSelectTrailingWhitespaceEnabled()
222 {
223     return client() && client()->isSelectTrailingWhitespaceEnabled();
224 }
225 
deleteWithDirection(SelectionController::EDirection direction,TextGranularity granularity,bool killRing,bool isTypingAction)226 bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
227 {
228     if (!canEdit())
229         return false;
230 
231     if (m_frame->selection()->isRange()) {
232         if (isTypingAction) {
233             TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity);
234             revealSelectionAfterEditingOperation();
235         } else {
236             if (killRing)
237                 addToKillRing(selectedRange().get(), false);
238             deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
239             // Implicitly calls revealSelectionAfterEditingOperation().
240         }
241     } else {
242         switch (direction) {
243             case SelectionController::FORWARD:
244             case SelectionController::RIGHT:
245                 TypingCommand::forwardDeleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
246                 break;
247             case SelectionController::BACKWARD:
248             case SelectionController::LEFT:
249                 TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity, killRing);
250                 break;
251         }
252         revealSelectionAfterEditingOperation();
253     }
254 
255     // FIXME: We should to move this down into deleteKeyPressed.
256     // clear the "start new kill ring sequence" setting, because it was set to true
257     // when the selection was updated by deleting the range
258     if (killRing)
259         setStartNewKillRingSequence(false);
260 
261     return true;
262 }
263 
deleteSelectionWithSmartDelete(bool smartDelete)264 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
265 {
266     if (m_frame->selection()->isNone())
267         return;
268 
269     applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
270 }
271 
pasteAsPlainTextWithPasteboard(Pasteboard * pasteboard)272 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
273 {
274     String text = pasteboard->plainText(m_frame);
275     if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
276         replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
277 }
278 
pasteWithPasteboard(Pasteboard * pasteboard,bool allowPlainText)279 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
280 {
281     RefPtr<Range> range = selectedRange();
282     bool chosePlainText;
283     RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
284     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
285         replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
286 }
287 
canSmartReplaceWithPasteboard(Pasteboard * pasteboard)288 bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
289 {
290     return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
291 }
292 
shouldInsertFragment(PassRefPtr<DocumentFragment> fragment,PassRefPtr<Range> replacingDOMRange,EditorInsertAction givenAction)293 bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction)
294 {
295     if (!client())
296         return false;
297 
298     Node* child = fragment->firstChild();
299     if (child && fragment->lastChild() == child && child->isCharacterDataNode())
300         return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction);
301 
302     return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
303 }
304 
replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,bool selectReplacement,bool smartReplace,bool matchStyle)305 void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
306 {
307     if (m_frame->selection()->isNone() || !fragment)
308         return;
309 
310     applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle));
311     revealSelectionAfterEditingOperation();
312 }
313 
replaceSelectionWithText(const String & text,bool selectReplacement,bool smartReplace)314 void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
315 {
316     replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
317 }
318 
selectedRange()319 PassRefPtr<Range> Editor::selectedRange()
320 {
321     if (!m_frame)
322         return 0;
323     return m_frame->selection()->toNormalizedRange();
324 }
325 
shouldDeleteRange(Range * range) const326 bool Editor::shouldDeleteRange(Range* range) const
327 {
328     ExceptionCode ec;
329     if (!range || range->collapsed(ec))
330         return false;
331 
332     if (!canDeleteRange(range))
333         return false;
334 
335     return client() && client()->shouldDeleteRange(range);
336 }
337 
tryDHTMLCopy()338 bool Editor::tryDHTMLCopy()
339 {
340     if (m_frame->selection()->isInPasswordField())
341         return false;
342 
343     if (canCopy())
344         // Must be done before oncopy adds types and data to the pboard,
345         // also done for security, as it erases data from the last copy/paste.
346         Pasteboard::generalPasteboard()->clear();
347 
348     return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
349 }
350 
tryDHTMLCut()351 bool Editor::tryDHTMLCut()
352 {
353     if (m_frame->selection()->isInPasswordField())
354         return false;
355 
356     if (canCut())
357         // Must be done before oncut adds types and data to the pboard,
358         // also done for security, as it erases data from the last copy/paste.
359         Pasteboard::generalPasteboard()->clear();
360 
361     return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
362 }
363 
tryDHTMLPaste()364 bool Editor::tryDHTMLPaste()
365 {
366     return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
367 }
368 
writeSelectionToPasteboard(Pasteboard * pasteboard)369 void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
370 {
371     pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
372 }
373 
shouldInsertText(const String & text,Range * range,EditorInsertAction action) const374 bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
375 {
376     return client() && client()->shouldInsertText(text, range, action);
377 }
378 
shouldShowDeleteInterface(HTMLElement * element) const379 bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
380 {
381     return client() && client()->shouldShowDeleteInterface(element);
382 }
383 
respondToChangedSelection(const VisibleSelection & oldSelection)384 void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
385 {
386     if (client())
387         client()->respondToChangedSelection();
388     m_deleteButtonController->respondToChangedSelection(oldSelection);
389 }
390 
respondToChangedContents(const VisibleSelection & endingSelection)391 void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
392 {
393     if (AXObjectCache::accessibilityEnabled()) {
394         Node* node = endingSelection.start().node();
395         if (node)
396             m_frame->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged", false);
397     }
398 
399     if (client())
400         client()->respondToChangedContents();
401 }
402 
fontForSelection(bool & hasMultipleFonts) const403 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
404 {
405 #if !PLATFORM(QT)
406     hasMultipleFonts = false;
407 
408     if (!m_frame->selection()->isRange()) {
409         Node* nodeToRemove;
410         RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove); // sets nodeToRemove
411 
412         const SimpleFontData* result = 0;
413         if (style)
414             result = style->font().primaryFont();
415 
416         if (nodeToRemove) {
417             ExceptionCode ec;
418             nodeToRemove->remove(ec);
419             ASSERT(ec == 0);
420         }
421 
422         return result;
423     }
424 
425     const SimpleFontData* font = 0;
426 
427     RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
428     Node* startNode = range->editingStartPosition().node();
429     if (startNode) {
430         Node* pastEnd = range->pastLastNode();
431         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
432         // unreproducible case where this didn't happen, so check for nil also.
433         for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
434             RenderObject *renderer = n->renderer();
435             if (!renderer)
436                 continue;
437             // FIXME: Are there any node types that have renderers, but that we should be skipping?
438             const SimpleFontData* f = renderer->style()->font().primaryFont();
439             if (!font)
440                 font = f;
441             else if (font != f) {
442                 hasMultipleFonts = true;
443                 break;
444             }
445         }
446     }
447 
448     return font;
449 #else
450     return 0;
451 #endif
452 }
453 
textDirectionForSelection(bool & hasNestedOrMultipleEmbeddings) const454 WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbeddings) const
455 {
456     hasNestedOrMultipleEmbeddings = true;
457 
458     if (m_frame->selection()->isNone())
459         return NaturalWritingDirection;
460 
461     Position pos = m_frame->selection()->selection().start().downstream();
462 
463     Node* node = pos.node();
464     if (!node)
465         return NaturalWritingDirection;
466 
467     Position end;
468     if (m_frame->selection()->isRange()) {
469         end = m_frame->selection()->selection().end().upstream();
470 
471         Node* pastLast = Range::create(m_frame->document(), rangeCompliantEquivalent(pos), rangeCompliantEquivalent(end))->pastLastNode();
472         for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
473             if (!n->isStyledElement())
474                 continue;
475 
476             RefPtr<CSSComputedStyleDeclaration> style = computedStyle(n);
477             RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
478             if (!unicodeBidi)
479                 continue;
480 
481             ASSERT(unicodeBidi->isPrimitiveValue());
482             int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
483             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
484                 return NaturalWritingDirection;
485         }
486     }
487 
488     if (m_frame->selection()->isCaret()) {
489         if (CSSMutableStyleDeclaration *typingStyle = m_frame->typingStyle()) {
490             RefPtr<CSSValue> unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
491             if (unicodeBidi) {
492                 ASSERT(unicodeBidi->isPrimitiveValue());
493                 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
494                 if (unicodeBidiValue == CSSValueEmbed) {
495                     RefPtr<CSSValue> direction = typingStyle->getPropertyCSSValue(CSSPropertyDirection);
496                     ASSERT(!direction || direction->isPrimitiveValue());
497                     if (direction) {
498                         hasNestedOrMultipleEmbeddings = false;
499                         return static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
500                     }
501                 } else if (unicodeBidiValue == CSSValueNormal) {
502                     hasNestedOrMultipleEmbeddings = false;
503                     return NaturalWritingDirection;
504                 }
505             }
506         }
507         node = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
508     }
509 
510     // 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
511     // to decide.
512     Node* block = enclosingBlock(node);
513     WritingDirection foundDirection = NaturalWritingDirection;
514 
515     for (; node != block; node = node->parent()) {
516         if (!node->isStyledElement())
517             continue;
518 
519         RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
520         RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
521         if (!unicodeBidi)
522             continue;
523 
524         ASSERT(unicodeBidi->isPrimitiveValue());
525         int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
526         if (unicodeBidiValue == CSSValueNormal)
527             continue;
528 
529         if (unicodeBidiValue == CSSValueBidiOverride)
530             return NaturalWritingDirection;
531 
532         ASSERT(unicodeBidiValue == CSSValueEmbed);
533         RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
534         if (!direction)
535             continue;
536 
537         ASSERT(direction->isPrimitiveValue());
538         int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
539         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
540             continue;
541 
542         if (foundDirection != NaturalWritingDirection)
543             return NaturalWritingDirection;
544 
545         // In the range case, make sure that the embedding element persists until the end of the range.
546         if (m_frame->selection()->isRange() && !end.node()->isDescendantOf(node))
547             return NaturalWritingDirection;
548 
549         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
550     }
551     hasNestedOrMultipleEmbeddings = false;
552     return foundDirection;
553 }
554 
hasBidiSelection() const555 bool Editor::hasBidiSelection() const
556 {
557     if (m_frame->selection()->isNone())
558         return false;
559 
560     Node* startNode;
561     if (m_frame->selection()->isRange()) {
562         startNode = m_frame->selection()->selection().start().downstream().node();
563         Node* endNode = m_frame->selection()->selection().end().upstream().node();
564         if (enclosingBlock(startNode) != enclosingBlock(endNode))
565             return false;
566     } else
567         startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().node();
568 
569     RenderObject* renderer = startNode->renderer();
570     while (renderer && !renderer->isRenderBlock())
571         renderer = renderer->parent();
572 
573     if (!renderer)
574         return false;
575 
576     RenderStyle* style = renderer->style();
577     if (style->direction() == RTL)
578         return true;
579 
580     return toRenderBlock(renderer)->containsNonZeroBidiLevel();
581 }
582 
selectionUnorderedListState() const583 TriState Editor::selectionUnorderedListState() const
584 {
585     if (m_frame->selection()->isCaret()) {
586         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag))
587             return TrueTriState;
588     } else if (m_frame->selection()->isRange()) {
589         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag);
590         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag);
591         if (startNode && endNode && startNode == endNode)
592             return TrueTriState;
593     }
594 
595     return FalseTriState;
596 }
597 
selectionOrderedListState() const598 TriState Editor::selectionOrderedListState() const
599 {
600     if (m_frame->selection()->isCaret()) {
601         if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag))
602             return TrueTriState;
603     } else if (m_frame->selection()->isRange()) {
604         Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag);
605         Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag);
606         if (startNode && endNode && startNode == endNode)
607             return TrueTriState;
608     }
609 
610     return FalseTriState;
611 }
612 
insertOrderedList()613 PassRefPtr<Node> Editor::insertOrderedList()
614 {
615     if (!canEditRichly())
616         return 0;
617 
618     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList);
619     revealSelectionAfterEditingOperation();
620     return newList;
621 }
622 
insertUnorderedList()623 PassRefPtr<Node> Editor::insertUnorderedList()
624 {
625     if (!canEditRichly())
626         return 0;
627 
628     RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList);
629     revealSelectionAfterEditingOperation();
630     return newList;
631 }
632 
canIncreaseSelectionListLevel()633 bool Editor::canIncreaseSelectionListLevel()
634 {
635     return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
636 }
637 
canDecreaseSelectionListLevel()638 bool Editor::canDecreaseSelectionListLevel()
639 {
640     return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
641 }
642 
increaseSelectionListLevel()643 PassRefPtr<Node> Editor::increaseSelectionListLevel()
644 {
645     if (!canEditRichly() || m_frame->selection()->isNone())
646         return 0;
647 
648     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document());
649     revealSelectionAfterEditingOperation();
650     return newList;
651 }
652 
increaseSelectionListLevelOrdered()653 PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
654 {
655     if (!canEditRichly() || m_frame->selection()->isNone())
656         return 0;
657 
658     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document());
659     revealSelectionAfterEditingOperation();
660     return newList.release();
661 }
662 
increaseSelectionListLevelUnordered()663 PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
664 {
665     if (!canEditRichly() || m_frame->selection()->isNone())
666         return 0;
667 
668     RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document());
669     revealSelectionAfterEditingOperation();
670     return newList.release();
671 }
672 
decreaseSelectionListLevel()673 void Editor::decreaseSelectionListLevel()
674 {
675     if (!canEditRichly() || m_frame->selection()->isNone())
676         return;
677 
678     DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
679     revealSelectionAfterEditingOperation();
680 }
681 
removeFormattingAndStyle()682 void Editor::removeFormattingAndStyle()
683 {
684     applyCommand(RemoveFormatCommand::create(m_frame->document()));
685 }
686 
clearLastEditCommand()687 void Editor::clearLastEditCommand()
688 {
689     m_lastEditCommand.clear();
690 }
691 
692 // Returns whether caller should continue with "the default processing", which is the same as
693 // the event handler NOT setting the return value to false
dispatchCPPEvent(const AtomicString & eventType,ClipboardAccessPolicy policy)694 bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
695 {
696     Node* target = m_frame->selection()->start().element();
697     if (!target)
698         target = m_frame->document()->body();
699     if (!target)
700         return true;
701     target = target->shadowAncestorNode();
702 
703     RefPtr<Clipboard> clipboard = newGeneralClipboard(policy);
704 
705     ExceptionCode ec = 0;
706     RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
707     target->dispatchEvent(evt, ec);
708     bool noDefaultProcessing = evt->defaultPrevented();
709 
710     // invalidate clipboard here for security
711     clipboard->setAccessPolicy(ClipboardNumb);
712 
713     return !noDefaultProcessing;
714 }
715 
applyStyle(CSSStyleDeclaration * style,EditAction editingAction)716 void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
717 {
718     switch (m_frame->selection()->selectionType()) {
719         case VisibleSelection::NoSelection:
720             // do nothing
721             break;
722         case VisibleSelection::CaretSelection:
723             m_frame->computeAndSetTypingStyle(style, editingAction);
724             break;
725         case VisibleSelection::RangeSelection:
726             if (style)
727                 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction));
728             break;
729     }
730 }
731 
shouldApplyStyle(CSSStyleDeclaration * style,Range * range)732 bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
733 {
734     return client()->shouldApplyStyle(style, range);
735 }
736 
applyParagraphStyle(CSSStyleDeclaration * style,EditAction editingAction)737 void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
738 {
739     switch (m_frame->selection()->selectionType()) {
740         case VisibleSelection::NoSelection:
741             // do nothing
742             break;
743         case VisibleSelection::CaretSelection:
744         case VisibleSelection::RangeSelection:
745             if (style)
746                 applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
747             break;
748     }
749 }
750 
applyStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)751 void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
752 {
753     if (!style || style->length() == 0 || !canEditRichly())
754         return;
755 
756     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
757         applyStyle(style, editingAction);
758 }
759 
applyParagraphStyleToSelection(CSSStyleDeclaration * style,EditAction editingAction)760 void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction)
761 {
762     if (!style || style->length() == 0 || !canEditRichly())
763         return;
764 
765     if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
766         applyParagraphStyle(style, editingAction);
767 }
768 
clientIsEditable() const769 bool Editor::clientIsEditable() const
770 {
771     return client() && client()->isEditable();
772 }
773 
774 // CSS properties that only has a visual difference when applied to text.
775 static const int textOnlyProperties[] = {
776     CSSPropertyTextDecoration,
777     CSSPropertyWebkitTextDecorationsInEffect,
778     CSSPropertyFontStyle,
779     CSSPropertyFontWeight,
780     CSSPropertyColor,
781 };
782 
triStateOfStyleInComputedStyle(CSSStyleDeclaration * desiredStyle,CSSComputedStyleDeclaration * computedStyle,bool ignoreTextOnlyProperties=false)783 static TriState triStateOfStyleInComputedStyle(CSSStyleDeclaration* desiredStyle, CSSComputedStyleDeclaration* computedStyle, bool ignoreTextOnlyProperties = false)
784 {
785     RefPtr<CSSMutableStyleDeclaration> diff = getPropertiesNotInComputedStyle(desiredStyle, computedStyle);
786 
787     if (ignoreTextOnlyProperties)
788         diff->removePropertiesInSet(textOnlyProperties, sizeof(textOnlyProperties)/sizeof(textOnlyProperties[0]));
789 
790     if (!diff->length())
791         return TrueTriState;
792     else if (diff->length() == desiredStyle->length())
793         return FalseTriState;
794     return MixedTriState;
795 }
796 
selectionStartHasStyle(CSSStyleDeclaration * style) const797 bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const
798 {
799     Node* nodeToRemove;
800     RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
801     if (!selectionStyle)
802         return false;
803     TriState state = triStateOfStyleInComputedStyle(style, selectionStyle.get());
804     if (nodeToRemove) {
805         ExceptionCode ec = 0;
806         nodeToRemove->remove(ec);
807         ASSERT(ec == 0);
808     }
809     return state == TrueTriState;
810 }
811 
selectionHasStyle(CSSStyleDeclaration * style) const812 TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const
813 {
814     TriState state = FalseTriState;
815 
816     if (!m_frame->selection()->isRange()) {
817         Node* nodeToRemove;
818         RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove);
819         if (!selectionStyle)
820             return FalseTriState;
821         state = triStateOfStyleInComputedStyle(style, selectionStyle.get());
822         if (nodeToRemove) {
823             ExceptionCode ec = 0;
824             nodeToRemove->remove(ec);
825             ASSERT(ec == 0);
826         }
827     } else {
828         for (Node* node = m_frame->selection()->start().node(); node; node = node->traverseNextNode()) {
829             RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
830             if (nodeStyle) {
831                 TriState nodeState = triStateOfStyleInComputedStyle(style, nodeStyle.get(), !node->isTextNode());
832                 if (node == m_frame->selection()->start().node())
833                     state = nodeState;
834                 else if (state != nodeState) {
835                     state = MixedTriState;
836                     break;
837                 }
838             }
839             if (node == m_frame->selection()->end().node())
840                 break;
841         }
842     }
843 
844     return state;
845 }
indent()846 void Editor::indent()
847 {
848     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
849 }
850 
outdent()851 void Editor::outdent()
852 {
853     applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
854 }
855 
dispatchEditableContentChangedEvents(const EditCommand & command)856 static void dispatchEditableContentChangedEvents(const EditCommand& command)
857 {
858     Element* startRoot = command.startingRootEditableElement();
859     Element* endRoot = command.endingRootEditableElement();
860     ExceptionCode ec;
861     if (startRoot)
862         startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
863     if (endRoot && endRoot != startRoot)
864         endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec);
865 }
866 
appliedEditing(PassRefPtr<EditCommand> cmd)867 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
868 {
869     dispatchEditableContentChangedEvents(*cmd);
870 
871     VisibleSelection newSelection(cmd->endingSelection());
872     // Don't clear the typing style with this selection change.  We do those things elsewhere if necessary.
873     changeSelectionAfterCommand(newSelection, false, false, cmd.get());
874 
875     if (!cmd->preservesTypingStyle())
876         m_frame->setTypingStyle(0);
877 
878     // Command will be equal to last edit command only in the case of typing
879     if (m_lastEditCommand.get() == cmd)
880         ASSERT(cmd->isTypingCommand());
881     else {
882         // Only register a new undo command if the command passed in is
883         // different from the last command
884         m_lastEditCommand = cmd;
885         if (client())
886             client()->registerCommandForUndo(m_lastEditCommand);
887     }
888     respondToChangedContents(newSelection);
889 }
890 
unappliedEditing(PassRefPtr<EditCommand> cmd)891 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
892 {
893     dispatchEditableContentChangedEvents(*cmd);
894 
895     VisibleSelection newSelection(cmd->startingSelection());
896     changeSelectionAfterCommand(newSelection, true, true, cmd.get());
897 
898     m_lastEditCommand = 0;
899     if (client())
900         client()->registerCommandForRedo(cmd);
901     respondToChangedContents(newSelection);
902 }
903 
reappliedEditing(PassRefPtr<EditCommand> cmd)904 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
905 {
906     dispatchEditableContentChangedEvents(*cmd);
907 
908     VisibleSelection newSelection(cmd->endingSelection());
909     changeSelectionAfterCommand(newSelection, true, true, cmd.get());
910 
911     m_lastEditCommand = 0;
912     if (client())
913         client()->registerCommandForUndo(cmd);
914     respondToChangedContents(newSelection);
915 }
916 
Editor(Frame * frame)917 Editor::Editor(Frame* frame)
918     : m_frame(frame)
919     , m_deleteButtonController(new DeleteButtonController(frame))
920     , m_ignoreCompositionSelectionChange(false)
921     , m_shouldStartNewKillRingSequence(false)
922     // This is off by default, since most editors want this behavior (this matches IE but not FF).
923     , m_shouldStyleWithCSS(false)
924 {
925 }
926 
~Editor()927 Editor::~Editor()
928 {
929 }
930 
clear()931 void Editor::clear()
932 {
933     m_compositionNode = 0;
934     m_customCompositionUnderlines.clear();
935     m_shouldStyleWithCSS = false;
936 }
937 
insertText(const String & text,Event * triggeringEvent)938 bool Editor::insertText(const String& text, Event* triggeringEvent)
939 {
940     return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
941 }
942 
insertTextWithoutSendingTextEvent(const String & text,bool selectInsertedText,Event * triggeringEvent)943 bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent)
944 {
945     if (text.isEmpty())
946         return false;
947 
948     VisibleSelection selection = selectionForCommand(triggeringEvent);
949     if (!selection.isContentEditable())
950         return false;
951     RefPtr<Range> range = selection.toNormalizedRange();
952 
953     if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
954         return true;
955 
956     // Get the selection to use for the event that triggered this insertText.
957     // If the event handler changed the selection, we may want to use a different selection
958     // that is contained in the event target.
959     selection = selectionForCommand(triggeringEvent);
960     if (selection.isContentEditable()) {
961         if (Node* selectionStart = selection.start().node()) {
962             RefPtr<Document> document = selectionStart->document();
963 
964             // Insert the text
965             TypingCommand::insertText(document.get(), text, selection, selectInsertedText);
966 
967             // Reveal the current selection
968             if (Frame* editedFrame = document->frame())
969                 if (Page* page = editedFrame->page())
970                     page->focusController()->focusedOrMainFrame()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
971         }
972     }
973 
974     return true;
975 }
976 
insertLineBreak()977 bool Editor::insertLineBreak()
978 {
979     if (!canEdit())
980         return false;
981 
982     if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
983         return true;
984 
985     TypingCommand::insertLineBreak(m_frame->document());
986     revealSelectionAfterEditingOperation();
987     return true;
988 }
989 
insertParagraphSeparator()990 bool Editor::insertParagraphSeparator()
991 {
992     if (!canEdit())
993         return false;
994 
995     if (!canEditRichly())
996         return insertLineBreak();
997 
998     if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
999         return true;
1000 
1001     TypingCommand::insertParagraphSeparator(m_frame->document());
1002     revealSelectionAfterEditingOperation();
1003     return true;
1004 }
1005 
cut()1006 void Editor::cut()
1007 {
1008     if (tryDHTMLCut())
1009         return; // DHTML did the whole operation
1010     if (!canCut()) {
1011         systemBeep();
1012         return;
1013     }
1014     RefPtr<Range> selection = selectedRange();
1015     if (shouldDeleteRange(selection.get())) {
1016         Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame);
1017         didWriteSelectionToPasteboard();
1018         deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1019     }
1020 }
1021 
copy()1022 void Editor::copy()
1023 {
1024     if (tryDHTMLCopy())
1025         return; // DHTML did the whole operation
1026     if (!canCopy()) {
1027         systemBeep();
1028         return;
1029     }
1030 
1031     Document* document = m_frame->document();
1032     if (HTMLImageElement* imageElement = imageElementFromImageDocument(document))
1033         Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title());
1034     else
1035         Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
1036 
1037     didWriteSelectionToPasteboard();
1038 }
1039 
1040 #if !PLATFORM(MAC)
1041 
paste()1042 void Editor::paste()
1043 {
1044     ASSERT(m_frame->document());
1045     if (tryDHTMLPaste())
1046         return;     // DHTML did the whole operation
1047     if (!canPaste())
1048         return;
1049     DocLoader* loader = m_frame->document()->docLoader();
1050     loader->setAllowStaleResources(true);
1051     if (m_frame->selection()->isContentRichlyEditable())
1052         pasteWithPasteboard(Pasteboard::generalPasteboard(), true);
1053     else
1054         pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1055     loader->setAllowStaleResources(false);
1056 }
1057 
1058 #endif
1059 
pasteAsPlainText()1060 void Editor::pasteAsPlainText()
1061 {
1062     if (!canPaste())
1063         return;
1064     pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1065 }
1066 
performDelete()1067 void Editor::performDelete()
1068 {
1069     if (!canDelete()) {
1070         systemBeep();
1071         return;
1072     }
1073 
1074     addToKillRing(selectedRange().get(), false);
1075     deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
1076 
1077     // clear the "start new kill ring sequence" setting, because it was set to true
1078     // when the selection was updated by deleting the range
1079     setStartNewKillRingSequence(false);
1080 }
1081 
copyURL(const KURL & url,const String & title)1082 void Editor::copyURL(const KURL& url, const String& title)
1083 {
1084     Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1085 }
1086 
copyImage(const HitTestResult & result)1087 void Editor::copyImage(const HitTestResult& result)
1088 {
1089     KURL url = result.absoluteLinkURL();
1090     if (url.isEmpty())
1091         url = result.absoluteImageURL();
1092 
1093     Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString());
1094 }
1095 
isContinuousSpellCheckingEnabled()1096 bool Editor::isContinuousSpellCheckingEnabled()
1097 {
1098     return client() && client()->isContinuousSpellCheckingEnabled();
1099 }
1100 
toggleContinuousSpellChecking()1101 void Editor::toggleContinuousSpellChecking()
1102 {
1103     if (client())
1104         client()->toggleContinuousSpellChecking();
1105 }
1106 
isGrammarCheckingEnabled()1107 bool Editor::isGrammarCheckingEnabled()
1108 {
1109     return client() && client()->isGrammarCheckingEnabled();
1110 }
1111 
toggleGrammarChecking()1112 void Editor::toggleGrammarChecking()
1113 {
1114     if (client())
1115         client()->toggleGrammarChecking();
1116 }
1117 
spellCheckerDocumentTag()1118 int Editor::spellCheckerDocumentTag()
1119 {
1120     return client() ? client()->spellCheckerDocumentTag() : 0;
1121 }
1122 
1123 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1124 
uppercaseWord()1125 void Editor::uppercaseWord()
1126 {
1127     if (client())
1128         client()->uppercaseWord();
1129 }
1130 
lowercaseWord()1131 void Editor::lowercaseWord()
1132 {
1133     if (client())
1134         client()->lowercaseWord();
1135 }
1136 
capitalizeWord()1137 void Editor::capitalizeWord()
1138 {
1139     if (client())
1140         client()->capitalizeWord();
1141 }
1142 
showSubstitutionsPanel()1143 void Editor::showSubstitutionsPanel()
1144 {
1145     if (!client()) {
1146         LOG_ERROR("No NSSpellChecker");
1147         return;
1148     }
1149 
1150     if (client()->substitutionsPanelIsShowing()) {
1151         client()->showSubstitutionsPanel(false);
1152         return;
1153     }
1154     client()->showSubstitutionsPanel(true);
1155 }
1156 
substitutionsPanelIsShowing()1157 bool Editor::substitutionsPanelIsShowing()
1158 {
1159     if (!client())
1160         return false;
1161     return client()->substitutionsPanelIsShowing();
1162 }
1163 
toggleSmartInsertDelete()1164 void Editor::toggleSmartInsertDelete()
1165 {
1166     if (client())
1167         client()->toggleSmartInsertDelete();
1168 }
1169 
isAutomaticQuoteSubstitutionEnabled()1170 bool Editor::isAutomaticQuoteSubstitutionEnabled()
1171 {
1172     return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1173 }
1174 
toggleAutomaticQuoteSubstitution()1175 void Editor::toggleAutomaticQuoteSubstitution()
1176 {
1177     if (client())
1178         client()->toggleAutomaticQuoteSubstitution();
1179 }
1180 
isAutomaticLinkDetectionEnabled()1181 bool Editor::isAutomaticLinkDetectionEnabled()
1182 {
1183     return client() && client()->isAutomaticLinkDetectionEnabled();
1184 }
1185 
toggleAutomaticLinkDetection()1186 void Editor::toggleAutomaticLinkDetection()
1187 {
1188     if (client())
1189         client()->toggleAutomaticLinkDetection();
1190 }
1191 
isAutomaticDashSubstitutionEnabled()1192 bool Editor::isAutomaticDashSubstitutionEnabled()
1193 {
1194     return client() && client()->isAutomaticDashSubstitutionEnabled();
1195 }
1196 
toggleAutomaticDashSubstitution()1197 void Editor::toggleAutomaticDashSubstitution()
1198 {
1199     if (client())
1200         client()->toggleAutomaticDashSubstitution();
1201 }
1202 
isAutomaticTextReplacementEnabled()1203 bool Editor::isAutomaticTextReplacementEnabled()
1204 {
1205     return client() && client()->isAutomaticTextReplacementEnabled();
1206 }
1207 
toggleAutomaticTextReplacement()1208 void Editor::toggleAutomaticTextReplacement()
1209 {
1210     if (client())
1211         client()->toggleAutomaticTextReplacement();
1212 }
1213 
isAutomaticSpellingCorrectionEnabled()1214 bool Editor::isAutomaticSpellingCorrectionEnabled()
1215 {
1216     return client() && client()->isAutomaticSpellingCorrectionEnabled();
1217 }
1218 
toggleAutomaticSpellingCorrection()1219 void Editor::toggleAutomaticSpellingCorrection()
1220 {
1221     if (client())
1222         client()->toggleAutomaticSpellingCorrection();
1223 }
1224 
1225 #endif
1226 
shouldEndEditing(Range * range)1227 bool Editor::shouldEndEditing(Range* range)
1228 {
1229     return client() && client()->shouldEndEditing(range);
1230 }
1231 
shouldBeginEditing(Range * range)1232 bool Editor::shouldBeginEditing(Range* range)
1233 {
1234     return client() && client()->shouldBeginEditing(range);
1235 }
1236 
clearUndoRedoOperations()1237 void Editor::clearUndoRedoOperations()
1238 {
1239     if (client())
1240         client()->clearUndoRedoOperations();
1241 }
1242 
canUndo()1243 bool Editor::canUndo()
1244 {
1245     return client() && client()->canUndo();
1246 }
1247 
undo()1248 void Editor::undo()
1249 {
1250     if (client())
1251         client()->undo();
1252 }
1253 
canRedo()1254 bool Editor::canRedo()
1255 {
1256     return client() && client()->canRedo();
1257 }
1258 
redo()1259 void Editor::redo()
1260 {
1261     if (client())
1262         client()->redo();
1263 }
1264 
didBeginEditing()1265 void Editor::didBeginEditing()
1266 {
1267     if (client())
1268         client()->didBeginEditing();
1269 }
1270 
didEndEditing()1271 void Editor::didEndEditing()
1272 {
1273     if (client())
1274         client()->didEndEditing();
1275 }
1276 
didWriteSelectionToPasteboard()1277 void Editor::didWriteSelectionToPasteboard()
1278 {
1279     if (client())
1280         client()->didWriteSelectionToPasteboard();
1281 }
1282 
toggleBold()1283 void Editor::toggleBold()
1284 {
1285     command("ToggleBold").execute();
1286 }
1287 
toggleUnderline()1288 void Editor::toggleUnderline()
1289 {
1290     command("ToggleUnderline").execute();
1291 }
1292 
setBaseWritingDirection(WritingDirection direction)1293 void Editor::setBaseWritingDirection(WritingDirection direction)
1294 {
1295     Node* focusedNode = frame()->document()->focusedNode();
1296     if (focusedNode && (focusedNode->hasTagName(textareaTag)
1297                         || (focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT
1298                                                                 || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH)))) {
1299         if (direction == NaturalWritingDirection)
1300             return;
1301         static_cast<HTMLElement*>(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
1302         frame()->document()->updateStyleIfNeeded();
1303         return;
1304     }
1305 
1306     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
1307     style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false);
1308     applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection);
1309 }
1310 
selectComposition()1311 void Editor::selectComposition()
1312 {
1313     RefPtr<Range> range = compositionRange();
1314     if (!range)
1315         return;
1316 
1317     // The composition can start inside a composed character sequence, so we have to override checks.
1318     // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
1319     VisibleSelection selection;
1320     selection.setWithoutValidation(range->startPosition(), range->endPosition());
1321     m_frame->selection()->setSelection(selection, false, false);
1322 }
1323 
confirmComposition()1324 void Editor::confirmComposition()
1325 {
1326     if (!m_compositionNode)
1327         return;
1328     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1329 }
1330 
confirmCompositionWithoutDisturbingSelection()1331 void Editor::confirmCompositionWithoutDisturbingSelection()
1332 {
1333     if (!m_compositionNode)
1334         return;
1335     confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1336 }
1337 
confirmComposition(const String & text)1338 void Editor::confirmComposition(const String& text)
1339 {
1340     confirmComposition(text, false);
1341 }
1342 
confirmComposition(const String & text,bool preserveSelection)1343 void Editor::confirmComposition(const String& text, bool preserveSelection)
1344 {
1345     setIgnoreCompositionSelectionChange(true);
1346 
1347     VisibleSelection oldSelection = m_frame->selection()->selection();
1348 
1349     selectComposition();
1350 
1351     if (m_frame->selection()->isNone()) {
1352         setIgnoreCompositionSelectionChange(false);
1353         return;
1354     }
1355 
1356     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1357     // will delete the old composition with an optimized replace operation.
1358     if (text.isEmpty())
1359         TypingCommand::deleteSelection(m_frame->document(), false);
1360 
1361     m_compositionNode = 0;
1362     m_customCompositionUnderlines.clear();
1363 
1364     insertText(text, 0);
1365 
1366     if (preserveSelection)
1367         m_frame->selection()->setSelection(oldSelection, false, false);
1368 
1369     setIgnoreCompositionSelectionChange(false);
1370 }
1371 
setComposition(const String & text,const Vector<CompositionUnderline> & underlines,unsigned selectionStart,unsigned selectionEnd)1372 void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
1373 {
1374     setIgnoreCompositionSelectionChange(true);
1375 
1376     selectComposition();
1377 
1378     if (m_frame->selection()->isNone()) {
1379         setIgnoreCompositionSelectionChange(false);
1380         return;
1381     }
1382 
1383     // If text is empty, then delete the old composition here.  If text is non-empty, InsertTextCommand::input
1384     // will delete the old composition with an optimized replace operation.
1385     if (text.isEmpty())
1386         TypingCommand::deleteSelection(m_frame->document(), false);
1387 
1388     m_compositionNode = 0;
1389     m_customCompositionUnderlines.clear();
1390 
1391     if (!text.isEmpty()) {
1392         TypingCommand::insertText(m_frame->document(), text, true, true);
1393 
1394         Node* baseNode = m_frame->selection()->base().node();
1395         unsigned baseOffset = m_frame->selection()->base().deprecatedEditingOffset();
1396         Node* extentNode = m_frame->selection()->extent().node();
1397         unsigned extentOffset = m_frame->selection()->extent().deprecatedEditingOffset();
1398 
1399         if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
1400             m_compositionNode = static_cast<Text*>(baseNode);
1401             m_compositionStart = baseOffset;
1402             m_compositionEnd = extentOffset;
1403             m_customCompositionUnderlines = underlines;
1404             size_t numUnderlines = m_customCompositionUnderlines.size();
1405             for (size_t i = 0; i < numUnderlines; ++i) {
1406                 m_customCompositionUnderlines[i].startOffset += baseOffset;
1407                 m_customCompositionUnderlines[i].endOffset += baseOffset;
1408             }
1409             if (baseNode->renderer())
1410                 baseNode->renderer()->repaint();
1411 
1412             unsigned start = min(baseOffset + selectionStart, extentOffset);
1413             unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
1414             RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
1415             m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
1416         }
1417     }
1418 
1419     setIgnoreCompositionSelectionChange(false);
1420 }
1421 
ignoreSpelling()1422 void Editor::ignoreSpelling()
1423 {
1424     if (!client())
1425         return;
1426 
1427     RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
1428     if (selectedRange)
1429         frame()->document()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
1430 
1431     String text = frame()->selectedText();
1432     ASSERT(text.length() != 0);
1433     client()->ignoreWordInSpellDocument(text);
1434 }
1435 
learnSpelling()1436 void Editor::learnSpelling()
1437 {
1438     if (!client())
1439         return;
1440 
1441     // FIXME: We don't call this on the Mac, and it should remove misppelling markers around the
1442     // learned word, see <rdar://problem/5396072>.
1443 
1444     String text = frame()->selectedText();
1445     ASSERT(text.length() != 0);
1446     client()->learnWord(text);
1447 }
1448 
findFirstMisspellingInRange(EditorClient * client,Range * searchRange,int & firstMisspellingOffset,bool markAll,RefPtr<Range> & firstMisspellingRange)1449 static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll, RefPtr<Range>& firstMisspellingRange)
1450 {
1451     ASSERT_ARG(client, client);
1452     ASSERT_ARG(searchRange, searchRange);
1453 
1454     WordAwareIterator it(searchRange);
1455     firstMisspellingOffset = 0;
1456 
1457     String firstMisspelling;
1458     int currentChunkOffset = 0;
1459 
1460     while (!it.atEnd()) {
1461         const UChar* chars = it.characters();
1462         int len = it.length();
1463 
1464         // Skip some work for one-space-char hunks
1465         if (!(len == 1 && chars[0] == ' ')) {
1466 
1467             int misspellingLocation = -1;
1468             int misspellingLength = 0;
1469             client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
1470 
1471             // 5490627 shows that there was some code path here where the String constructor below crashes.
1472             // We don't know exactly what combination of bad input caused this, so we're making this much
1473             // more robust against bad input on release builds.
1474             ASSERT(misspellingLength >= 0);
1475             ASSERT(misspellingLocation >= -1);
1476             ASSERT(misspellingLength == 0 || misspellingLocation >= 0);
1477             ASSERT(misspellingLocation < len);
1478             ASSERT(misspellingLength <= len);
1479             ASSERT(misspellingLocation + misspellingLength <= len);
1480 
1481             if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
1482 
1483                 // Compute range of misspelled word
1484                 RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
1485 
1486                 // Remember first-encountered misspelling and its offset.
1487                 if (!firstMisspelling) {
1488                     firstMisspellingOffset = currentChunkOffset + misspellingLocation;
1489                     firstMisspelling = String(chars + misspellingLocation, misspellingLength);
1490                     firstMisspellingRange = misspellingRange;
1491                 }
1492 
1493                 // Store marker for misspelled word.
1494                 ExceptionCode ec = 0;
1495                 misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1496                 ASSERT(ec == 0);
1497 
1498                 // Bail out if we're marking only the first misspelling, and not all instances.
1499                 if (!markAll)
1500                     break;
1501             }
1502         }
1503 
1504         currentChunkOffset += len;
1505         it.advance();
1506     }
1507 
1508     return firstMisspelling;
1509 }
1510 
1511 #ifndef BUILDING_ON_TIGER
1512 
paragraphAlignedRangeForRange(Range * arbitraryRange,int & offsetIntoParagraphAlignedRange,String & paragraphString)1513 static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
1514 {
1515     ASSERT_ARG(arbitraryRange, arbitraryRange);
1516 
1517     ExceptionCode ec = 0;
1518 
1519     // Expand range to paragraph boundaries
1520     RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
1521     setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
1522     setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
1523 
1524     // Compute offset from start of expanded range to start of original range
1525     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
1526     offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
1527 
1528     // Fill in out parameter with string representing entire paragraph range.
1529     // Someday we might have a caller that doesn't use this, but for now all callers do.
1530     paragraphString = plainText(paragraphRange.get());
1531 
1532     return paragraphRange;
1533 }
1534 
findFirstGrammarDetailInRange(const Vector<GrammarDetail> & grammarDetails,int badGrammarPhraseLocation,int,Range * searchRange,int startOffset,int endOffset,bool markAll)1535 static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int /*badGrammarPhraseLength*/, Range *searchRange, int startOffset, int endOffset, bool markAll)
1536 {
1537     // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1538     // Optionally add a DocumentMarker for each detail in the range.
1539     int earliestDetailLocationSoFar = -1;
1540     int earliestDetailIndex = -1;
1541     for (unsigned i = 0; i < grammarDetails.size(); i++) {
1542         const GrammarDetail* detail = &grammarDetails[i];
1543         ASSERT(detail->length > 0 && detail->location >= 0);
1544 
1545         int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
1546 
1547         // Skip this detail if it starts before the original search range
1548         if (detailStartOffsetInParagraph < startOffset)
1549             continue;
1550 
1551         // Skip this detail if it starts after the original search range
1552         if (detailStartOffsetInParagraph >= endOffset)
1553             continue;
1554 
1555         if (markAll) {
1556             RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
1557             ExceptionCode ec = 0;
1558             badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
1559             ASSERT(ec == 0);
1560         }
1561 
1562         // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
1563         if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
1564             earliestDetailIndex = i;
1565             earliestDetailLocationSoFar = detail->location;
1566         }
1567     }
1568 
1569     return earliestDetailIndex;
1570 }
1571 
findFirstBadGrammarInRange(EditorClient * client,Range * searchRange,GrammarDetail & outGrammarDetail,int & outGrammarPhraseOffset,bool markAll)1572 static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
1573 {
1574     ASSERT_ARG(client, client);
1575     ASSERT_ARG(searchRange, searchRange);
1576 
1577     // Initialize out parameters; these will be updated if we find something to return.
1578     outGrammarDetail.location = -1;
1579     outGrammarDetail.length = 0;
1580     outGrammarDetail.guesses.clear();
1581     outGrammarDetail.userDescription = "";
1582     outGrammarPhraseOffset = 0;
1583 
1584     String firstBadGrammarPhrase;
1585 
1586     // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
1587     // Determine the character offset from the start of the paragraph to the start of the original search range,
1588     // since we will want to ignore results in this area.
1589     int searchRangeStartOffset;
1590     String paragraphString;
1591     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
1592 
1593     // Determine the character offset from the start of the paragraph to the end of the original search range,
1594     // since we will want to ignore results in this area also.
1595     int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
1596 
1597     // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
1598     int startOffset = 0;
1599     while (startOffset < searchRangeEndOffset) {
1600         Vector<GrammarDetail> grammarDetails;
1601         int badGrammarPhraseLocation = -1;
1602         int badGrammarPhraseLength = 0;
1603         client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
1604 
1605         if (badGrammarPhraseLength == 0) {
1606             ASSERT(badGrammarPhraseLocation == -1);
1607             return String();
1608         }
1609 
1610         ASSERT(badGrammarPhraseLocation >= 0);
1611         badGrammarPhraseLocation += startOffset;
1612 
1613 
1614         // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
1615         int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
1616         if (badGrammarIndex >= 0) {
1617             ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
1618             outGrammarDetail = grammarDetails[badGrammarIndex];
1619         }
1620 
1621         // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
1622         // kept going so we could mark all instances).
1623         if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
1624             outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
1625             firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
1626 
1627             // Found one. We're done now, unless we're marking each instance.
1628             if (!markAll)
1629                 break;
1630         }
1631 
1632         // These results were all between the start of the paragraph and the start of the search range; look
1633         // beyond this phrase.
1634         startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
1635     }
1636 
1637     return firstBadGrammarPhrase;
1638 }
1639 
1640 #endif /* not BUILDING_ON_TIGER */
1641 
1642 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1643 
findFirstMisspellingOrBadGrammarInRange(EditorClient * client,Range * searchRange,bool checkGrammar,bool & outIsSpelling,int & outFirstFoundOffset,GrammarDetail & outGrammarDetail)1644 static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Range* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
1645 {
1646     ASSERT_ARG(client, client);
1647     ASSERT_ARG(searchRange, searchRange);
1648 
1649     String firstFoundItem;
1650     String misspelledWord;
1651     String badGrammarPhrase;
1652     ExceptionCode ec = 0;
1653 
1654     // Initialize out parameters; these will be updated if we find something to return.
1655     outIsSpelling = true;
1656     outFirstFoundOffset = 0;
1657     outGrammarDetail.location = -1;
1658     outGrammarDetail.length = 0;
1659     outGrammarDetail.guesses.clear();
1660     outGrammarDetail.userDescription = "";
1661 
1662     // Expand the search range to encompass entire paragraphs, since text checking needs that much context.
1663     // Determine the character offset from the start of the paragraph to the start of the original search range,
1664     // since we will want to ignore results in this area.
1665     RefPtr<Range> paragraphRange = searchRange->cloneRange(ec);
1666     setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition()));
1667     int totalRangeLength = TextIterator::rangeLength(paragraphRange.get());
1668     setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition()));
1669 
1670     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->startPosition());
1671     int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
1672     int totalLengthProcessed = 0;
1673 
1674     bool firstIteration = true;
1675     bool lastIteration = false;
1676     while (totalLengthProcessed < totalRangeLength) {
1677         // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
1678         int currentLength = TextIterator::rangeLength(paragraphRange.get());
1679         int currentStartOffset = firstIteration ? searchRangeStartOffset : 0;
1680         int currentEndOffset = currentLength;
1681         if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPosition())) {
1682             // Determine the character offset from the end of the original search range to the end of the paragraph,
1683             // since we will want to ignore results in this area.
1684             RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->endPosition());
1685             currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get());
1686             lastIteration = true;
1687         }
1688         if (currentStartOffset < currentEndOffset) {
1689             String paragraphString = plainText(paragraphRange.get());
1690             if (paragraphString.length() > 0) {
1691                 bool foundGrammar = false;
1692                 int spellingLocation = 0;
1693                 int grammarPhraseLocation = 0;
1694                 int grammarDetailLocation = 0;
1695                 unsigned grammarDetailIndex = 0;
1696 
1697                 Vector<TextCheckingResult> results;
1698                 uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
1699                 client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
1700 
1701                 for (unsigned i = 0; i < results.size(); i++) {
1702                     const TextCheckingResult* result = &results[i];
1703                     if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
1704                         ASSERT(result->length > 0 && result->location >= 0);
1705                         spellingLocation = result->location;
1706                         misspelledWord = paragraphString.substring(result->location, result->length);
1707                         ASSERT(misspelledWord.length() != 0);
1708                         break;
1709                     } else if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
1710                         ASSERT(result->length > 0 && result->location >= 0);
1711                         // We can't stop after the first grammar result, since there might still be a spelling result after
1712                         // it begins but before the first detail in it, but we can stop if we find a second grammar result.
1713                         if (foundGrammar) break;
1714                         for (unsigned j = 0; j < result->details.size(); j++) {
1715                             const GrammarDetail* detail = &result->details[j];
1716                             ASSERT(detail->length > 0 && detail->location >= 0);
1717                             if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
1718                                 grammarDetailIndex = j;
1719                                 grammarDetailLocation = result->location + detail->location;
1720                                 foundGrammar = true;
1721                             }
1722                         }
1723                         if (foundGrammar) {
1724                             grammarPhraseLocation = result->location;
1725                             outGrammarDetail = result->details[grammarDetailIndex];
1726                             badGrammarPhrase = paragraphString.substring(result->location, result->length);
1727                             ASSERT(badGrammarPhrase.length() != 0);
1728                         }
1729                     }
1730                 }
1731 
1732                 if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
1733                     int spellingOffset = spellingLocation - currentStartOffset;
1734                     if (!firstIteration) {
1735                         RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
1736                         spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
1737                     }
1738                     outIsSpelling = true;
1739                     outFirstFoundOffset = spellingOffset;
1740                     firstFoundItem = misspelledWord;
1741                     break;
1742                 } else if (checkGrammar && !badGrammarPhrase.isEmpty()) {
1743                     int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
1744                     if (!firstIteration) {
1745                         RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
1746                         grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
1747                     }
1748                     outIsSpelling = false;
1749                     outFirstFoundOffset = grammarPhraseOffset;
1750                     firstFoundItem = badGrammarPhrase;
1751                     break;
1752                 }
1753             }
1754         }
1755         if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
1756             break;
1757         setStart(paragraphRange.get(), startOfNextParagraph(paragraphRange->endPosition()));
1758         setEnd(paragraphRange.get(), endOfParagraph(paragraphRange->startPosition()));
1759         firstIteration = false;
1760         totalLengthProcessed += currentLength;
1761     }
1762     return firstFoundItem;
1763 }
1764 
1765 #endif
1766 
advanceToNextMisspelling(bool startBeforeSelection)1767 void Editor::advanceToNextMisspelling(bool startBeforeSelection)
1768 {
1769     ExceptionCode ec = 0;
1770 
1771     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
1772     // then we wrap and search from the doc start to (approximately) where we started.
1773 
1774     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
1775     // repeated "check spelling" commands work.
1776     VisibleSelection selection(frame()->selection()->selection());
1777     RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
1778     bool startedWithSelection = false;
1779     if (selection.start().node()) {
1780         startedWithSelection = true;
1781         if (startBeforeSelection) {
1782             VisiblePosition start(selection.visibleStart());
1783             // We match AppKit's rule: Start 1 character before the selection.
1784             VisiblePosition oneBeforeStart = start.previous();
1785             setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
1786         } else
1787             setStart(spellingSearchRange.get(), selection.visibleEnd());
1788     }
1789 
1790     Position position = spellingSearchRange->startPosition();
1791     if (!isEditablePosition(position)) {
1792         // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
1793         // selection is editable.
1794         // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
1795         // when spell checking the whole document before sending the message.
1796         // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
1797 
1798         position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent();
1799         if (position.isNull())
1800             return;
1801 
1802         Position rangeCompliantPosition = rangeCompliantEquivalent(position);
1803         spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.deprecatedEditingOffset(), ec);
1804         startedWithSelection = false;   // won't need to wrap
1805     }
1806 
1807     // topNode defines the whole range we want to operate on
1808     Node* topNode = highestEditableRoot(position);
1809     // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
1810     spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec);
1811 
1812     // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
1813     // at a word boundary. Going back by one char and then forward by a word does the trick.
1814     if (startedWithSelection) {
1815         VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
1816         if (oneBeforeStart.isNotNull()) {
1817             setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
1818         } // else we were already at the start of the editable node
1819     }
1820 
1821     if (spellingSearchRange->collapsed(ec))
1822         return;       // nothing to search in
1823 
1824     // Get the spell checker if it is available
1825     if (!client())
1826         return;
1827 
1828     // We go to the end of our first range instead of the start of it, just to be sure
1829     // we don't get foiled by any word boundary problems at the start.  It means we might
1830     // do a tiny bit more searching.
1831     Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
1832     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
1833 
1834     int misspellingOffset = 0;
1835 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1836     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1837     String misspelledWord;
1838     String badGrammarPhrase;
1839     int grammarPhraseOffset = 0;
1840     bool isSpelling = true;
1841     int foundOffset = 0;
1842     GrammarDetail grammarDetail;
1843     String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1844     if (isSpelling) {
1845         misspelledWord = foundItem;
1846         misspellingOffset = foundOffset;
1847     } else {
1848         badGrammarPhrase = foundItem;
1849         grammarPhraseOffset = foundOffset;
1850     }
1851 #else
1852     RefPtr<Range> firstMisspellingRange;
1853     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
1854     String badGrammarPhrase;
1855 
1856 #ifndef BUILDING_ON_TIGER
1857     int grammarPhraseOffset = 0;
1858     GrammarDetail grammarDetail;
1859 
1860     // Search for bad grammar that occurs prior to the next misspelled word (if any)
1861     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
1862     if (!misspelledWord.isEmpty()) {
1863         // Stop looking at start of next misspelled word
1864         CharacterIterator chars(grammarSearchRange.get());
1865         chars.advance(misspellingOffset);
1866         grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1867     }
1868 
1869     if (isGrammarCheckingEnabled())
1870         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1871 #endif
1872 #endif
1873 
1874     // 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
1875     // block rather than at a selection).
1876     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
1877         spellingSearchRange->setStart(topNode, 0, ec);
1878         // going until the end of the very first chunk we tested is far enough
1879         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
1880 
1881 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1882         grammarSearchRange = spellingSearchRange->cloneRange(ec);
1883         foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
1884         if (isSpelling) {
1885             misspelledWord = foundItem;
1886             misspellingOffset = foundOffset;
1887         } else {
1888             badGrammarPhrase = foundItem;
1889             grammarPhraseOffset = foundOffset;
1890         }
1891 #else
1892         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
1893 
1894 #ifndef BUILDING_ON_TIGER
1895         grammarSearchRange = spellingSearchRange->cloneRange(ec);
1896         if (!misspelledWord.isEmpty()) {
1897             // Stop looking at start of next misspelled word
1898             CharacterIterator chars(grammarSearchRange.get());
1899             chars.advance(misspellingOffset);
1900             grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
1901         }
1902         if (isGrammarCheckingEnabled())
1903             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
1904 #endif
1905 #endif
1906     }
1907 
1908     if (!badGrammarPhrase.isEmpty()) {
1909 #ifdef BUILDING_ON_TIGER
1910         ASSERT_NOT_REACHED();
1911 #else
1912         // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
1913         // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
1914         // panel, and store a marker so we draw the green squiggle later.
1915 
1916         ASSERT(badGrammarPhrase.length() > 0);
1917         ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
1918 
1919         // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
1920         RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
1921         frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
1922         frame()->revealSelection();
1923 
1924         client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
1925         frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
1926 #endif
1927     } else if (!misspelledWord.isEmpty()) {
1928         // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
1929         // a marker so we draw the red squiggle later.
1930 
1931         RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
1932         frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
1933         frame()->revealSelection();
1934 
1935         client()->updateSpellingUIWithMisspelledWord(misspelledWord);
1936         frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
1937     }
1938 }
1939 
isSelectionMisspelled()1940 bool Editor::isSelectionMisspelled()
1941 {
1942     String selectedString = frame()->selectedText();
1943     int length = selectedString.length();
1944     if (length == 0)
1945         return false;
1946 
1947     if (!client())
1948         return false;
1949 
1950     int misspellingLocation = -1;
1951     int misspellingLength = 0;
1952     client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength);
1953 
1954     // The selection only counts as misspelled if the selected text is exactly one misspelled word
1955     if (misspellingLength != length)
1956         return false;
1957 
1958     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
1959     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
1960     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
1961     // or a grammar error.
1962     client()->updateSpellingUIWithMisspelledWord(selectedString);
1963 
1964     return true;
1965 }
1966 
1967 #ifndef BUILDING_ON_TIGER
isRangeUngrammatical(EditorClient * client,Range * range,Vector<String> & guessesVector)1968 static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
1969 {
1970     if (!client)
1971         return false;
1972 
1973     ExceptionCode ec;
1974     if (!range || range->collapsed(ec))
1975         return false;
1976 
1977     // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
1978     // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
1979     // or overlapping the range; the ranges must exactly match.
1980     guessesVector.clear();
1981     int grammarPhraseOffset;
1982 
1983     GrammarDetail grammarDetail;
1984     String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);
1985 
1986     // No bad grammar in these parts at all.
1987     if (badGrammarPhrase.isEmpty())
1988         return false;
1989 
1990     // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
1991     if (grammarPhraseOffset > 0)
1992         return false;
1993 
1994     ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
1995 
1996     // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
1997     if (grammarDetail.location + grammarPhraseOffset != 0)
1998         return false;
1999 
2000     // Bad grammar at start of range, but end of bad grammar is before or after end of range
2001     if (grammarDetail.length != TextIterator::rangeLength(range))
2002         return false;
2003 
2004     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
2005     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
2006     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
2007     // or a grammar error.
2008     client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
2009 
2010     return true;
2011 }
2012 #endif
2013 
isSelectionUngrammatical()2014 bool Editor::isSelectionUngrammatical()
2015 {
2016 #ifdef BUILDING_ON_TIGER
2017     return false;
2018 #else
2019     Vector<String> ignoredGuesses;
2020     return isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), ignoredGuesses);
2021 #endif
2022 }
2023 
guessesForUngrammaticalSelection()2024 Vector<String> Editor::guessesForUngrammaticalSelection()
2025 {
2026 #ifdef BUILDING_ON_TIGER
2027     return Vector<String>();
2028 #else
2029     Vector<String> guesses;
2030     // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
2031     isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), guesses);
2032     return guesses;
2033 #endif
2034 }
2035 
guessesForMisspelledSelection()2036 Vector<String> Editor::guessesForMisspelledSelection()
2037 {
2038     String selectedString = frame()->selectedText();
2039     ASSERT(selectedString.length() != 0);
2040 
2041     Vector<String> guesses;
2042     if (client())
2043         client()->getGuessesForWord(selectedString, guesses);
2044     return guesses;
2045 }
2046 
2047 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2048 
guessesForMisspelledOrUngrammaticalRange(EditorClient * client,Range * range,bool checkGrammar,bool & misspelled,bool & ungrammatical)2049 static Vector<String> guessesForMisspelledOrUngrammaticalRange(EditorClient* client, Range *range, bool checkGrammar, bool& misspelled, bool& ungrammatical)
2050 {
2051     Vector<String> guesses;
2052     ExceptionCode ec;
2053     misspelled = false;
2054     ungrammatical = false;
2055 
2056     if (!client || !range || range->collapsed(ec))
2057         return guesses;
2058 
2059     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2060     int rangeStartOffset;
2061     String paragraphString;
2062     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(range, rangeStartOffset, paragraphString);
2063     int rangeLength = TextIterator::rangeLength(range);
2064     if (rangeLength == 0 || paragraphString.length() == 0)
2065         return guesses;
2066 
2067     Vector<TextCheckingResult> results;
2068     uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
2069     client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
2070 
2071     for (unsigned i = 0; i < results.size(); i++) {
2072         const TextCheckingResult* result = &results[i];
2073         if (result->type == TextCheckingTypeSpelling && result->location == rangeStartOffset && result->length == rangeLength) {
2074             String misspelledWord = paragraphString.substring(rangeStartOffset, rangeLength);
2075             ASSERT(misspelledWord.length() != 0);
2076             client->getGuessesForWord(misspelledWord, guesses);
2077             client->updateSpellingUIWithMisspelledWord(misspelledWord);
2078             misspelled = true;
2079             return guesses;
2080         }
2081     }
2082 
2083     if (!checkGrammar)
2084         return guesses;
2085 
2086     for (unsigned i = 0; i < results.size(); i++) {
2087         const TextCheckingResult* result = &results[i];
2088         if (result->type == TextCheckingTypeGrammar && result->location <= rangeStartOffset && result->location + result->length >= rangeStartOffset + rangeLength) {
2089             for (unsigned j = 0; j < result->details.size(); j++) {
2090                 const GrammarDetail* detail = &result->details[j];
2091                 ASSERT(detail->length > 0 && detail->location >= 0);
2092                 if (result->location + detail->location == rangeStartOffset && detail->length == rangeLength) {
2093                     String badGrammarPhrase = paragraphString.substring(result->location, result->length);
2094                     ASSERT(badGrammarPhrase.length() != 0);
2095                     for (unsigned k = 0; k < detail->guesses.size(); k++)
2096                         guesses.append(detail->guesses[k]);
2097                     client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail);
2098                     ungrammatical = true;
2099                     return guesses;
2100                 }
2101             }
2102         }
2103     }
2104     return guesses;
2105 }
2106 
2107 #endif
2108 
guessesForMisspelledOrUngrammaticalSelection(bool & misspelled,bool & ungrammatical)2109 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
2110 {
2111 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2112     return guessesForMisspelledOrUngrammaticalRange(client(), frame()->selection()->toNormalizedRange().get(), isGrammarCheckingEnabled(), misspelled, ungrammatical);
2113 #else
2114     misspelled = isSelectionMisspelled();
2115     if (misspelled) {
2116         ungrammatical = false;
2117         return guessesForMisspelledSelection();
2118     }
2119     if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) {
2120         ungrammatical = true;
2121         return guessesForUngrammaticalSelection();
2122     }
2123     ungrammatical = false;
2124     return Vector<String>();
2125 #endif
2126 }
2127 
showSpellingGuessPanel()2128 void Editor::showSpellingGuessPanel()
2129 {
2130     if (!client()) {
2131         LOG_ERROR("No NSSpellChecker");
2132         return;
2133     }
2134 
2135 #ifndef BUILDING_ON_TIGER
2136     // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
2137     // to match rest of OS X.
2138     if (client()->spellingUIIsShowing()) {
2139         client()->showSpellingUI(false);
2140         return;
2141     }
2142 #endif
2143 
2144     advanceToNextMisspelling(true);
2145     client()->showSpellingUI(true);
2146 }
2147 
spellingPanelIsShowing()2148 bool Editor::spellingPanelIsShowing()
2149 {
2150     if (!client())
2151         return false;
2152     return client()->spellingUIIsShowing();
2153 }
2154 
markMisspellingsAfterTypingToPosition(const VisiblePosition & p)2155 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
2156 {
2157 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2158     bool markSpelling = isContinuousSpellCheckingEnabled();
2159     bool markGrammar = markSpelling && isGrammarCheckingEnabled();
2160     bool performTextCheckingReplacements = isAutomaticQuoteSubstitutionEnabled()
2161                                         || isAutomaticLinkDetectionEnabled()
2162                                         || isAutomaticDashSubstitutionEnabled()
2163                                         || isAutomaticTextReplacementEnabled()
2164                                         || (markSpelling && isAutomaticSpellingCorrectionEnabled());
2165     if (!markSpelling && !performTextCheckingReplacements)
2166         return;
2167 
2168     VisibleSelection adjacentWords = VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary));
2169     if (markGrammar) {
2170         VisibleSelection selectedSentence = VisibleSelection(startOfSentence(p), endOfSentence(p));
2171         markAllMisspellingsAndBadGrammarInRanges(true, adjacentWords.toNormalizedRange().get(), true, selectedSentence.toNormalizedRange().get(), performTextCheckingReplacements);
2172     } else {
2173         markAllMisspellingsAndBadGrammarInRanges(markSpelling, adjacentWords.toNormalizedRange().get(), false, adjacentWords.toNormalizedRange().get(), performTextCheckingReplacements);
2174     }
2175 #else
2176     if (!isContinuousSpellCheckingEnabled())
2177         return;
2178 
2179     // Check spelling of one word
2180     RefPtr<Range> misspellingRange;
2181     markMisspellings(VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)), misspellingRange);
2182 
2183     // Autocorrect the misspelled word.
2184     if (misspellingRange == 0)
2185         return;
2186 
2187     // Get the misspelled word.
2188     const String misspelledWord = plainText(misspellingRange.get());
2189     String autocorrectedString = client()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
2190 
2191     // If autocorrected word is non empty, replace the misspelled word by this word.
2192     if (!autocorrectedString.isEmpty()) {
2193         VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
2194         if (newSelection != frame()->selection()->selection()) {
2195             if (!frame()->shouldChangeSelection(newSelection))
2196                 return;
2197             frame()->selection()->setSelection(newSelection);
2198         }
2199 
2200         if (!frame()->editor()->shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
2201             return;
2202         frame()->editor()->replaceSelectionWithText(autocorrectedString, false, false);
2203 
2204         // Reset the charet one character further.
2205         frame()->selection()->moveTo(frame()->selection()->end());
2206         frame()->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2207     }
2208 
2209     if (!isGrammarCheckingEnabled())
2210         return;
2211 
2212     // Check grammar of entire sentence
2213     markBadGrammar(VisibleSelection(startOfSentence(p), endOfSentence(p)));
2214 #endif
2215 }
2216 
markAllMisspellingsInRange(EditorClient * client,Range * searchRange,RefPtr<Range> & firstMisspellingRange)2217 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange, RefPtr<Range>& firstMisspellingRange)
2218 {
2219     // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
2220     // all we need to do is mark every instance.
2221     int ignoredOffset;
2222     findFirstMisspellingInRange(client, searchRange, ignoredOffset, true, firstMisspellingRange);
2223 }
2224 
2225 #ifndef BUILDING_ON_TIGER
markAllBadGrammarInRange(EditorClient * client,Range * searchRange)2226 static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
2227 {
2228     // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
2229     // do is mark every instance.
2230     GrammarDetail ignoredGrammarDetail;
2231     int ignoredOffset;
2232     findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
2233 }
2234 #endif
2235 
markMisspellingsOrBadGrammar(Editor * editor,const VisibleSelection & selection,bool checkSpelling,RefPtr<Range> & firstMisspellingRange)2236 static void markMisspellingsOrBadGrammar(Editor* editor, const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
2237 {
2238     // This function is called with a selection already expanded to word boundaries.
2239     // Might be nice to assert that here.
2240 
2241     // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
2242     // grammar checking can only be on if spell checking is also on.
2243     if (!editor->isContinuousSpellCheckingEnabled())
2244         return;
2245 
2246     RefPtr<Range> searchRange(selection.toNormalizedRange());
2247     if (!searchRange)
2248         return;
2249 
2250     // If we're not in an editable node, bail.
2251     Node* editableNode = searchRange->startContainer();
2252     if (!editableNode || !editableNode->isContentEditable())
2253         return;
2254 
2255     if (!editor->spellCheckingEnabledInFocusedNode())
2256         return;
2257 
2258     // Get the spell checker if it is available
2259     if (!editor->client())
2260         return;
2261 
2262     if (checkSpelling)
2263         markAllMisspellingsInRange(editor->client(), searchRange.get(), firstMisspellingRange);
2264     else {
2265 #ifdef BUILDING_ON_TIGER
2266         ASSERT_NOT_REACHED();
2267 #else
2268         if (editor->isGrammarCheckingEnabled())
2269             markAllBadGrammarInRange(editor->client(), searchRange.get());
2270 #endif
2271     }
2272 }
2273 
spellCheckingEnabledInFocusedNode() const2274 bool Editor::spellCheckingEnabledInFocusedNode() const
2275 {
2276     // Ascend the DOM tree to find a "spellcheck" attribute.
2277     // When we find a "spellcheck" attribute, retrieve its value and return false if its value is "false".
2278     const Node* node = frame()->document()->focusedNode();
2279     while (node) {
2280         if (node->isElementNode()) {
2281             const WebCore::AtomicString& value = static_cast<const Element*>(node)->getAttribute(spellcheckAttr);
2282             if (equalIgnoringCase(value, "true"))
2283                 return true;
2284             if (equalIgnoringCase(value, "false"))
2285                 return false;
2286         }
2287         node = node->parent();
2288     }
2289     return true;
2290 }
2291 
markMisspellings(const VisibleSelection & selection,RefPtr<Range> & firstMisspellingRange)2292 void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2293 {
2294     markMisspellingsOrBadGrammar(this, selection, true, firstMisspellingRange);
2295 }
2296 
markBadGrammar(const VisibleSelection & selection)2297 void Editor::markBadGrammar(const VisibleSelection& selection)
2298 {
2299 #ifndef BUILDING_ON_TIGER
2300     RefPtr<Range> firstMisspellingRange;
2301     markMisspellingsOrBadGrammar(this, selection, false, firstMisspellingRange);
2302 #else
2303     UNUSED_PARAM(selection);
2304 #endif
2305 }
2306 
2307 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2308 
isAmbiguousBoundaryCharacter(UChar character)2309 static inline bool isAmbiguousBoundaryCharacter(UChar character)
2310 {
2311     // These are characters that can behave as word boundaries, but can appear within words.
2312     // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
2313     // FIXME: this is required until 6853027 is fixed and text checking can do this for us.
2314     return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
2315 }
2316 
markAllMisspellingsAndBadGrammarInRanges(bool markSpelling,Range * spellingRange,bool markGrammar,Range * grammarRange,bool performTextCheckingReplacements)2317 void Editor::markAllMisspellingsAndBadGrammarInRanges(bool markSpelling, Range* spellingRange, bool markGrammar, Range* grammarRange, bool performTextCheckingReplacements)
2318 {
2319     // This function is called with selections already expanded to word boundaries.
2320     ExceptionCode ec = 0;
2321     if (!client() || !spellingRange || (markGrammar && !grammarRange))
2322         return;
2323 
2324     // If we're not in an editable node, bail.
2325     Node* editableNode = spellingRange->startContainer();
2326     if (!editableNode || !editableNode->isContentEditable())
2327         return;
2328 
2329     if (!spellCheckingEnabledInFocusedNode())
2330         return;
2331 
2332     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
2333     int spellingRangeStartOffset = 0;
2334     int spellingRangeEndOffset = 0;
2335     int grammarRangeStartOffset = 0;
2336     int grammarRangeEndOffset = 0;
2337     int offsetDueToReplacement = 0;
2338     int paragraphLength = 0;
2339     int selectionOffset = 0;
2340     int ambiguousBoundaryOffset = -1;
2341     bool selectionChanged = false;
2342     bool restoreSelectionAfterChange = false;
2343     bool adjustSelectionForParagraphBoundaries = false;
2344     String paragraphString;
2345     RefPtr<Range> paragraphRange;
2346 
2347     if (markGrammar) {
2348         // The spelling range should be contained in the paragraph-aligned extension of the grammar range.
2349         paragraphRange = paragraphAlignedRangeForRange(grammarRange, grammarRangeStartOffset, paragraphString);
2350         RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
2351         spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
2352         grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
2353     } else {
2354         paragraphRange = paragraphAlignedRangeForRange(spellingRange, spellingRangeStartOffset, paragraphString);
2355     }
2356     spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
2357     paragraphLength = paragraphString.length();
2358     if (paragraphLength <= 0 || (spellingRangeStartOffset >= spellingRangeEndOffset && (!markGrammar || grammarRangeStartOffset >= grammarRangeEndOffset)))
2359         return;
2360 
2361     if (performTextCheckingReplacements) {
2362         if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) {
2363             // Attempt to save the caret position so we can restore it later if needed
2364             RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), paragraphRange->startPosition());
2365             Position caretPosition = m_frame->selection()->end();
2366             offsetAsRange->setEnd(caretPosition.containerNode(), caretPosition.computeOffsetInContainerNode(), ec);
2367             if (!ec) {
2368                 selectionOffset = TextIterator::rangeLength(offsetAsRange.get());
2369                 restoreSelectionAfterChange = true;
2370                 if (selectionOffset > 0 && (selectionOffset > paragraphLength || paragraphString[selectionOffset - 1] == newlineCharacter))
2371                     adjustSelectionForParagraphBoundaries = true;
2372                 if (selectionOffset > 0 && selectionOffset <= paragraphLength && isAmbiguousBoundaryCharacter(paragraphString[selectionOffset - 1]))
2373                     ambiguousBoundaryOffset = selectionOffset - 1;
2374             }
2375         }
2376     }
2377 
2378     Vector<TextCheckingResult> results;
2379     uint64_t checkingTypes = 0;
2380     if (markSpelling)
2381         checkingTypes |= TextCheckingTypeSpelling;
2382     if (markGrammar)
2383         checkingTypes |= TextCheckingTypeGrammar;
2384     if (performTextCheckingReplacements) {
2385         if (isAutomaticLinkDetectionEnabled())
2386             checkingTypes |= TextCheckingTypeLink;
2387         if (isAutomaticQuoteSubstitutionEnabled())
2388             checkingTypes |= TextCheckingTypeQuote;
2389         if (isAutomaticDashSubstitutionEnabled())
2390             checkingTypes |= TextCheckingTypeDash;
2391         if (isAutomaticTextReplacementEnabled())
2392             checkingTypes |= TextCheckingTypeReplacement;
2393         if (markSpelling && isAutomaticSpellingCorrectionEnabled())
2394             checkingTypes |= TextCheckingTypeCorrection;
2395     }
2396     client()->checkTextOfParagraph(paragraphString.characters(), paragraphLength, checkingTypes, results);
2397 
2398     for (unsigned i = 0; i < results.size(); i++) {
2399         const TextCheckingResult* result = &results[i];
2400         int resultLocation = result->location + offsetDueToReplacement;
2401         int resultLength = result->length;
2402         if (markSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= spellingRangeStartOffset && resultLocation + resultLength <= spellingRangeEndOffset) {
2403             ASSERT(resultLength > 0 && resultLocation >= 0);
2404             RefPtr<Range> misspellingRange = TextIterator::subrange(spellingRange, resultLocation - spellingRangeStartOffset, resultLength);
2405             misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2406         } else if (markGrammar && result->type == TextCheckingTypeGrammar && resultLocation < grammarRangeEndOffset && resultLocation + resultLength > grammarRangeStartOffset) {
2407             ASSERT(resultLength > 0 && resultLocation >= 0);
2408             for (unsigned j = 0; j < result->details.size(); j++) {
2409                 const GrammarDetail* detail = &result->details[j];
2410                 ASSERT(detail->length > 0 && detail->location >= 0);
2411                 if (resultLocation + detail->location >= grammarRangeStartOffset && resultLocation + detail->location + detail->length <= grammarRangeEndOffset) {
2412                     RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarRange, resultLocation + detail->location - grammarRangeStartOffset, detail->length);
2413                     grammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
2414                 }
2415             }
2416         } else if (performTextCheckingReplacements && resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingRangeStartOffset &&
2417                     (result->type == TextCheckingTypeLink
2418                     || result->type == TextCheckingTypeQuote
2419                     || result->type == TextCheckingTypeDash
2420                     || result->type == TextCheckingTypeReplacement
2421                     || result->type == TextCheckingTypeCorrection)) {
2422             // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
2423             ASSERT(resultLength > 0 && resultLocation >= 0);
2424             int replacementLength = result->replacement.length();
2425             bool doReplacement = (replacementLength > 0);
2426             RefPtr<Range> rangeToReplace = TextIterator::subrange(paragraphRange.get(), resultLocation, resultLength);
2427             VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM);
2428 
2429             // avoid correcting text after an ambiguous boundary character has been typed
2430             // FIXME: this is required until 6853027 is fixed and text checking can do this for us
2431             if (ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset)
2432                 doReplacement = false;
2433 
2434             // adding links should be done only immediately after they are typed
2435             if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1)
2436                 doReplacement = false;
2437 
2438             // Don't correct spelling in an already-corrected word.
2439             if (doReplacement && result->type == TextCheckingTypeCorrection) {
2440                 Node* node = rangeToReplace->startContainer();
2441                 int startOffset = rangeToReplace->startOffset();
2442                 int endOffset = startOffset + replacementLength;
2443                 Vector<DocumentMarker> markers = node->document()->markersForNode(node);
2444                 size_t markerCount = markers.size();
2445                 for (size_t i = 0; i < markerCount; ++i) {
2446                     const DocumentMarker& marker = markers[i];
2447                     if (marker.type == DocumentMarker::Replacement && static_cast<int>(marker.startOffset) < endOffset && static_cast<int>(marker.endOffset) > startOffset) {
2448                         doReplacement = false;
2449                         break;
2450                     }
2451                     if (static_cast<int>(marker.startOffset) >= endOffset)
2452                         break;
2453                 }
2454             }
2455             if (doReplacement && selectionToReplace != m_frame->selection()->selection()) {
2456                 if (m_frame->shouldChangeSelection(selectionToReplace)) {
2457                     m_frame->selection()->setSelection(selectionToReplace);
2458                     selectionChanged = true;
2459                 } else {
2460                     doReplacement = false;
2461                 }
2462             }
2463             if (doReplacement) {
2464                 if (result->type == TextCheckingTypeLink) {
2465                     restoreSelectionAfterChange = false;
2466                     if (canEditRichly())
2467                         applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
2468                 } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
2469                     String replacedString;
2470                     if (result->type == TextCheckingTypeCorrection)
2471                         replacedString = plainText(rangeToReplace.get());
2472                     replaceSelectionWithText(result->replacement, false, false);
2473                     spellingRangeEndOffset += replacementLength - resultLength;
2474                     offsetDueToReplacement += replacementLength - resultLength;
2475                     if (resultLocation < selectionOffset)
2476                         selectionOffset += replacementLength - resultLength;
2477                     if (result->type == TextCheckingTypeCorrection) {
2478                         // Add a marker so that corrections can easily be undone and won't be re-corrected.
2479                         RefPtr<Range> replacedRange = TextIterator::subrange(paragraphRange.get(), resultLocation, replacementLength);
2480                         replacedRange->startContainer()->document()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString);
2481                     }
2482                 }
2483             }
2484         }
2485     }
2486 
2487     if (selectionChanged) {
2488         // Restore the caret position if we have made any replacements
2489         setEnd(paragraphRange.get(), endOfParagraph(startOfNextParagraph(paragraphRange->startPosition())));
2490         int newLength = TextIterator::rangeLength(paragraphRange.get());
2491         if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= newLength) {
2492             RefPtr<Range> selectionRange = TextIterator::subrange(paragraphRange.get(), 0, selectionOffset);
2493             m_frame->selection()->moveTo(selectionRange->endPosition(), DOWNSTREAM);
2494             if (adjustSelectionForParagraphBoundaries)
2495                 m_frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2496         } else {
2497             // If this fails for any reason, the fallback is to go one position beyond the last replacement
2498             m_frame->selection()->moveTo(m_frame->selection()->end());
2499             m_frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity);
2500         }
2501     }
2502 }
2503 
changeBackToReplacedString(const String & replacedString)2504 void Editor::changeBackToReplacedString(const String& replacedString)
2505 {
2506     if (replacedString.isEmpty())
2507         return;
2508 
2509     RefPtr<Range> selection = selectedRange();
2510     if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
2511         return;
2512 
2513     String paragraphString;
2514     int selectionOffset;
2515     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(selection.get(), selectionOffset, paragraphString);
2516     replaceSelectionWithText(replacedString, false, false);
2517     RefPtr<Range> changedRange = TextIterator::subrange(paragraphRange.get(), selectionOffset, replacedString.length());
2518     changedRange->startContainer()->document()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
2519 }
2520 
2521 #endif
2522 
markMisspellingsAndBadGrammar(const VisibleSelection & spellingSelection,bool markGrammar,const VisibleSelection & grammarSelection)2523 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
2524 {
2525 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2526     if (!isContinuousSpellCheckingEnabled())
2527         return;
2528     markAllMisspellingsAndBadGrammarInRanges(true, spellingSelection.toNormalizedRange().get(), markGrammar && isGrammarCheckingEnabled(), grammarSelection.toNormalizedRange().get(), false);
2529 #else
2530     RefPtr<Range> firstMisspellingRange;
2531     markMisspellings(spellingSelection, firstMisspellingRange);
2532     if (markGrammar)
2533         markBadGrammar(grammarSelection);
2534 #endif
2535 }
2536 
rangeForPoint(const IntPoint & windowPoint)2537 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
2538 {
2539     Document* document = m_frame->documentAtPoint(windowPoint);
2540     if (!document)
2541         return 0;
2542 
2543     Frame* frame = document->frame();
2544     ASSERT(frame);
2545     FrameView* frameView = frame->view();
2546     if (!frameView)
2547         return 0;
2548     IntPoint framePoint = frameView->windowToContents(windowPoint);
2549     VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
2550     return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
2551 }
2552 
revealSelectionAfterEditingOperation()2553 void Editor::revealSelectionAfterEditingOperation()
2554 {
2555     if (m_ignoreCompositionSelectionChange)
2556         return;
2557 
2558     m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2559 }
2560 
setIgnoreCompositionSelectionChange(bool ignore)2561 void Editor::setIgnoreCompositionSelectionChange(bool ignore)
2562 {
2563     if (m_ignoreCompositionSelectionChange == ignore)
2564         return;
2565 
2566     m_ignoreCompositionSelectionChange = ignore;
2567     if (!ignore)
2568         revealSelectionAfterEditingOperation();
2569 }
2570 
compositionRange() const2571 PassRefPtr<Range> Editor::compositionRange() const
2572 {
2573     if (!m_compositionNode)
2574         return 0;
2575     unsigned length = m_compositionNode->length();
2576     unsigned start = min(m_compositionStart, length);
2577     unsigned end = min(max(start, m_compositionEnd), length);
2578     if (start >= end)
2579         return 0;
2580     return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
2581 }
2582 
getCompositionSelection(unsigned & selectionStart,unsigned & selectionEnd) const2583 bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const
2584 {
2585     if (!m_compositionNode)
2586         return false;
2587     Position start = m_frame->selection()->start();
2588     if (start.node() != m_compositionNode)
2589         return false;
2590     Position end = m_frame->selection()->end();
2591     if (end.node() != m_compositionNode)
2592         return false;
2593 
2594     if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart)
2595         return false;
2596     if (static_cast<unsigned>(end.deprecatedEditingOffset()) > m_compositionEnd)
2597         return false;
2598 
2599     selectionStart = start.deprecatedEditingOffset() - m_compositionStart;
2600     selectionEnd = start.deprecatedEditingOffset() - m_compositionEnd;
2601     return true;
2602 }
2603 
transpose()2604 void Editor::transpose()
2605 {
2606     if (!canEdit())
2607         return;
2608 
2609      VisibleSelection selection = m_frame->selection()->selection();
2610      if (!selection.isCaret())
2611          return;
2612 
2613     // Make a selection that goes back one character and forward two characters.
2614     VisiblePosition caret = selection.visibleStart();
2615     VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next();
2616     VisiblePosition previous = next.previous();
2617     if (next == previous)
2618         return;
2619     previous = previous.previous();
2620     if (!inSameParagraph(next, previous))
2621         return;
2622     RefPtr<Range> range = makeRange(previous, next);
2623     if (!range)
2624         return;
2625     VisibleSelection newSelection(range.get(), DOWNSTREAM);
2626 
2627     // Transpose the two characters.
2628     String text = plainText(range.get());
2629     if (text.length() != 2)
2630         return;
2631     String transposed = text.right(1) + text.left(1);
2632 
2633     // Select the two characters.
2634     if (newSelection != m_frame->selection()->selection()) {
2635         if (!m_frame->shouldChangeSelection(newSelection))
2636             return;
2637         m_frame->selection()->setSelection(newSelection);
2638     }
2639 
2640     // Insert the transposed characters.
2641     if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
2642         return;
2643     replaceSelectionWithText(transposed, false, false);
2644 }
2645 
addToKillRing(Range * range,bool prepend)2646 void Editor::addToKillRing(Range* range, bool prepend)
2647 {
2648     if (m_shouldStartNewKillRingSequence)
2649         startNewKillRingSequence();
2650 
2651     String text = m_frame->displayStringModifiedByEncoding(plainText(range));
2652     if (prepend)
2653         prependToKillRing(text);
2654     else
2655         appendToKillRing(text);
2656     m_shouldStartNewKillRingSequence = false;
2657 }
2658 
2659 #if !PLATFORM(MAC)
2660 
appendToKillRing(const String &)2661 void Editor::appendToKillRing(const String&)
2662 {
2663 }
2664 
prependToKillRing(const String &)2665 void Editor::prependToKillRing(const String&)
2666 {
2667 }
2668 
yankFromKillRing()2669 String Editor::yankFromKillRing()
2670 {
2671     return String();
2672 }
2673 
startNewKillRingSequence()2674 void Editor::startNewKillRingSequence()
2675 {
2676 }
2677 
setKillRingToYankedState()2678 void Editor::setKillRingToYankedState()
2679 {
2680 }
2681 
2682 #endif
2683 
insideVisibleArea(const IntPoint & point) const2684 bool Editor::insideVisibleArea(const IntPoint& point) const
2685 {
2686     if (m_frame->excludeFromTextSearch())
2687         return false;
2688 
2689     // Right now, we only check the visibility of a point for disconnected frames. For all other
2690     // frames, we assume visibility.
2691     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2692     if (!frame->isDisconnected())
2693         return true;
2694 
2695     RenderPart* renderer = frame->ownerRenderer();
2696     RenderBlock* container = renderer->containingBlock();
2697     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2698         return true;
2699 
2700     IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2701     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2702                                     rectInPageCoords.width(), rectInPageCoords.height());
2703 
2704     return rectInFrameCoords.contains(point);
2705 }
2706 
insideVisibleArea(Range * range) const2707 bool Editor::insideVisibleArea(Range* range) const
2708 {
2709     if (!range)
2710         return true;
2711 
2712     if (m_frame->excludeFromTextSearch())
2713         return false;
2714 
2715     // Right now, we only check the visibility of a range for disconnected frames. For all other
2716     // frames, we assume visibility.
2717     Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true);
2718     if (!frame->isDisconnected())
2719         return true;
2720 
2721     RenderPart* renderer = frame->ownerRenderer();
2722     RenderBlock* container = renderer->containingBlock();
2723     if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
2724         return true;
2725 
2726     IntRect rectInPageCoords = container->overflowClipRect(0, 0);
2727     IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
2728                                     rectInPageCoords.width(), rectInPageCoords.height());
2729     IntRect resultRect = range->boundingBox();
2730 
2731     return rectInFrameCoords.contains(resultRect);
2732 }
2733 
firstVisibleRange(const String & target,bool caseFlag)2734 PassRefPtr<Range> Editor::firstVisibleRange(const String& target, bool caseFlag)
2735 {
2736     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2737     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2738     ExceptionCode ec = 0;
2739 
2740     while (!insideVisibleArea(resultRange.get())) {
2741         searchRange->setStartAfter(resultRange->endContainer(), ec);
2742         if (searchRange->startContainer() == searchRange->endContainer())
2743             return Range::create(m_frame->document());
2744         resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
2745     }
2746 
2747     return resultRange;
2748 }
2749 
lastVisibleRange(const String & target,bool caseFlag)2750 PassRefPtr<Range> Editor::lastVisibleRange(const String& target, bool caseFlag)
2751 {
2752     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2753     RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2754     ExceptionCode ec = 0;
2755 
2756     while (!insideVisibleArea(resultRange.get())) {
2757         searchRange->setEndBefore(resultRange->startContainer(), ec);
2758         if (searchRange->startContainer() == searchRange->endContainer())
2759             return Range::create(m_frame->document());
2760         resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
2761     }
2762 
2763     return resultRange;
2764 }
2765 
nextVisibleRange(Range * currentRange,const String & target,bool forward,bool caseFlag,bool wrapFlag)2766 PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, bool forward, bool caseFlag, bool wrapFlag)
2767 {
2768     if (m_frame->excludeFromTextSearch())
2769         return Range::create(m_frame->document());
2770 
2771     RefPtr<Range> resultRange = currentRange;
2772     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
2773     ExceptionCode ec = 0;
2774 
2775     for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, forward, caseFlag)) {
2776         if (resultRange->collapsed(ec)) {
2777             if (!resultRange->startContainer()->isInShadowTree())
2778                 break;
2779             searchRange = rangeOfContents(m_frame->document());
2780             if (forward)
2781                 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
2782             else
2783                 searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec);
2784             continue;
2785         }
2786 
2787         if (forward)
2788             searchRange->setStartAfter(resultRange->endContainer(), ec);
2789         else
2790             searchRange->setEndBefore(resultRange->startContainer(), ec);
2791 
2792         Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
2793         if (searchRange->collapsed(ec) && shadowTreeRoot) {
2794             if (forward)
2795                 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
2796             else
2797                 searchRange->setStartBefore(shadowTreeRoot, ec);
2798         }
2799 
2800         if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode())
2801             break;
2802     }
2803 
2804     if (insideVisibleArea(resultRange.get()))
2805         return resultRange;
2806 
2807     if (!wrapFlag)
2808         return Range::create(m_frame->document());
2809 
2810     if (forward)
2811         return firstVisibleRange(target, caseFlag);
2812 
2813     return lastVisibleRange(target, caseFlag);
2814 }
2815 
changeSelectionAfterCommand(const VisibleSelection & newSelection,bool closeTyping,bool clearTypingStyle,EditCommand * cmd)2816 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle, EditCommand* cmd)
2817 {
2818     // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
2819     // because there is work that it must do in this situation.
2820     // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
2821     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
2822     bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection();
2823     if (selectionDidNotChangeDOMPosition || m_frame->shouldChangeSelection(newSelection))
2824         m_frame->selection()->setSelection(newSelection, closeTyping, clearTypingStyle);
2825 
2826     // Some kinds of deletes and line break insertions change the selection's position within the document without
2827     // changing its position within the DOM.  For example when you press return in the following (the caret is marked by ^):
2828     // <div contentEditable="true"><div>^Hello</div></div>
2829     // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
2830     // change the caret's DOM position (["hello", 0]).  In these situations the above SelectionController::setSelection call
2831     // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
2832     // starts a new kill ring sequence, but we want to do these things (matches AppKit).
2833     if (selectionDidNotChangeDOMPosition && cmd->isTypingCommand())
2834         client()->respondToChangedSelection();
2835 }
2836 
2837 } // namespace WebCore
2838