Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / editing / SpellChecker.cpp
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/LocalFrame.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 isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isTextField();
59 }
60
61 } // namespace
62
63 PassOwnPtr<SpellChecker> SpellChecker::create(LocalFrame& 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(LocalFrame& 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 (LocalFrame* 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 = isHTMLInputElement(*textControl) && 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 (RefPtrWillBeRawPtr<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     RefPtrWillBeRawPtr<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())
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     RefPtrWillBeRawPtr<Range> grammarSearchRange = nullptr;
217     String badGrammarPhrase;
218     String misspelledWord;
219
220     bool isSpelling = true;
221     int foundOffset = 0;
222     String foundItem;
223     RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
224     if (unifiedTextCheckerEnabled()) {
225         grammarSearchRange = spellingSearchRange->cloneRange();
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();
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();
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();
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         RefPtrWillBeRawPtr<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         RefPtrWillBeRawPtr<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 void SpellChecker::showSpellingGuessPanel()
308 {
309     if (spellCheckerClient().spellingUIIsShowing()) {
310         spellCheckerClient().showSpellingUI(false);
311         return;
312     }
313
314     advanceToNextMisspelling(true);
315     spellCheckerClient().showSpellingUI(true);
316 }
317
318 void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
319 {
320     RefPtrWillBeRawPtr<Range> selectedRange = movingSelection.toNormalizedRange();
321     if (selectedRange)
322         m_frame.document()->markers().removeMarkers(selectedRange.get(), DocumentMarker::MisspellingMarkers());
323 }
324
325 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelection)
326 {
327     markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled(), movingSelection);
328 }
329
330 void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping)
331 {
332     if (unifiedTextCheckerEnabled()) {
333         TextCheckingTypeMask textCheckingOptions = 0;
334
335         if (isContinuousSpellCheckingEnabled())
336             textCheckingOptions |= TextCheckingTypeSpelling;
337
338         if (!(textCheckingOptions & TextCheckingTypeSpelling))
339             return;
340
341         if (isGrammarCheckingEnabled())
342             textCheckingOptions |= TextCheckingTypeGrammar;
343
344         VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
345         if (textCheckingOptions & TextCheckingTypeGrammar) {
346             VisibleSelection selectedSentence = VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart));
347             markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
348         } else {
349             markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
350         }
351         return;
352     }
353
354     if (!isContinuousSpellCheckingEnabled())
355         return;
356
357     // Check spelling of one word
358     RefPtrWillBeRawPtr<Range> misspellingRange = nullptr;
359     markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
360
361     // Autocorrect the misspelled word.
362     if (!misspellingRange)
363         return;
364
365     // Get the misspelled word.
366     const String misspelledWord = plainText(misspellingRange.get());
367     String autocorrectedString = textChecker().getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
368
369     // If autocorrected word is non empty, replace the misspelled word by this word.
370     if (!autocorrectedString.isEmpty()) {
371         VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
372         if (newSelection != m_frame.selection().selection()) {
373             m_frame.selection().setSelection(newSelection);
374         }
375
376         m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false);
377
378         // Reset the charet one character further.
379         m_frame.selection().moveTo(m_frame.selection().selection().visibleEnd());
380         m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
381     }
382
383     if (!isGrammarCheckingEnabled())
384         return;
385
386     // Check grammar of entire sentence
387     markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wordStart)));
388 }
389
390 void SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtrWillBeRawPtr<Range>& firstMisspellingRange)
391 {
392     // This function is called with a selection already expanded to word boundaries.
393     // Might be nice to assert that here.
394
395     // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
396     // grammar checking can only be on if spell checking is also on.
397     if (!isContinuousSpellCheckingEnabled())
398         return;
399
400     RefPtrWillBeRawPtr<Range> searchRange(selection.toNormalizedRange());
401     if (!searchRange)
402         return;
403
404     // If we're not in an editable node, bail.
405     Node* editableNode = searchRange->startContainer();
406     if (!editableNode || !editableNode->rendererIsEditable())
407         return;
408
409     if (!isSpellCheckingEnabledFor(editableNode))
410         return;
411
412     TextCheckingHelper checker(spellCheckerClient(), searchRange);
413     if (checkSpelling)
414         checker.markAllMisspellings(firstMisspellingRange);
415     else if (isGrammarCheckingEnabled())
416         checker.markAllBadGrammar();
417 }
418
419 bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const
420 {
421     if (!node)
422         return false;
423     const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
424     if (!focusedElement)
425         return false;
426     return focusedElement->isSpellCheckingEnabled();
427 }
428
429 bool SpellChecker::isSpellCheckingEnabledInFocusedNode() const
430 {
431     return isSpellCheckingEnabledFor(m_frame.selection().start().deprecatedNode());
432 }
433
434 void SpellChecker::markMisspellings(const VisibleSelection& selection, RefPtrWillBeRawPtr<Range>& firstMisspellingRange)
435 {
436     markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
437 }
438
439 void SpellChecker::markBadGrammar(const VisibleSelection& selection)
440 {
441     RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
442     markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
443 }
444
445 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
446 {
447     ASSERT(unifiedTextCheckerEnabled());
448
449     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
450
451     // This function is called with selections already expanded to word boundaries.
452     if (!spellingRange || (shouldMarkGrammar && !grammarRange))
453         return;
454
455     // If we're not in an editable node, bail.
456     Node* editableNode = spellingRange->startContainer();
457     if (!editableNode || !editableNode->rendererIsEditable())
458         return;
459
460     if (!isSpellCheckingEnabledFor(editableNode))
461         return;
462
463     Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
464     TextCheckingParagraph fullParagraphToCheck(rangeToCheck);
465
466     bool asynchronous = m_frame.settings() && m_frame.settings()->asynchronousSpellCheckingEnabled();
467     chunkAndMarkAllMisspellingsAndBadGrammar(textCheckingOptions, fullParagraphToCheck, asynchronous);
468 }
469
470 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node)
471 {
472     if (!node)
473         return;
474     RefPtrWillBeRawPtr<Range> rangeToCheck = Range::create(*m_frame.document(), firstPositionInNode(node), lastPositionInNode(node));
475     TextCheckingParagraph textToCheck(rangeToCheck, rangeToCheck);
476     bool asynchronous = true;
477     chunkAndMarkAllMisspellingsAndBadGrammar(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), textToCheck, asynchronous);
478 }
479
480 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(TextCheckingTypeMask textCheckingOptions, const TextCheckingParagraph& fullParagraphToCheck, bool asynchronous)
481 {
482     if (fullParagraphToCheck.isRangeEmpty() || fullParagraphToCheck.isEmpty())
483         return;
484
485     // Since the text may be quite big chunk it up and adjust to the sentence boundary.
486     const int kChunkSize = 16 * 1024;
487     int start = fullParagraphToCheck.checkingStart();
488     int end = fullParagraphToCheck.checkingEnd();
489     start = std::min(start, end);
490     end = std::max(start, end);
491     const int kNumChunksToCheck = asynchronous ? (end - start + kChunkSize - 1) / (kChunkSize) : 1;
492     int currentChunkStart = start;
493     RefPtrWillBeRawPtr<Range> checkRange = fullParagraphToCheck.checkingRange();
494     if (kNumChunksToCheck == 1 && asynchronous) {
495         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, 0);
496         return;
497     }
498
499     for (int iter = 0; iter < kNumChunksToCheck; ++iter) {
500         checkRange = fullParagraphToCheck.subrange(currentChunkStart, kChunkSize);
501         setStart(checkRange.get(), startOfSentence(VisiblePosition(checkRange->startPosition())));
502         setEnd(checkRange.get(), endOfSentence(VisiblePosition(checkRange->endPosition())));
503
504         int checkingLength = 0;
505         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange.get(), checkRange.get(), asynchronous, iter, &checkingLength);
506         currentChunkStart += checkingLength;
507     }
508 }
509
510 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* checkRange, Range* paragraphRange, bool asynchronous, int requestNumber, int* checkingLength)
511 {
512     TextCheckingParagraph sentenceToCheck(checkRange, paragraphRange);
513     if (checkingLength)
514         *checkingLength = sentenceToCheck.checkingLength();
515
516     RefPtr<SpellCheckRequest> request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessBatch, checkRange, paragraphRange, requestNumber);
517
518     if (asynchronous) {
519         m_spellCheckRequester->requestCheckingFor(request);
520     } else {
521         Vector<TextCheckingResult> results;
522         checkTextOfParagraph(textChecker(), sentenceToCheck.text(), resolveTextCheckingTypeMask(textCheckingOptions), results);
523         markAndReplaceFor(request, results);
524     }
525 }
526
527 void SpellChecker::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results)
528 {
529     ASSERT(request);
530
531     TextCheckingTypeMask textCheckingOptions = request->data().mask();
532     TextCheckingParagraph paragraph(request->checkingRange(), request->paragraphRange());
533
534     bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
535     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
536
537     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
538     int selectionOffset = 0;
539     int ambiguousBoundaryOffset = -1;
540     bool selectionChanged = false;
541     bool restoreSelectionAfterChange = false;
542     bool adjustSelectionForParagraphBoundaries = false;
543
544     if (shouldMarkSpelling) {
545         if (m_frame.selection().isCaret()) {
546             // Attempt to save the caret position so we can restore it later if needed
547             Position caretPosition = m_frame.selection().end();
548             selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION);
549             restoreSelectionAfterChange = true;
550             if (selectionOffset > 0 && (static_cast<unsigned>(selectionOffset) > paragraph.text().length() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter))
551                 adjustSelectionForParagraphBoundaries = true;
552             if (selectionOffset > 0 && static_cast<unsigned>(selectionOffset) <= paragraph.text().length() && isAmbiguousBoundaryCharacter(paragraph.textCharAt(selectionOffset - 1)))
553                 ambiguousBoundaryOffset = selectionOffset - 1;
554         }
555     }
556
557     for (unsigned i = 0; i < results.size(); i++) {
558         int spellingRangeEndOffset = paragraph.checkingEnd();
559         const TextCheckingResult* result = &results[i];
560         int resultLocation = result->location + paragraph.checkingStart();
561         int resultLength = result->length;
562         bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && resultLocation + resultLength == ambiguousBoundaryOffset;
563
564         // Only mark misspelling if:
565         // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
566         // 2. Result falls within spellingRange.
567         // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
568         //    "wouldn'" as misspelled right after apostrophe is typed.
569         if (shouldMarkSpelling && result->decoration == TextDecorationTypeSpelling && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
570             ASSERT(resultLength > 0 && resultLocation >= 0);
571             RefPtrWillBeRawPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength);
572             misspellingRange->startContainer()->document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling, result->replacement, result->hash);
573         } else if (shouldMarkGrammar && result->decoration == TextDecorationTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
574             ASSERT(resultLength > 0 && resultLocation >= 0);
575             for (unsigned j = 0; j < result->details.size(); j++) {
576                 const GrammarDetail* detail = &result->details[j];
577                 ASSERT(detail->length > 0 && detail->location >= 0);
578                 if (paragraph.checkingRangeCovers(resultLocation + detail->location, detail->length)) {
579                     RefPtrWillBeRawPtr<Range> badGrammarRange = paragraph.subrange(resultLocation + detail->location, detail->length);
580                     badGrammarRange->startContainer()->document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription, result->hash);
581                 }
582             }
583         } else if (result->decoration == TextDecorationTypeInvisibleSpellcheck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset) {
584             ASSERT(resultLength > 0 && resultLocation >= 0);
585             RefPtrWillBeRawPtr<Range> invisibleSpellcheckRange = paragraph.subrange(resultLocation, resultLength);
586             invisibleSpellcheckRange->startContainer()->document().markers().addMarker(invisibleSpellcheckRange.get(), DocumentMarker::InvisibleSpellcheck, result->replacement, result->hash);
587         }
588     }
589
590     if (selectionChanged) {
591         TextCheckingParagraph extendedParagraph(paragraph);
592         // Restore the caret position if we have made any replacements
593         extendedParagraph.expandRangeToNextEnd();
594         if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffset <= extendedParagraph.rangeLength()) {
595             RefPtrWillBeRawPtr<Range> selectionRange = extendedParagraph.subrange(0, selectionOffset);
596             m_frame.selection().moveTo(selectionRange->endPosition(), DOWNSTREAM);
597             if (adjustSelectionForParagraphBoundaries)
598                 m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
599         } else {
600             // If this fails for any reason, the fallback is to go one position beyond the last replacement
601             m_frame.selection().moveTo(m_frame.selection().selection().visibleEnd());
602             m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
603         }
604     }
605 }
606
607 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
608 {
609     if (unifiedTextCheckerEnabled()) {
610         if (!isContinuousSpellCheckingEnabled())
611             return;
612
613         // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
614         TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
615         if (markGrammar && isGrammarCheckingEnabled())
616             textCheckingOptions |= TextCheckingTypeGrammar;
617         markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
618         return;
619     }
620
621     RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
622     markMisspellings(spellingSelection, firstMisspellingRange);
623     if (markGrammar)
624         markBadGrammar(grammarSelection);
625 }
626
627 void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
628 {
629     if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling))
630         return;
631
632     // We want to remove the markers from a word if an editing command will change the word. This can happen in one of
633     // several scenarios:
634     // 1. Insert in the middle of a word.
635     // 2. Appending non whitespace at the beginning of word.
636     // 3. Appending non whitespace at the end of word.
637     // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to
638     // remove the markers on that word.
639     // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
640     // selection, and remove words between the selection boundaries.
641     //
642     VisiblePosition startOfSelection = m_frame.selection().selection().visibleStart();
643     VisiblePosition endOfSelection = m_frame.selection().selection().visibleEnd();
644     if (startOfSelection.isNull())
645         return;
646     // First word is the word that ends after or on the start of selection.
647     VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary);
648     VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary);
649     // Last word is the word that begins before or on the end of selection
650     VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary);
651     VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary);
652
653     if (startOfFirstWord.isNull()) {
654         startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
655         endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
656     }
657
658     if (endOfLastWord.isNull()) {
659         startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
660         endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
661     }
662
663     // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection,
664     // we choose next word as the first word.
665     if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) {
666         startOfFirstWord = nextWordPosition(startOfFirstWord);
667         endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
668         if (startOfFirstWord == endOfSelection)
669             return;
670     }
671
672     // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
673     // we choose previous word as the last word.
674     if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) {
675         startOfLastWord = previousWordPosition(startOfLastWord);
676         endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
677         if (endOfLastWord == startOfSelection)
678             return;
679     }
680
681     if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull())
682         return;
683
684     // Now we remove markers on everything between startOfFirstWord and endOfLastWord.
685     // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
686     // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant
687     // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
688     // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of
689     // of marker that contains the word in question, and remove marker on that whole range.
690     Document* document = m_frame.document();
691     ASSERT(document);
692     RefPtrWillBeRawPtr<Range> wordRange = Range::create(*document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
693
694     document->markers().removeMarkers(wordRange.get(), DocumentMarker::MisspellingMarkers(), DocumentMarkerController::RemovePartiallyOverlappingMarker);
695 }
696
697 void SpellChecker::didEndEditingOnTextField(Element* e)
698 {
699     // Remove markers when deactivating a selection in an <input type="text"/>.
700     // Prevent new ones from appearing too.
701     m_spellCheckRequester->cancelCheck();
702     HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlElement(e);
703     HTMLElement* innerText = textFormControlElement->innerTextElement();
704     DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling);
705     if (isGrammarCheckingEnabled() || unifiedTextCheckerEnabled())
706         markerTypes.add(DocumentMarker::Grammar);
707     for (Node* node = innerText; node; node = NodeTraversal::next(*node, innerText)) {
708         m_frame.document()->markers().removeMarkers(node, markerTypes);
709     }
710 }
711
712 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
713 {
714     bool closeTyping = options & FrameSelection::CloseTyping;
715     bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
716     bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
717     if (isContinuousSpellCheckingEnabled) {
718         VisibleSelection newAdjacentWords;
719         VisibleSelection newSelectedSentence;
720         bool caretBrowsing = m_frame.settings() && m_frame.settings()->caretBrowsingEnabled();
721         if (m_frame.selection().selection().isContentEditable() || caretBrowsing) {
722             VisiblePosition newStart(m_frame.selection().selection().visibleStart());
723             newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
724             if (isContinuousGrammarCheckingEnabled)
725                 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
726         }
727
728         // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
729         bool shouldCheckSpellingAndGrammar = !(options & FrameSelection::SpellCorrectionTriggered);
730
731         // When typing we check spelling elsewhere, so don't redo it here.
732         // If this is a change in selection resulting from a delete operation,
733         // oldSelection may no longer be in the document.
734         if (shouldCheckSpellingAndGrammar
735             && closeTyping
736             && oldSelection.isContentEditable()
737             && oldSelection.start().inDocument()
738             && !isSelectionInTextField(oldSelection)) {
739             spellCheckOldSelection(oldSelection, newAdjacentWords, newSelectedSentence);
740         }
741
742         if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)) {
743             if (RefPtrWillBeRawPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
744                 m_frame.document()->markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling);
745         }
746         if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeGrammar)) {
747             if (RefPtrWillBeRawPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
748                 m_frame.document()->markers().removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
749         }
750     }
751
752     // When continuous spell checking is off, existing markers disappear after the selection changes.
753     if (!isContinuousSpellCheckingEnabled)
754         m_frame.document()->markers().removeMarkers(DocumentMarker::Spelling);
755     if (!isContinuousGrammarCheckingEnabled)
756         m_frame.document()->markers().removeMarkers(DocumentMarker::Grammar);
757 }
758
759 void SpellChecker::spellCheckAfterBlur()
760 {
761     if (!m_frame.selection().selection().isContentEditable())
762         return;
763
764     if (isSelectionInTextField(m_frame.selection().selection())) {
765         // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this.
766         return;
767     }
768
769     VisibleSelection empty;
770     spellCheckOldSelection(m_frame.selection().selection(), empty, empty);
771 }
772
773 void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords, const VisibleSelection& newSelectedSentence)
774 {
775     VisiblePosition oldStart(oldSelection.visibleStart());
776     VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
777     if (oldAdjacentWords  != newAdjacentWords) {
778         if (isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled()) {
779             VisibleSelection selectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
780             markMisspellingsAndBadGrammar(oldAdjacentWords, true, selectedSentence);
781         } else {
782             markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
783         }
784     }
785 }
786
787 static Node* findFirstMarkable(Node* node)
788 {
789     while (node) {
790         if (!node->renderer())
791             return 0;
792         if (node->renderer()->isText())
793             return node;
794         if (node->renderer()->isTextControl())
795             node = toRenderTextControl(node->renderer())->textFormControlElement()->visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
796         else if (node->firstChild())
797             node = node->firstChild();
798         else
799             node = node->nextSibling();
800     }
801
802     return 0;
803 }
804
805 bool SpellChecker::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
806 {
807     Node* node = findFirstMarkable(m_frame.selection().start().deprecatedNode());
808     if (!node)
809         return false;
810
811     unsigned startOffset = static_cast<unsigned>(from);
812     unsigned endOffset = static_cast<unsigned>(from + length);
813     Vector<DocumentMarker*> markers = m_frame.document()->markers().markersFor(node);
814     for (size_t i = 0; i < markers.size(); ++i) {
815         DocumentMarker* marker = markers[i];
816         if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType)
817             return true;
818     }
819
820     return false;
821 }
822
823 TextCheckingTypeMask SpellChecker::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions)
824 {
825     bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
826     bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
827
828     TextCheckingTypeMask checkingTypes = 0;
829     if (shouldMarkSpelling)
830         checkingTypes |= TextCheckingTypeSpelling;
831     if (shouldMarkGrammar)
832         checkingTypes |= TextCheckingTypeGrammar;
833
834     return checkingTypes;
835 }
836
837 bool SpellChecker::unifiedTextCheckerEnabled() const
838 {
839     return WebCore::unifiedTextCheckerEnabled(&m_frame);
840 }
841
842 void SpellChecker::cancelCheck()
843 {
844     m_spellCheckRequester->cancelCheck();
845 }
846
847 void SpellChecker::requestTextChecking(const Element& element)
848 {
849     RefPtrWillBeRawPtr<Range> rangeToCheck = rangeOfContents(const_cast<Element*>(&element));
850     m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
851 }
852
853
854 } // namespace WebCore