Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2006, 2007, 2008, 2011 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 "core/editing/SpellChecker.h"
     29 
     30 #include "HTMLNames.h"
     31 #include "core/dom/Document.h"
     32 #include "core/dom/DocumentMarkerController.h"
     33 #include "core/dom/Element.h"
     34 #include "core/dom/NodeTraversal.h"
     35 #include "core/editing/Editor.h"
     36 #include "core/editing/SpellCheckRequester.h"
     37 #include "core/editing/TextCheckingHelper.h"
     38 #include "core/editing/VisibleUnits.h"
     39 #include "core/editing/htmlediting.h"
     40 #include "core/frame/Frame.h"
     41 #include "core/html/HTMLInputElement.h"
     42 #include "core/loader/EmptyClients.h"
     43 #include "core/page/Page.h"
     44 #include "core/frame/Settings.h"
     45 #include "core/page/SpellCheckerClient.h"
     46 #include "core/rendering/RenderTextControl.h"
     47 #include "platform/text/TextCheckerClient.h"
     48 
     49 namespace WebCore {
     50 
     51 using namespace HTMLNames;
     52 
     53 namespace {
     54 
     55 bool isSelectionInTextField(const VisibleSelection& selection)
     56 {
     57     HTMLTextFormControlElement* textControl = enclosingTextFormControl(selection.start());
     58     return textControl && textControl->hasTagName(inputTag) && toHTMLInputElement(textControl)->isTextField();
     59 }
     60 
     61 } // namespace
     62 
     63 PassOwnPtr<SpellChecker> SpellChecker::create(Frame& frame)
     64 {
     65     return adoptPtr(new SpellChecker(frame));
     66 }
     67 
     68 static SpellCheckerClient& emptySpellCheckerClient()
     69 {
     70     DEFINE_STATIC_LOCAL(EmptySpellCheckerClient, client, ());
     71     return client;
     72 }
     73 
     74 SpellCheckerClient& SpellChecker::spellCheckerClient() const
     75 {
     76     if (Page* page = m_frame.page())
     77         return page->spellCheckerClient();
     78     return emptySpellCheckerClient();
     79 }
     80 
     81 TextCheckerClient& SpellChecker::textChecker() const
     82 {
     83     return spellCheckerClient().textChecker();
     84 }
     85 
     86 SpellChecker::SpellChecker(Frame& frame)
     87     : m_frame(frame)
     88     , m_spellCheckRequester(adoptPtr(new SpellCheckRequester(frame)))
     89 {
     90 }
     91 
     92 SpellChecker::~SpellChecker()
     93 {
     94 }
     95 
     96 bool SpellChecker::isContinuousSpellCheckingEnabled() const
     97 {
     98     return spellCheckerClient().isContinuousSpellCheckingEnabled();
     99 }
    100 
    101 void SpellChecker::toggleContinuousSpellChecking()
    102 {
    103     spellCheckerClient().toggleContinuousSpellChecking();
    104     if (isContinuousSpellCheckingEnabled())
    105         return;
    106     for (Frame* frame = m_frame.page()->mainFrame(); frame && frame->document(); frame = frame->tree().traverseNext()) {
    107         for (Node* node = frame->document()->rootNode(); node; node = NodeTraversal::next(*node)) {
    108             node->setAlreadySpellChecked(false);
    109         }
    110     }
    111 }
    112 
    113 bool SpellChecker::isGrammarCheckingEnabled()
    114 {
    115     return spellCheckerClient().isGrammarCheckingEnabled();
    116 }
    117 
    118 void SpellChecker::didBeginEditing(Element* element)
    119 {
    120     if (isContinuousSpellCheckingEnabled() && unifiedTextCheckerEnabled()) {
    121         bool isTextField = false;
    122         HTMLTextFormControlElement* enclosingHTMLTextFormControlElement = 0;
    123         if (!isHTMLTextFormControlElement(element))
    124             enclosingHTMLTextFormControlElement = enclosingTextFormControl(firstPositionInNode(element));
    125         element = enclosingHTMLTextFormControlElement ? enclosingHTMLTextFormControlElement : element;
    126         Element* parent = element;
    127         if (isHTMLTextFormControlElement(element)) {
    128             HTMLTextFormControlElement* textControl = toHTMLTextFormControlElement(element);
    129             parent = textControl;
    130             element = textControl->innerTextElement();
    131             isTextField = textControl->hasTagName(inputTag) && toHTMLInputElement(textControl)->isTextField();
    132         }
    133 
    134         if (isTextField || !parent->isAlreadySpellChecked()) {
    135             // We always recheck textfields because markers are removed from them on blur.
    136             VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(element);
    137             markMisspellingsAndBadGrammar(selection);
    138             if (!isTextField)
    139                 parent->setAlreadySpellChecked(true);
    140         }
    141     }
    142 }
    143 
    144 void SpellChecker::ignoreSpelling()
    145 {
    146     if (RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange())
    147         m_frame.document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
    148 }
    149 
    150 void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection)
    151 {
    152     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
    153     // then we wrap and search from the doc start to (approximately) where we started.
    154 
    155     // Start at the end of the selection, search to edge of document. Starting at the selection end makes
    156     // repeated "check spelling" commands work.
    157     VisibleSelection selection(m_frame.selection().selection());
    158     RefPtr<Range> spellingSearchRange(rangeOfContents(m_frame.document()));
    159 
    160     bool startedWithSelection = false;
    161     if (selection.start().deprecatedNode()) {
    162         startedWithSelection = true;
    163         if (startBeforeSelection) {
    164             VisiblePosition start(selection.visibleStart());
    165             // We match AppKit's rule: Start 1 character before the selection.
    166             VisiblePosition oneBeforeStart = start.previous();
    167             setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
    168         } else {
    169             setStart(spellingSearchRange.get(), selection.visibleEnd());
    170         }
    171     }
    172 
    173     Position position = spellingSearchRange->startPosition();
    174     if (!isEditablePosition(position)) {
    175         // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
    176         // selection is editable.
    177         // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
    178         // when spell checking the whole document before sending the message.
    179         // In that case the document might not be editable, but there are editable pockets that need to be spell checked.
    180 
    181         position = firstEditablePositionAfterPositionInRoot(position, m_frame.document()->documentElement()).deepEquivalent();
    182         if (position.isNull())
    183             return;
    184 
    185         Position rangeCompliantPosition = position.parentAnchoredEquivalent();
    186         spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), IGNORE_EXCEPTION);
    187         startedWithSelection = false; // won't need to wrap
    188     }
    189 
    190     // topNode defines the whole range we want to operate on
    191     Node* topNode = highestEditableRoot(position);
    192     // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
    193     spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), IGNORE_EXCEPTION);
    194 
    195     // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
    196     // at a word boundary. Going back by one char and then forward by a word does the trick.
    197     if (startedWithSelection) {
    198         VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
    199         if (oneBeforeStart.isNotNull())
    200             setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
    201         // else we were already at the start of the editable node
    202     }
    203 
    204     if (spellingSearchRange->collapsed(IGNORE_EXCEPTION))
    205         return; // nothing to search in
    206 
    207     // We go to the end of our first range instead of the start of it, just to be sure
    208     // we don't get foiled by any word boundary problems at the start. It means we might
    209     // do a tiny bit more searching.
    210     Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer();
    211     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset();
    212 
    213     int misspellingOffset = 0;
    214     GrammarDetail grammarDetail;
    215     int grammarPhraseOffset = 0;
    216     RefPtr<Range> grammarSearchRange;
    217     String badGrammarPhrase;
    218     String misspelledWord;
    219 
    220     bool isSpelling = true;
    221     int foundOffset = 0;
    222     String foundItem;
    223     RefPtr<Range> firstMisspellingRange;
    224     if (unifiedTextCheckerEnabled()) {
    225         grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
    226         foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    227         if (isSpelling) {
    228             misspelledWord = foundItem;
    229             misspellingOffset = foundOffset;
    230         } else {
    231             badGrammarPhrase = foundItem;
    232             grammarPhraseOffset = foundOffset;
    233         }
    234     } else {
    235         misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    236         grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
    237         if (!misspelledWord.isEmpty()) {
    238             // Stop looking at start of next misspelled word
    239             CharacterIterator chars(grammarSearchRange.get());
    240             chars.advance(misspellingOffset);
    241             grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
    242         }
    243 
    244         if (isGrammarCheckingEnabled())
    245             badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    246     }
    247 
    248     // 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
    249     // block rather than at a selection).
    250     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
    251         spellingSearchRange->setStart(topNode, 0, IGNORE_EXCEPTION);
    252         // going until the end of the very first chunk we tested is far enough
    253         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, IGNORE_EXCEPTION);
    254 
    255         if (unifiedTextCheckerEnabled()) {
    256             grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
    257             foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    258             if (isSpelling) {
    259                 misspelledWord = foundItem;
    260                 misspellingOffset = foundOffset;
    261             } else {
    262                 badGrammarPhrase = foundItem;
    263                 grammarPhraseOffset = foundOffset;
    264             }
    265         } else {
    266             misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    267             grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
    268             if (!misspelledWord.isEmpty()) {
    269                 // Stop looking at start of next misspelled word
    270                 CharacterIterator chars(grammarSearchRange.get());
    271                 chars.advance(misspellingOffset);
    272                 grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
    273             }
    274 
    275             if (isGrammarCheckingEnabled())
    276                 badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    277         }
    278     }
    279 
    280     if (!badGrammarPhrase.isEmpty()) {
    281         // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
    282         // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
    283         // panel, and store a marker so we draw the green squiggle later.
    284 
    285         ASSERT(badGrammarPhrase.length() > 0);
    286         ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
    287 
    288         // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
    289         RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
    290         m_frame.selection().setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
    291         m_frame.selection().revealSelection();
    292 
    293         m_frame.document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
    294     } else if (!misspelledWord.isEmpty()) {
    295         // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
    296         // a marker so we draw the red squiggle later.
    297 
    298         RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
    299         m_frame.selection().setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
    300         m_frame.selection().revealSelection();
    301 
    302         spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord);
    303         m_frame.document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
    304     }
    305 }
    306 
    307 String SpellChecker::misspelledWordAtCaretOrRange(Node* clickedNode) const
    308 {
    309     if (!isContinuousSpellCheckingEnabled() || !clickedNode || !isSpellCheckingEnabledFor(clickedNode))
    310         return String();
    311 
    312     VisibleSelection selection = m_frame.selection().selection();
    313     if (!selection.isContentEditable() || selection.isNone())
    314         return String();
    315 
    316     VisibleSelection wordSelection(selection.base());
    317     wordSelection.expandUsingGranularity(WordGranularity);
    318     RefPtr<Range> wordRange = wordSelection.toNormalizedRange();
    319 
    320     // In compliance with GTK+ applications, additionally allow to provide suggestions when the current
    321     // selection exactly match the word selection.
    322     if (selection.isRange() && !areRangesEqual(wordRange.get(), selection.toNormalizedRange().get()))
    323         return String();
    324 
    325     String word = wordRange->text();
    326     if (word.isEmpty())
    327         return String();
    328 
    329     int wordLength = word.length();
    330     int misspellingLocation = -1;
    331     int misspellingLength = 0;
    332     textChecker().checkSpellingOfString(word, &misspellingLocation, &misspellingLength);
    333 
    334     return misspellingLength == wordLength ? word : String();
    335 }
    336 
    337 void SpellChecker::showSpellingGuessPanel()
    338 {
    339     if (spellCheckerClient().spellingUIIsShowing()) {
    340         spellCheckerClient().showSpellingUI(false);
    341         return;
    342     }
    343 
    344     advanceToNextMisspelling(true);
    345     spellCheckerClient().showSpellingUI(true);
    346 }
    347 
    348 void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
    349 {
    350     RefPtr<Range> selectedRange = movingSelection.toNormalizedRange();
    351     if (selectedRange)
    352         m_frame.document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::MisspellingMarkers());
    353 }
    354 
    355 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
    356 {
    357     markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled(), movingSelection);
    358 }
    359 
    360 void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping)
    361 {
    362     if (unifiedTextCheckerEnabled()) {
    363         TextCheckingTypeMask textCheckingOptions = 0;
    364 
    365         if (isContinuousSpellCheckingEnabled())
    366             textCheckingOptions |= TextCheckingTypeSpelling;
    367 
    368         if (!(textCheckingOptions & TextCheckingTypeSpelling))
    369             return;
    370 
    371         if (isGrammarCheckingEnabled())
    372             textCheckingOptions |= TextCheckingTypeGrammar;
    373 
    374         VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
    375         if (textCheckingOptions & TextCheckingTypeGrammar) {
    376             VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
    377             markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
    378         } else {
    379             markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
    380         }
    381         return;
    382     }
    383 
    384     if (!isContinuousSpellCheckingEnabled())
    385         return;
    386 
    387     // Check spelling of one word
    388     RefPtr<Range> misspellingRange;
    389     markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
    390 
    391     // Autocorrect the misspelled word.
    392     if (!misspellingRange)
    393         return;
    394 
    395     // Get the misspelled word.
    396     const String misspelledWord = plainText(misspellingRange.get());
    397     String autocorrectedString = textChecker().getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
    398 
    399     // If autocorrected word is non empty, replace the misspelled word by this word.
    400     if (!autocorrectedString.isEmpty()) {
    401         VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
    402         if (newSelection != m_frame.selection().selection()) {
    403             m_frame.selection().setSelection(newSelection);
    404         }
    405 
    406         m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false);
    407 
    408         // Reset the charet one character further.
    409         m_frame.selection().moveTo(m_frame.selection().end());
    410         m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
    411     }
    412 
    413     if (!isGrammarCheckingEnabled())
    414         return;
    415 
    416     // Check grammar of entire sentence
    417     markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
    418 }
    419 
    420 void SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
    421 {
    422     // This function is called with a selection already expanded to word boundaries.
    423     // Might be nice to assert that here.
    424 
    425     // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
    426     // grammar checking can only be on if spell checking is also on.
    427     if (!isContinuousSpellCheckingEnabled())
    428         return;
    429 
    430     RefPtr<Range> searchRange(selection.toNormalizedRange());
    431     if (!searchRange)
    432         return;
    433 
    434     // If we're not in an editable node, bail.
    435     Node* editableNode = searchRange->startContainer();
    436     if (!editableNode || !editableNode->rendererIsEditable())
    437         return;
    438 
    439     if (!isSpellCheckingEnabledFor(editableNode))
    440         return;
    441 
    442     TextCheckingHelper checker(spellCheckerClient(), searchRange);
    443     if (checkSpelling)
    444         checker.markAllMisspellings(firstMisspellingRange);
    445     else if (isGrammarCheckingEnabled())
    446         checker.markAllBadGrammar();
    447 }
    448 
    449 bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const
    450 {
    451     if (!node)
    452         return false;
    453     const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
    454     if (!focusedElement)
    455         return false;
    456     return focusedElement->isSpellCheckingEnabled();
    457 }
    458 
    459 bool SpellChecker::isSpellCheckingEnabledInFocusedNode() const
    460 {
    461     return isSpellCheckingEnabledFor(m_frame.selection().start().deprecatedNode());
    462 }
    463 
    464 void SpellChecker::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
    465 {
    466     markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
    467 }
    468 
    469 void SpellChecker::markBadGrammar(const VisibleSelection& selection)
    470 {
    471     RefPtr<Range> firstMisspellingRange;
    472     markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
    473 }
    474 
    475 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
    476 {
    477     ASSERT(unifiedTextCheckerEnabled());
    478 
    479     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
    480 
    481     // This function is called with selections already expanded to word boundaries.
    482     if (!spellingRange || (shouldMarkGrammar && !grammarRange))
    483         return;
    484 
    485     // If we're not in an editable node, bail.
    486     Node* editableNode = spellingRange->startContainer();
    487     if (!editableNode || !editableNode->rendererIsEditable())
    488         return;
    489 
    490     if (!isSpellCheckingEnabledFor(editableNode))
    491         return;
    492 
    493     Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
    494     TextCheckingParagraph fullParagraphToCheck(rangeToCheck);
    495 
    496     bool asynchronous = m_frame.settings() && m_frame.settings()->asynchronousSpellCheckingEnabled();
    497     chunkAndMarkAllMisspellingsAndBadGrammar(textCheckingOptions, fullParagraphToCheck, asynchronous);
    498 }
    499 
    500 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node)
    501 {
    502     if (!node)
    503         return;
    504     RefPtr<Range> rangeToCheck = Range::create(*m_frame.document(), firstPositionInNode(node), lastPositionInNode(node));
    505     TextCheckingParagraph textToCheck(rangeToCheck, rangeToCheck);
    506     bool asynchronous = true;
    507     chunkAndMarkAllMisspellingsAndBadGrammar(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), textToCheck, asynchronous);
    508 }
    509 
    510 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(TextCheckingTypeMask textCheckingOptions, const TextCheckingParagraph& fullParagraphToCheck, bool asynchronous)
    511 {
    512     if (fullParagraphToCheck.isRangeEmpty() || fullParagraphToCheck.isEmpty())
    513         return;
    514 
    515     // Since the text may be quite big chunk it up and adjust to the sentence boundary.
    516     const int kChunkSize = 16 * 1024;
    517     int start = fullParagraphToCheck.checkingStart();
    518     int end = fullParagraphToCheck.checkingEnd();
    519     start = std::min(start, end);
    520     end = std::max(start, end);
    521     const int kNumChunksToCheck = asynchronous ? (end - start + kChunkSize - 1) / (kChunkSize) : 1;
    522     int currentChunkStart = start;
    523     RefPtr<Range> checkRange = fullParagraphToCheck.checkingRange();
    524     if (kNumChunksToCheck == 1 && asynchronous) {
    525         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, 0);
    526         return;
    527     }
    528 
    529     for (int iter = 0; iter < kNumChunksToCheck; ++iter) {
    530         checkRange = fullParagraphToCheck.subrange(currentChunkStart, kChunkSize);
    531         setStart(checkRange.get(), startOfSentence(checkRange->startPosition()));
    532         setEnd(checkRange.get(), endOfSentence(checkRange->endPosition()));
    533 
    534         int checkingLength = 0;
    535         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, iter, &checkingLength);
    536         currentChunkStart += checkingLength;
    537     }
    538 }
    539 
    540 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* checkRange, Range* paragraphRange, bool asynchronous, int requestNumber, int* checkingLength)
    541 {
    542     TextCheckingParagraph sentenceToCheck(checkRange, paragraphRange);
    543     if (checkingLength)
    544         *checkingLength = sentenceToCheck.checkingLength();
    545 
    546     RefPtr<SpellCheckRequest> request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessBatch, checkRange, paragraphRange, requestNumber);
    547 
    548     if (asynchronous) {
    549         m_spellCheckRequester->requestCheckingFor(request);
    550     } else {
    551         Vector<TextCheckingResult> results;
    552         checkTextOfParagraph(textChecker(), sentenceToCheck.text(), resolveTextCheckingTypeMask(textCheckingOptions), results);
    553         markAndReplaceFor(request, results);
    554     }
    555 }
    556 
    557 void SpellChecker::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results)
    558 {
    559     ASSERT(request);
    560 
    561     TextCheckingTypeMask textCheckingOptions = request->data().mask();
    562     TextCheckingParagraph paragraph(request->checkingRange(), request->paragraphRange());
    563 
    564     bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
    565     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
    566 
    567     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
    568     int selectionOffset = 0;
    569     int ambiguousBoundaryOffset = -1;
    570     bool selectionChanged = false;
    571     bool restoreSelectionAfterChange = false;
    572     bool adjustSelectionForParagraphBoundaries = false;
    573 
    574     if (shouldMarkSpelling) {
    575         if (m_frame.selection().isCaret()) {
    576             // Attempt to save the caret position so we can restore it later if needed
    577             Position caretPosition = m_frame.selection().end();
    578             selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION);
    579             restoreSelectionAfterChange = true;
    580             if (selectionOffset > 0 && (static_cast<unsigned>(selectionOffset) > paragraph.text().length() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter))
    581                 adjustSelectionForParagraphBoundaries = true;
    582             if (selectionOffset > 0 && static_cast<unsigned>(selectionOffset) <= paragraph.text().length() && isAmbiguousBoundaryCharacter(paragraph.textCharAt(selectionOffset - 1)))
    583                 ambiguousBoundaryOffset = selectionOffset - 1;
    584         }
    585     }
    586 
    587     for (unsigned i = 0; i < results.size(); i++) {
    588         int spellingRangeEndOffset = paragraph.checkingEnd();
    589         const TextCheckingResult* result = &results[i];
    590         int resultLocation = result->location + paragraph.checkingStart();
    591         int resultLength = result->length;
    592         bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset;
    593 
    594         // Only mark misspelling if:
    595         // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
    596         // 2. Result falls within spellingRange.
    597         // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
    598         //    "wouldn'" as misspelled right after apostrophe is typed.
    599         if (shouldMarkSpelling && result->decoration == TextDecorationTypeSpelling && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
    600             ASSERT(resultLength > 0 && resultLocation >= 0);
    601             RefPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength);
    602             misspellingRange->startContainer()->document().markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling, result->replacement, result->hash);
    603         } else if (shouldMarkGrammar && result->decoration == TextDecorationTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
    604             ASSERT(resultLength > 0 && resultLocation >= 0);
    605             for (unsigned j = 0; j < result->details.size(); j++) {
    606                 const GrammarDetail* detail = &result->details[j];
    607                 ASSERT(detail->length > 0 && detail->location >= 0);
    608                 if (paragraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) {
    609                     RefPtr<Range> badGrammarRange = paragraph.subrange(resultLocation + detail->location, detail->length);
    610                     badGrammarRange->startContainer()->document().markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription, result->hash);
    611                 }
    612             }
    613         } else if (result->decoration == TextDecorationTypeInvisibleSpellcheck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset) {
    614             ASSERT(resultLength > 0 && resultLocation >= 0);
    615             RefPtr<Range> invisibleSpellcheckRange = paragraph.subrange(resultLocation, resultLength);
    616             invisibleSpellcheckRange->startContainer()->document().markers()->addMarker(invisibleSpellcheckRange.get(), DocumentMarker::InvisibleSpellcheck, result->replacement, result->hash);
    617         }
    618     }
    619 
    620     if (selectionChanged) {
    621         TextCheckingParagraph extendedParagraph(paragraph);
    622         // Restore the caret position if we have made any replacements
    623         extendedParagraph.expandRangeToNextEnd();
    624         if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= extendedParagraph.rangeLength()) {
    625             RefPtr<Range> selectionRange = extendedParagraph.subrange(0, selectionOffset);
    626             m_frame.selection().moveTo(selectionRange->endPosition(), DOWNSTREAM);
    627             if (adjustSelectionForParagraphBoundaries)
    628                 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
    629         } else {
    630             // If this fails for any reason, the fallback is to go one position beyond the last replacement
    631             m_frame.selection().moveTo(m_frame.selection().end());
    632             m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
    633         }
    634     }
    635 }
    636 
    637 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
    638 {
    639     if (unifiedTextCheckerEnabled()) {
    640         if (!isContinuousSpellCheckingEnabled())
    641             return;
    642 
    643         // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
    644         TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
    645         if (markGrammar && isGrammarCheckingEnabled())
    646             textCheckingOptions |= TextCheckingTypeGrammar;
    647         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
    648         return;
    649     }
    650 
    651     RefPtr<Range> firstMisspellingRange;
    652     markMisspellings(spellingSelection, firstMisspellingRange);
    653     if (markGrammar)
    654         markBadGrammar(grammarSelection);
    655 }
    656 
    657 void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
    658 {
    659     if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling))
    660         return;
    661 
    662     // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
    663     // several scenarios:
    664     // 1. Insert in the middle of a word.
    665     // 2. Appending non whitespace at the beginning of word.
    666     // 3. Appending non whitespace at the end of word.
    667     // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
    668     // remove the markers on that word.
    669     // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
    670     // selection, and remove words between the selection boundaries.
    671     //
    672     VisiblePosition startOfSelection = m_frame.selection().selection().start();
    673     VisiblePosition endOfSelection = m_frame.selection().selection().end();
    674     if (startOfSelection.isNull())
    675         return;
    676     // First word is the word that ends after or on the start of selection.
    677     VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
    678     VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
    679     // Last word is the word that begins before or on the end of selection
    680     VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
    681     VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
    682 
    683     if (startOfFirstWord.isNull()) {
    684         startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
    685         endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
    686     }
    687 
    688     if (endOfLastWord.isNull()) {
    689         startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
    690         endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
    691     }
    692 
    693     // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
    694     // we choose next word as the first word.
    695     if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) {
    696         startOfFirstWord = nextWordPosition(startOfFirstWord);
    697         endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
    698         if (startOfFirstWord == endOfSelection)
    699             return;
    700     }
    701 
    702     // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
    703     // we choose previous word as the last word.
    704     if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) {
    705         startOfLastWord = previousWordPosition(startOfLastWord);
    706         endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
    707         if (endOfLastWord == startOfSelection)
    708             return;
    709     }
    710 
    711     if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
    712         return;
    713 
    714     // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
    715     // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
    716     // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
    717     // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
    718     // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
    719     // of marker that contains the word in question, and remove marker on that whole range.
    720     Document* document = m_frame.document();
    721     ASSERT(document);
    722     RefPtr<Range> wordRange = Range::create(*document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
    723 
    724     document->markers()->removeMarkers(wordRange.get(), DocumentMarker::MisspellingMarkers(), DocumentMarkerController::RemovePartiallyOverlappingMarker);
    725 }
    726 
    727 void SpellChecker::didEndEditingOnTextField(Element* e)
    728 {
    729     // Remove markers when deactivating a selection in an <input type="text"/>.
    730     // Prevent new ones from appearing too.
    731     m_spellCheckRequester->cancelCheck();
    732     HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlElement(e);
    733     HTMLElement* innerText = textFormControlElement->innerTextElement();
    734     DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling);
    735     if (isGrammarCheckingEnabled() || unifiedTextCheckerEnabled())
    736         markerTypes.add(DocumentMarker::Grammar);
    737     for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
    738         m_frame.document()->markers()->removeMarkers(node, markerTypes);
    739     }
    740 }
    741 
    742 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
    743 {
    744     bool closeTyping = options & FrameSelection::CloseTyping;
    745     bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
    746     bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
    747     if (isContinuousSpellCheckingEnabled) {
    748         VisibleSelection newAdjacentWords;
    749         VisibleSelection newSelectedSentence;
    750         bool caretBrowsing = m_frame.settings() && m_frame.settings()->caretBrowsingEnabled();
    751         if (m_frame.selection().selection().isContentEditable() || caretBrowsing) {
    752             VisiblePosition newStart(m_frame.selection().selection().visibleStart());
    753             newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
    754             if (isContinuousGrammarCheckingEnabled)
    755                 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
    756         }
    757 
    758         // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
    759         bool shouldCheckSpellingAndGrammar = !(options & FrameSelection::SpellCorrectionTriggered);
    760 
    761         // When typing we check spelling elsewhere, so don't redo it here.
    762         // If this is a change in selection resulting from a delete operation,
    763         // oldSelection may no longer be in the document.
    764         if (shouldCheckSpellingAndGrammar
    765             && closeTyping
    766             && oldSelection.isContentEditable()
    767             && oldSelection.start().inDocument()
    768             && !isSelectionInTextField(oldSelection)) {
    769             spellCheckOldSelection(oldSelection, newAdjacentWords, newSelectedSentence);
    770         }
    771 
    772         if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)) {
    773             if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
    774                 m_frame.document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
    775         }
    776         if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeGrammar)) {
    777             if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
    778                 m_frame.document()->markers()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
    779         }
    780     }
    781 
    782     // When continuous spell checking is off, existing markers disappear after the selection changes.
    783     if (!isContinuousSpellCheckingEnabled)
    784         m_frame.document()->markers()->removeMarkers(DocumentMarker::Spelling);
    785     if (!isContinuousGrammarCheckingEnabled)
    786         m_frame.document()->markers()->removeMarkers(DocumentMarker::Grammar);
    787 }
    788 
    789 void SpellChecker::spellCheckAfterBlur()
    790 {
    791     if (!m_frame.selection().selection().isContentEditable())
    792         return;
    793 
    794     if (isSelectionInTextField(m_frame.selection().selection())) {
    795         // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this.
    796         return;
    797     }
    798 
    799     VisibleSelection empty;
    800     spellCheckOldSelection(m_frame.selection().selection(), empty, empty);
    801 }
    802 
    803 void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords, const VisibleSelection& newSelectedSentence)
    804 {
    805     VisiblePosition oldStart(oldSelection.visibleStart());
    806     VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
    807     if (oldAdjacentWords  != newAdjacentWords) {
    808         if (isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled()) {
    809             VisibleSelection selectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
    810             markMisspellingsAndBadGrammar(oldAdjacentWords, true, selectedSentence);
    811         } else {
    812             markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
    813         }
    814     }
    815 }
    816 
    817 static Node* findFirstMarkable(Node* node)
    818 {
    819     while (node) {
    820         if (!node->renderer())
    821             return 0;
    822         if (node->renderer()->isText())
    823             return node;
    824         if (node->renderer()->isTextControl())
    825             node = toRenderTextControl(node->renderer())->textFormControlElement()->visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
    826         else if (node->firstChild())
    827             node = node->firstChild();
    828         else
    829             node = node->nextSibling();
    830     }
    831 
    832     return 0;
    833 }
    834 
    835 bool SpellChecker::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
    836 {
    837     Node* node = findFirstMarkable(m_frame.selection().start().deprecatedNode());
    838     if (!node)
    839         return false;
    840 
    841     unsigned startOffset = static_cast<unsigned>(from);
    842     unsigned endOffset = static_cast<unsigned>(from + length);
    843     Vector<DocumentMarker*> markers = m_frame.document()->markers()->markersFor(node);
    844     for (size_t i = 0; i < markers.size(); ++i) {
    845         DocumentMarker* marker = markers[i];
    846         if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType)
    847             return true;
    848     }
    849 
    850     return false;
    851 }
    852 
    853 TextCheckingTypeMask SpellChecker::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions)
    854 {
    855     bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
    856     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
    857 
    858     TextCheckingTypeMask checkingTypes = 0;
    859     if (shouldMarkSpelling)
    860         checkingTypes |= TextCheckingTypeSpelling;
    861     if (shouldMarkGrammar)
    862         checkingTypes |= TextCheckingTypeGrammar;
    863 
    864     return checkingTypes;
    865 }
    866 
    867 bool SpellChecker::unifiedTextCheckerEnabled() const
    868 {
    869     return WebCore::unifiedTextCheckerEnabled(&m_frame);
    870 }
    871 
    872 void SpellChecker::cancelCheck()
    873 {
    874     m_spellCheckRequester->cancelCheck();
    875 }
    876 
    877 void SpellChecker::requestTextChecking(const Element& element)
    878 {
    879     RefPtr<Range> rangeToCheck = rangeOfContents(const_cast<Element*>(&element));
    880     m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
    881 }
    882 
    883 
    884 } // namespace WebCore
    885