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