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
74namespace WebCore {
75
76using namespace std;
77using 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.
81VisibleSelection 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
99EditorClient* Editor::client() const
100{
101    if (Page* page = m_frame->page())
102        return page->editorClient();
103    return 0;
104}
105
106void Editor::handleKeyboardEvent(KeyboardEvent* event)
107{
108    if (EditorClient* c = client())
109        c->handleKeyboardEvent(event);
110}
111
112void Editor::handleInputMethodKeydown(KeyboardEvent* event)
113{
114    if (EditorClient* c = client())
115        c->handleInputMethodKeydown(event);
116}
117
118bool Editor::canEdit() const
119{
120    return m_frame->selection()->isContentEditable();
121}
122
123bool 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
133bool Editor::canDHTMLCut()
134{
135    return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
136}
137
138bool Editor::canDHTMLCopy()
139{
140    return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
141}
142
143bool Editor::canDHTMLPaste()
144{
145    return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
146}
147
148bool Editor::canCut() const
149{
150    return canCopy() && canDelete();
151}
152
153static 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
172bool 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
180bool Editor::canPaste() const
181{
182    return canEdit();
183}
184
185bool Editor::canDelete() const
186{
187    SelectionController* selection = m_frame->selection();
188    return selection->isRange() && selection->isContentEditable();
189}
190
191bool 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
212bool Editor::smartInsertDeleteEnabled()
213{
214    return client() && client()->smartInsertDeleteEnabled();
215}
216
217bool Editor::canSmartCopyOrDelete()
218{
219    return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity;
220}
221
222bool Editor::isSelectTrailingWhitespaceEnabled()
223{
224    return client() && client()->isSelectTrailingWhitespaceEnabled();
225}
226
227bool 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
265void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
266{
267    if (m_frame->selection()->isNone())
268        return;
269
270    applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
271}
272
273void 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
280void 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
289bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
290{
291    return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace();
292}
293
294bool 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
306void 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
315void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
316{
317    replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true);
318}
319
320PassRefPtr<Range> Editor::selectedRange()
321{
322    if (!m_frame)
323        return 0;
324    return m_frame->selection()->toNormalizedRange();
325}
326
327bool 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
339bool 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
352bool 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
365bool Editor::tryDHTMLPaste()
366{
367    return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
368}
369
370void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard)
371{
372    pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame);
373}
374
375bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
376{
377    return client() && client()->shouldInsertText(text, range, action);
378}
379
380bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
381{
382    return client() && client()->shouldShowDeleteInterface(element);
383}
384
385void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
386{
387    if (client())
388        client()->respondToChangedSelection();
389    m_deleteButtonController->respondToChangedSelection(oldSelection);
390}
391
392void 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
404const 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
455WritingDirection 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
556bool 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
584TriState 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
599TriState 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
614PassRefPtr<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
624PassRefPtr<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
634bool Editor::canIncreaseSelectionListLevel()
635{
636    return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document());
637}
638
639bool Editor::canDecreaseSelectionListLevel()
640{
641    return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document());
642}
643
644PassRefPtr<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
654PassRefPtr<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
664PassRefPtr<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
674void Editor::decreaseSelectionListLevel()
675{
676    if (!canEditRichly() || m_frame->selection()->isNone())
677        return;
678
679    DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document());
680    revealSelectionAfterEditingOperation();
681}
682
683void Editor::removeFormattingAndStyle()
684{
685    applyCommand(RemoveFormatCommand::create(m_frame->document()));
686}
687
688void 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
695bool 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
717void 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
733bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
734{
735    return client()->shouldApplyStyle(style, range);
736}
737
738void 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
752void 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
761void 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
770bool Editor::clientIsEditable() const
771{
772    return client() && client()->isEditable();
773}
774
775// CSS properties that only has a visual difference when applied to text.
776static const int textOnlyProperties[] = {
777    CSSPropertyTextDecoration,
778    CSSPropertyWebkitTextDecorationsInEffect,
779    CSSPropertyFontStyle,
780    CSSPropertyFontWeight,
781    CSSPropertyColor,
782};
783
784static 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
798bool 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
813TriState 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}
847void Editor::indent()
848{
849    applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent));
850}
851
852void Editor::outdent()
853{
854    applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent));
855}
856
857static 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
868void 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
892void 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
905void 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
918Editor::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
928Editor::~Editor()
929{
930}
931
932void Editor::clear()
933{
934    m_compositionNode = 0;
935    m_customCompositionUnderlines.clear();
936    m_shouldStyleWithCSS = false;
937}
938
939bool Editor::insertText(const String& text, Event* triggeringEvent)
940{
941    return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent);
942}
943
944bool 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
978bool 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
991bool 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
1007void 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
1026void 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
1050void 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
1068void Editor::pasteAsPlainText()
1069{
1070    if (tryDHTMLPaste())
1071        return;
1072    if (!canPaste())
1073        return;
1074    pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard());
1075}
1076
1077void 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
1092void Editor::copyURL(const KURL& url, const String& title)
1093{
1094    Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
1095}
1096
1097void 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
1106bool Editor::isContinuousSpellCheckingEnabled()
1107{
1108    return client() && client()->isContinuousSpellCheckingEnabled();
1109}
1110
1111void Editor::toggleContinuousSpellChecking()
1112{
1113    if (client())
1114        client()->toggleContinuousSpellChecking();
1115}
1116
1117bool Editor::isGrammarCheckingEnabled()
1118{
1119    return client() && client()->isGrammarCheckingEnabled();
1120}
1121
1122void Editor::toggleGrammarChecking()
1123{
1124    if (client())
1125        client()->toggleGrammarChecking();
1126}
1127
1128int Editor::spellCheckerDocumentTag()
1129{
1130    return client() ? client()->spellCheckerDocumentTag() : 0;
1131}
1132
1133#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1134
1135void Editor::uppercaseWord()
1136{
1137    if (client())
1138        client()->uppercaseWord();
1139}
1140
1141void Editor::lowercaseWord()
1142{
1143    if (client())
1144        client()->lowercaseWord();
1145}
1146
1147void Editor::capitalizeWord()
1148{
1149    if (client())
1150        client()->capitalizeWord();
1151}
1152
1153void 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
1167bool Editor::substitutionsPanelIsShowing()
1168{
1169    if (!client())
1170        return false;
1171    return client()->substitutionsPanelIsShowing();
1172}
1173
1174void Editor::toggleSmartInsertDelete()
1175{
1176    if (client())
1177        client()->toggleSmartInsertDelete();
1178}
1179
1180bool Editor::isAutomaticQuoteSubstitutionEnabled()
1181{
1182    return client() && client()->isAutomaticQuoteSubstitutionEnabled();
1183}
1184
1185void Editor::toggleAutomaticQuoteSubstitution()
1186{
1187    if (client())
1188        client()->toggleAutomaticQuoteSubstitution();
1189}
1190
1191bool Editor::isAutomaticLinkDetectionEnabled()
1192{
1193    return client() && client()->isAutomaticLinkDetectionEnabled();
1194}
1195
1196void Editor::toggleAutomaticLinkDetection()
1197{
1198    if (client())
1199        client()->toggleAutomaticLinkDetection();
1200}
1201
1202bool Editor::isAutomaticDashSubstitutionEnabled()
1203{
1204    return client() && client()->isAutomaticDashSubstitutionEnabled();
1205}
1206
1207void Editor::toggleAutomaticDashSubstitution()
1208{
1209    if (client())
1210        client()->toggleAutomaticDashSubstitution();
1211}
1212
1213bool Editor::isAutomaticTextReplacementEnabled()
1214{
1215    return client() && client()->isAutomaticTextReplacementEnabled();
1216}
1217
1218void Editor::toggleAutomaticTextReplacement()
1219{
1220    if (client())
1221        client()->toggleAutomaticTextReplacement();
1222}
1223
1224bool Editor::isAutomaticSpellingCorrectionEnabled()
1225{
1226    return client() && client()->isAutomaticSpellingCorrectionEnabled();
1227}
1228
1229void Editor::toggleAutomaticSpellingCorrection()
1230{
1231    if (client())
1232        client()->toggleAutomaticSpellingCorrection();
1233}
1234
1235#endif
1236
1237bool Editor::shouldEndEditing(Range* range)
1238{
1239    return client() && client()->shouldEndEditing(range);
1240}
1241
1242bool Editor::shouldBeginEditing(Range* range)
1243{
1244    return client() && client()->shouldBeginEditing(range);
1245}
1246
1247void Editor::clearUndoRedoOperations()
1248{
1249    if (client())
1250        client()->clearUndoRedoOperations();
1251}
1252
1253bool Editor::canUndo()
1254{
1255    return client() && client()->canUndo();
1256}
1257
1258void Editor::undo()
1259{
1260    if (client())
1261        client()->undo();
1262}
1263
1264bool Editor::canRedo()
1265{
1266    return client() && client()->canRedo();
1267}
1268
1269void Editor::redo()
1270{
1271    if (client())
1272        client()->redo();
1273}
1274
1275void Editor::didBeginEditing()
1276{
1277    if (client())
1278        client()->didBeginEditing();
1279}
1280
1281void Editor::didEndEditing()
1282{
1283    if (client())
1284        client()->didEndEditing();
1285}
1286
1287void Editor::didWriteSelectionToPasteboard()
1288{
1289    if (client())
1290        client()->didWriteSelectionToPasteboard();
1291}
1292
1293void Editor::toggleBold()
1294{
1295    command("ToggleBold").execute();
1296}
1297
1298void Editor::toggleUnderline()
1299{
1300    command("ToggleUnderline").execute();
1301}
1302
1303void 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
1321void 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
1334void Editor::confirmComposition()
1335{
1336    if (!m_compositionNode)
1337        return;
1338    confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false);
1339}
1340
1341void Editor::confirmCompositionWithoutDisturbingSelection()
1342{
1343    if (!m_compositionNode)
1344        return;
1345    confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true);
1346}
1347
1348void Editor::confirmComposition(const String& text)
1349{
1350    confirmComposition(text, false);
1351}
1352
1353void 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
1395void 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
1476void 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
1490void 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
1503static 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
1567static 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
1589static 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
1626static 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
1698static 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
1822void 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
1995bool 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
2023static 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
2069bool 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
2079Vector<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
2091Vector<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
2104static 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
2164Vector<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
2183void 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
2203bool Editor::spellingPanelIsShowing()
2204{
2205    if (!client())
2206        return false;
2207    return client()->spellingUIIsShowing();
2208}
2209
2210void 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
2272static 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
2281static 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
2291static 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
2329bool 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
2347void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
2348{
2349    markMisspellingsOrBadGrammar(this, selection, true, firstMisspellingRange);
2350}
2351
2352void 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
2364static 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
2372void 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
2559void 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
2578void 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
2592PassRefPtr<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
2608void Editor::revealSelectionAfterEditingOperation()
2609{
2610    if (m_ignoreCompositionSelectionChange)
2611        return;
2612
2613    m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2614}
2615
2616void Editor::setIgnoreCompositionSelectionChange(bool ignore)
2617{
2618    if (m_ignoreCompositionSelectionChange == ignore)
2619        return;
2620
2621    m_ignoreCompositionSelectionChange = ignore;
2622    if (!ignore)
2623        revealSelectionAfterEditingOperation();
2624}
2625
2626PassRefPtr<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
2638bool 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
2659void 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
2701void 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
2716void Editor::appendToKillRing(const String&)
2717{
2718}
2719
2720void Editor::prependToKillRing(const String&)
2721{
2722}
2723
2724String Editor::yankFromKillRing()
2725{
2726    return String();
2727}
2728
2729void Editor::startNewKillRingSequence()
2730{
2731}
2732
2733void Editor::setKillRingToYankedState()
2734{
2735}
2736
2737#endif
2738
2739bool 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
2765bool 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
2795PassRefPtr<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
2811PassRefPtr<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
2827PassRefPtr<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
2877void 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