Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Side by Side Diff: Source/core/editing/SpellChecker.cpp

Issue 1287263004: Move spell checker in core/editing/ related files into core/editing/spellcheck/ (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: 2015-08-17T10:33:57 Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/core/editing/SpellChecker.h ('k') | Source/core/editing/TextCheckingHelper.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "core/HTMLNames.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Element.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/editing/EditingUtilities.h"
35 #include "core/editing/Editor.h"
36 #include "core/editing/EphemeralRange.h"
37 #include "core/editing/SpellCheckRequester.h"
38 #include "core/editing/TextCheckingHelper.h"
39 #include "core/editing/VisibleUnits.h"
40 #include "core/editing/iterators/CharacterIterator.h"
41 #include "core/editing/markers/DocumentMarkerController.h"
42 #include "core/frame/LocalFrame.h"
43 #include "core/frame/Settings.h"
44 #include "core/html/HTMLInputElement.h"
45 #include "core/layout/LayoutTextControl.h"
46 #include "core/loader/EmptyClients.h"
47 #include "core/page/Page.h"
48 #include "core/page/SpellCheckerClient.h"
49 #include "platform/text/TextCheckerClient.h"
50
51 namespace blink {
52
53 using namespace HTMLNames;
54
55 namespace {
56
57 bool isSelectionInTextField(const VisibleSelection& selection)
58 {
59 HTMLTextFormControlElement* textControl = enclosingTextFormControl(selection .start());
60 return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->i sTextField();
61 }
62
63 bool isSelectionInTextArea(const VisibleSelection& selection)
64 {
65 HTMLTextFormControlElement* textControl = enclosingTextFormControl(selection .start());
66 return isHTMLTextAreaElement(textControl);
67 }
68
69 bool isSelectionInTextFormControl(const VisibleSelection& selection)
70 {
71 return !!enclosingTextFormControl(selection.start());
72 }
73
74 } // namespace
75
76 PassOwnPtrWillBeRawPtr<SpellChecker> SpellChecker::create(LocalFrame& frame)
77 {
78 return adoptPtrWillBeNoop(new SpellChecker(frame));
79 }
80
81 static SpellCheckerClient& emptySpellCheckerClient()
82 {
83 DEFINE_STATIC_LOCAL(EmptySpellCheckerClient, client, ());
84 return client;
85 }
86
87 SpellCheckerClient& SpellChecker::spellCheckerClient() const
88 {
89 if (Page* page = frame().page())
90 return page->spellCheckerClient();
91 return emptySpellCheckerClient();
92 }
93
94 TextCheckerClient& SpellChecker::textChecker() const
95 {
96 return spellCheckerClient().textChecker();
97 }
98
99 SpellChecker::SpellChecker(LocalFrame& frame)
100 : m_frame(&frame)
101 , m_spellCheckRequester(SpellCheckRequester::create(frame))
102 {
103 }
104
105 SpellChecker::~SpellChecker()
106 {
107 }
108
109 bool SpellChecker::isContinuousSpellCheckingEnabled() const
110 {
111 return spellCheckerClient().isContinuousSpellCheckingEnabled();
112 }
113
114 void SpellChecker::toggleContinuousSpellChecking()
115 {
116 spellCheckerClient().toggleContinuousSpellChecking();
117 if (isContinuousSpellCheckingEnabled())
118 return;
119 for (Frame* frame = this->frame().page()->mainFrame(); frame; frame = frame- >tree().traverseNext()) {
120 if (!frame->isLocalFrame())
121 continue;
122 for (Node& node : NodeTraversal::startsAt(&toLocalFrame(frame)->document ()->rootNode()))
123 node.setAlreadySpellChecked(false);
124 }
125 }
126
127 bool SpellChecker::isGrammarCheckingEnabled()
128 {
129 return spellCheckerClient().isGrammarCheckingEnabled();
130 }
131
132 void SpellChecker::didBeginEditing(Element* element)
133 {
134 if (isContinuousSpellCheckingEnabled() && unifiedTextCheckerEnabled()) {
135 bool isTextField = false;
136 HTMLTextFormControlElement* enclosingHTMLTextFormControlElement = 0;
137 if (!isHTMLTextFormControlElement(*element))
138 enclosingHTMLTextFormControlElement = enclosingTextFormControl(first PositionInNode(element));
139 element = enclosingHTMLTextFormControlElement ? enclosingHTMLTextFormCon trolElement : element;
140 Element* parent = element;
141 if (isHTMLTextFormControlElement(*element)) {
142 HTMLTextFormControlElement* textControl = toHTMLTextFormControlEleme nt(element);
143 parent = textControl;
144 element = textControl->innerEditorElement();
145 if (!element)
146 return;
147 isTextField = isHTMLInputElement(*textControl) && toHTMLInputElement (*textControl).isTextField();
148 }
149
150 if (isTextField || !parent->isAlreadySpellChecked()) {
151 // We always recheck textfields because markers are removed from the m on blur.
152 VisibleSelection selection = VisibleSelection::selectionFromContents OfNode(element);
153 markMisspellingsAndBadGrammar(selection);
154 if (!isTextField)
155 parent->setAlreadySpellChecked(true);
156 }
157 }
158 }
159
160 void SpellChecker::ignoreSpelling()
161 {
162 removeMarkers(frame().selection().selection(), DocumentMarker::Spelling);
163 }
164
165 void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection)
166 {
167 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
168 // then we wrap and search from the doc start to (approximately) where we st arted.
169
170 // Start at the end of the selection, search to edge of document. Starting a t the selection end makes
171 // repeated "check spelling" commands work.
172 VisibleSelection selection(frame().selection().selection());
173 Position spellingSearchStart, spellingSearchEnd;
174 Range::selectNodeContents(frame().document(), spellingSearchStart, spellingS earchEnd);
175
176 bool startedWithSelection = false;
177 if (selection.start().anchorNode()) {
178 startedWithSelection = true;
179 if (startBeforeSelection) {
180 VisiblePosition start(selection.visibleStart());
181 // We match AppKit's rule: Start 1 character before the selection.
182 VisiblePosition oneBeforeStart = start.previous();
183 spellingSearchStart = (oneBeforeStart.isNotNull() ? oneBeforeStart : start).toParentAnchoredPosition();
184 } else {
185 spellingSearchStart = selection.visibleEnd().toParentAnchoredPositio n();
186 }
187 }
188
189 Position position = spellingSearchStart;
190 if (!isEditablePosition(position)) {
191 // This shouldn't happen in very often because the Spelling menu items a ren't enabled unless the
192 // selection is editable.
193 // This can happen in Mail for a mix of non-editable and editable conten t (like Stationary),
194 // when spell checking the whole document before sending the message.
195 // In that case the document might not be editable, but there are editab le pockets that need to be spell checked.
196
197 position = firstEditableVisiblePositionAfterPositionInRoot(position, fra me().document()->documentElement()).deepEquivalent();
198 if (position.isNull())
199 return;
200
201 spellingSearchStart = position.parentAnchoredEquivalent();
202 startedWithSelection = false; // won't need to wrap
203 }
204
205 // topNode defines the whole range we want to operate on
206 ContainerNode* topNode = highestEditableRoot(position);
207 // TODO(yosin): |lastOffsetForEditing()| is wrong here if
208 // |editingIgnoresContent(highestEditableRoot())| returns true, e.g. <table>
209 spellingSearchEnd = createLegacyEditingPosition(topNode, EditingStrategy::la stOffsetForEditing(topNode));
210
211 // If spellingSearchRange starts in the middle of a word, advance to the nex t word so we start checking
212 // at a word boundary. Going back by one char and then forward by a word doe s the trick.
213 if (startedWithSelection) {
214 VisiblePosition oneBeforeStart = VisiblePosition(spellingSearchStart, DOWNST REAM).previous();
215 if (oneBeforeStart.isNotNull())
216 spellingSearchStart = endOfWord(oneBeforeStart).toParentAnchoredPosi tion();
217 // else we were already at the start of the editable node
218 }
219
220 if (spellingSearchStart == spellingSearchEnd)
221 return; // nothing to search in
222
223 // We go to the end of our first range instead of the start of it, just to b e sure
224 // we don't get foiled by any word boundary problems at the start. It means we might
225 // do a tiny bit more searching.
226 Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode();
227 int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode();
228
229 int misspellingOffset = 0;
230 GrammarDetail grammarDetail;
231 int grammarPhraseOffset = 0;
232 Position grammarSearchStart, grammarSearchEnd;
233 String badGrammarPhrase;
234 String misspelledWord;
235
236 bool isSpelling = true;
237 int foundOffset = 0;
238 String foundItem;
239 RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
240 if (unifiedTextCheckerEnabled()) {
241 grammarSearchStart = spellingSearchStart;
242 grammarSearchEnd = spellingSearchEnd;
243 foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchStart , spellingSearchEnd).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled() , isSpelling, foundOffset, grammarDetail);
244 if (isSpelling) {
245 misspelledWord = foundItem;
246 misspellingOffset = foundOffset;
247 } else {
248 badGrammarPhrase = foundItem;
249 grammarPhraseOffset = foundOffset;
250 }
251 } else {
252 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearch Start, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false, firstMi sspellingRange);
253 grammarSearchStart = spellingSearchStart;
254 grammarSearchEnd = spellingSearchEnd;
255 if (!misspelledWord.isEmpty()) {
256 // Stop looking at start of next misspelled word
257 CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
258 chars.advance(misspellingOffset);
259 grammarSearchEnd = chars.startPosition();
260 }
261
262 if (isGrammarCheckingEnabled())
263 badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarS earchStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhraseOf fset, false);
264 }
265
266 // 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
267 // block rather than at a selection).
268 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
269 spellingSearchStart = createLegacyEditingPosition(topNode, 0);
270 // going until the end of the very first chunk we tested is far enough
271 spellingSearchEnd = createLegacyEditingPosition(searchEndNodeAfterWrap, searchEndOffsetAfterWrap);
272
273 if (unifiedTextCheckerEnabled()) {
274 grammarSearchStart = spellingSearchStart;
275 grammarSearchEnd = spellingSearchEnd;
276 foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchS tart, spellingSearchEnd).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabl ed(), isSpelling, foundOffset, grammarDetail);
277 if (isSpelling) {
278 misspelledWord = foundItem;
279 misspellingOffset = foundOffset;
280 } else {
281 badGrammarPhrase = foundItem;
282 grammarPhraseOffset = foundOffset;
283 }
284 } else {
285 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSe archStart, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false, fir stMisspellingRange);
286 grammarSearchStart = spellingSearchStart;
287 grammarSearchEnd = spellingSearchEnd;
288 if (!misspelledWord.isEmpty()) {
289 // Stop looking at start of next misspelled word
290 CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
291 chars.advance(misspellingOffset);
292 grammarSearchEnd = chars.startPosition();
293 }
294
295 if (isGrammarCheckingEnabled())
296 badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), gram marSearchStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhra seOffset, false);
297 }
298 }
299
300 if (!badGrammarPhrase.isEmpty()) {
301 // We found bad grammar. Since we only searched for bad grammar up to th e first misspelled word, the bad grammar
302 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
303 // panel, and store a marker so we draw the green squiggle later.
304
305 ASSERT(badGrammarPhrase.length() > 0);
306 ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);
307
308 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
309 const EphemeralRange badGrammarRange = calculateCharacterSubrange(Epheme ralRange(grammarSearchStart, grammarSearchEnd), grammarPhraseOffset + grammarDet ail.location, grammarDetail.length);
310 frame().selection().setSelection(VisibleSelection(badGrammarRange));
311 frame().selection().revealSelection();
312
313 frame().document()->markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, grammarDetail.userDescr iption);
314 } else if (!misspelledWord.isEmpty()) {
315 // We found a misspelling, but not any earlier bad grammar. Select the m isspelling, update the spelling panel, and store
316 // a marker so we draw the red squiggle later.
317
318 const EphemeralRange misspellingRange = calculateCharacterSubrange(Ephem eralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelled Word.length());
319 frame().selection().setSelection(VisibleSelection(misspellingRange, DOWN STREAM));
320 frame().selection().revealSelection();
321
322 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord);
323 frame().document()->markers().addMarker(misspellingRange.startPosition() , misspellingRange.endPosition(), DocumentMarker::Spelling);
324 }
325 }
326
327 void SpellChecker::showSpellingGuessPanel()
328 {
329 if (spellCheckerClient().spellingUIIsShowing()) {
330 spellCheckerClient().showSpellingUI(false);
331 return;
332 }
333
334 advanceToNextMisspelling(true);
335 spellCheckerClient().showSpellingUI(true);
336 }
337
338 void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &moving Selection)
339 {
340 removeMarkers(movingSelection, DocumentMarker::MisspellingMarkers());
341 }
342
343 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingS election)
344 {
345 markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnab led() && isGrammarCheckingEnabled(), movingSelection);
346 }
347
348 void SpellChecker::markMisspellingsAfterLineBreak(const VisibleSelection& wordSe lection)
349 {
350 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterLineBreak");
351
352 if (unifiedTextCheckerEnabled()) {
353 TextCheckingTypeMask textCheckingOptions = 0;
354
355 if (isContinuousSpellCheckingEnabled())
356 textCheckingOptions |= TextCheckingTypeSpelling;
357
358 if (isGrammarCheckingEnabled())
359 textCheckingOptions |= TextCheckingTypeGrammar;
360
361 VisibleSelection wholeParagraph(
362 startOfParagraph(wordSelection.visibleStart()),
363 endOfParagraph(wordSelection.visibleEnd()));
364
365 markAllMisspellingsAndBadGrammarInRanges(
366 textCheckingOptions, wordSelection.toNormalizedRange().get(),
367 wholeParagraph.toNormalizedRange().get());
368 } else {
369 RefPtrWillBeRawPtr<Range> misspellingRange = nullptr;
370 markMisspellings(wordSelection, misspellingRange);
371 }
372 }
373
374 void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &word Start, const VisibleSelection& selectionAfterTyping)
375 {
376 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterTypingToWord");
377
378 if (unifiedTextCheckerEnabled()) {
379 TextCheckingTypeMask textCheckingOptions = 0;
380
381 if (isContinuousSpellCheckingEnabled())
382 textCheckingOptions |= TextCheckingTypeSpelling;
383
384 if (!(textCheckingOptions & TextCheckingTypeSpelling))
385 return;
386
387 if (isGrammarCheckingEnabled())
388 textCheckingOptions |= TextCheckingTypeGrammar;
389
390 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
391 if (textCheckingOptions & TextCheckingTypeGrammar) {
392 VisibleSelection selectedSentence = VisibleSelection(startOfSentence (wordStart), endOfSentence(wordStart));
393 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjace ntWords.toNormalizedRange().get(), selectedSentence.toNormalizedRange().get());
394 } else {
395 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjace ntWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
396 }
397 return;
398 }
399
400 if (!isContinuousSpellCheckingEnabled())
401 return;
402
403 // Check spelling of one word
404 RefPtrWillBeRawPtr<Range> misspellingRange = nullptr;
405 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundar y), endOfWord(wordStart, RightWordIfOnBoundary)), misspellingRange);
406
407 if (!misspellingRange || !isGrammarCheckingEnabled())
408 return;
409
410 // Check grammar of entire sentence
411 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wo rdStart)));
412 }
413
414 void SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selectio n, bool checkSpelling, RefPtrWillBeRawPtr<Range>& firstMisspellingRange)
415 {
416 // This function is called with a selection already expanded to word boundar ies.
417 // Might be nice to assert that here.
418
419 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
420 // grammar checking can only be on if spell checking is also on.
421 if (!isContinuousSpellCheckingEnabled())
422 return;
423
424 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsOrBadGrammar");
425
426 const EphemeralRange range = selection.toNormalizedEphemeralRange();
427 if (range.isNull())
428 return;
429
430 // If we're not in an editable node, bail.
431 Node* editableNode = range.startPosition().computeContainerNode();
432 if (!editableNode || !editableNode->hasEditableStyle())
433 return;
434
435 if (!isSpellCheckingEnabledFor(editableNode))
436 return;
437
438 TextCheckingHelper checker(spellCheckerClient(), range.startPosition(), rang e.endPosition());
439 if (checkSpelling)
440 checker.markAllMisspellings(firstMisspellingRange);
441 else if (isGrammarCheckingEnabled())
442 checker.markAllBadGrammar();
443 }
444
445 bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const
446 {
447 if (!node)
448 return false;
449 const Element* focusedElement = node->isElementNode() ? toElement(node) : no de->parentElement();
450 if (!focusedElement)
451 return false;
452 return focusedElement->isSpellCheckingEnabled();
453 }
454
455 bool SpellChecker::isSpellCheckingEnabledInFocusedNode() const
456 {
457 return isSpellCheckingEnabledFor(frame().selection().start().anchorNode());
458 }
459
460 void SpellChecker::markMisspellings(const VisibleSelection& selection, RefPtrWil lBeRawPtr<Range>& firstMisspellingRange)
461 {
462 markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
463 }
464
465 void SpellChecker::markBadGrammar(const VisibleSelection& selection)
466 {
467 RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
468 markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
469 }
470
471 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
472 {
473 ASSERT(unifiedTextCheckerEnabled());
474
475 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
476
477 // This function is called with selections already expanded to word boundari es.
478 if (!spellingRange || (shouldMarkGrammar && !grammarRange))
479 return;
480
481 // If we're not in an editable node, bail.
482 Node* editableNode = spellingRange->startContainer();
483 if (!editableNode || !editableNode->hasEditableStyle())
484 return;
485
486 if (!isSpellCheckingEnabledFor(editableNode))
487 return;
488
489 Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
490 TextCheckingParagraph fullParagraphToCheck(rangeToCheck);
491
492 bool asynchronous = frame().settings() && frame().settings()->asynchronousSp ellCheckingEnabled();
493 chunkAndMarkAllMisspellingsAndBadGrammar(textCheckingOptions, fullParagraphT oCheck, asynchronous);
494 }
495
496 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node)
497 {
498 TRACE_EVENT0("blink", "SpellChecker::chunkAndMarkAllMisspellingsAndBadGramma r");
499 if (!node)
500 return;
501 RefPtrWillBeRawPtr<Range> rangeToCheck = Range::create(*frame().document(), firstPositionInNode(node), lastPositionInNode(node));
502 TextCheckingParagraph textToCheck(rangeToCheck, rangeToCheck);
503 bool asynchronous = true;
504 chunkAndMarkAllMisspellingsAndBadGrammar(resolveTextCheckingTypeMask(TextChe ckingTypeSpelling | TextCheckingTypeGrammar), textToCheck, asynchronous);
505 }
506
507 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(TextCheckingTypeMask textCheckingOptions, const TextCheckingParagraph& fullParagraphToCheck, bool as ynchronous)
508 {
509 if (fullParagraphToCheck.isRangeEmpty() || fullParagraphToCheck.isEmpty())
510 return;
511
512 // Since the text may be quite big chunk it up and adjust to the sentence bo undary.
513 const int kChunkSize = 16 * 1024;
514 int start = fullParagraphToCheck.checkingStart();
515 int end = fullParagraphToCheck.checkingEnd();
516 start = std::min(start, end);
517 end = std::max(start, end);
518 const int kNumChunksToCheck = asynchronous ? (end - start + kChunkSize - 1) / (kChunkSize) : 1;
519 int currentChunkStart = start;
520 RefPtrWillBeRawPtr<Range> checkRange = fullParagraphToCheck.checkingRange();
521 if (kNumChunksToCheck == 1 && asynchronous) {
522 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange .get(), checkRange.get(), asynchronous, 0);
523 return;
524 }
525
526 for (int iter = 0; iter < kNumChunksToCheck; ++iter) {
527 checkRange = fullParagraphToCheck.subrange(currentChunkStart, kChunkSize );
528 expandRangeToSentenceBoundary(*checkRange);
529
530 int checkingLength = 0;
531 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, checkRange .get(), checkRange.get(), asynchronous, iter, &checkingLength);
532 currentChunkStart += checkingLength;
533 }
534 }
535
536 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* checkRange, Range* paragraphRange, bool asynchronou s, int requestNumber, int* checkingLength)
537 {
538 TextCheckingParagraph sentenceToCheck(checkRange, paragraphRange);
539 if (checkingLength)
540 *checkingLength = sentenceToCheck.checkingLength();
541
542 RefPtrWillBeRawPtr<SpellCheckRequest> request = SpellCheckRequest::create(re solveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessBatch, checkR ange, paragraphRange, requestNumber);
543 if (!request)
544 return;
545
546 if (asynchronous) {
547 m_spellCheckRequester->requestCheckingFor(request);
548 } else {
549 Vector<TextCheckingResult> results;
550 checkTextOfParagraph(textChecker(), sentenceToCheck.text(), resolveTextC heckingTypeMask(textCheckingOptions), results);
551 markAndReplaceFor(request, results);
552 }
553 }
554
555 void SpellChecker::markAndReplaceFor(PassRefPtrWillBeRawPtr<SpellCheckRequest> r equest, const Vector<TextCheckingResult>& results)
556 {
557 TRACE_EVENT0("blink", "SpellChecker::markAndReplaceFor");
558 ASSERT(request);
559
560 TextCheckingTypeMask textCheckingOptions = request->data().mask();
561 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraph Range());
562
563 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
564 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
565
566 // Expand the range to encompass entire paragraphs, since text checking need s that much context.
567 int selectionOffset = 0;
568 int ambiguousBoundaryOffset = -1;
569 bool selectionChanged = false;
570 bool restoreSelectionAfterChange = false;
571 bool adjustSelectionForParagraphBoundaries = false;
572
573 if (shouldMarkSpelling) {
574 if (frame().selection().isCaret()) {
575 // Attempt to save the caret position so we can restore it later if needed
576 Position caretPosition = frame().selection().end();
577 selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPT ION);
578 restoreSelectionAfterChange = true;
579 if (selectionOffset > 0 && (static_cast<unsigned>(selectionOffset) > paragraph.text().length() || paragraph.textCharAt(selectionOffset - 1) == newli neCharacter))
580 adjustSelectionForParagraphBoundaries = true;
581 if (selectionOffset > 0 && static_cast<unsigned>(selectionOffset) <= paragraph.text().length() && isAmbiguousBoundaryCharacter(paragraph.textCharAt( selectionOffset - 1)))
582 ambiguousBoundaryOffset = selectionOffset - 1;
583 }
584 }
585
586 for (unsigned i = 0; i < results.size(); i++) {
587 int spellingRangeEndOffset = paragraph.checkingEnd();
588 const TextCheckingResult* result = &results[i];
589 int resultLocation = result->location + paragraph.checkingStart();
590 int resultLength = result->length;
591 bool resultEndsAtAmbiguousBoundary = ambiguousBoundaryOffset >= 0 && res ultLocation + resultLength == ambiguousBoundaryOffset;
592
593 // Only mark misspelling if:
594 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
595 // 2. Result falls within spellingRange.
596 // 3. The word in question doesn't end at an ambiguous boundary. For ins tance, we would not mark
597 // "wouldn'" as misspelled right after apostrophe is typed.
598 if (shouldMarkSpelling && result->decoration == TextDecorationTypeSpelli ng && resultLocation >= paragraph.checkingStart() && resultLocation + resultLeng th <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
599 ASSERT(resultLength > 0 && resultLocation >= 0);
600 const EphemeralRange misspellingRange = calculateCharacterSubrange(E phemeralRange(paragraph.paragraphRange().get()), resultLocation, resultLength);
601 frame().document()->markers().addMarker(misspellingRange.startPositi on(), misspellingRange.endPosition(), DocumentMarker::Spelling, result->replacem ent, result->hash);
602 } else if (shouldMarkGrammar && result->decoration == TextDecorationType Grammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
603 ASSERT(resultLength > 0 && resultLocation >= 0);
604 for (unsigned j = 0; j < result->details.size(); j++) {
605 const GrammarDetail* detail = &result->details[j];
606 ASSERT(detail->length > 0 && detail->location >= 0);
607 if (paragraph.checkingRangeCovers(resultLocation + detail->locat ion, detail->length)) {
608 const EphemeralRange badGrammarRange = calculateCharacterSub range(EphemeralRange(paragraph.paragraphRange().get()), resultLocation + detail- >location, detail->length);
609 frame().document()->markers().addMarker(badGrammarRange.star tPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail->use rDescription, result->hash);
610 }
611 }
612 } else if (result->decoration == TextDecorationTypeInvisibleSpellcheck & & resultLocation >= paragraph.checkingStart() && resultLocation + resultLength < = spellingRangeEndOffset) {
613 ASSERT(resultLength > 0 && resultLocation >= 0);
614 const EphemeralRange invisibleSpellcheckRange = calculateCharacterSu brange(EphemeralRange(paragraph.paragraphRange().get()), resultLocation, resultL ength);
615 frame().document()->markers().addMarker(invisibleSpellcheckRange.sta rtPosition(), invisibleSpellcheckRange.endPosition(), DocumentMarker::InvisibleS pellcheck, result->replacement, result->hash);
616 }
617 }
618
619 if (selectionChanged) {
620 TextCheckingParagraph extendedParagraph(paragraph);
621 // Restore the caret position if we have made any replacements
622 extendedParagraph.expandRangeToNextEnd();
623 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffs et <= extendedParagraph.rangeLength()) {
624 RefPtrWillBeRawPtr<Range> selectionRange = extendedParagraph.subrang e(0, selectionOffset);
625 frame().selection().moveTo(selectionRange->endPosition(), DOWNSTREAM );
626 if (adjustSelectionForParagraphBoundaries)
627 frame().selection().modify(FrameSelection::AlterationMove, Direc tionForward, CharacterGranularity);
628 } else {
629 // If this fails for any reason, the fallback is to go one position beyond the last replacement
630 frame().selection().moveTo(frame().selection().selection().visibleEn d());
631 frame().selection().modify(FrameSelection::AlterationMove, Direction Forward, CharacterGranularity);
632 }
633 }
634 }
635
636 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection& spellin gSelection, bool markGrammar, const VisibleSelection& grammarSelection)
637 {
638 if (unifiedTextCheckerEnabled()) {
639 if (!isContinuousSpellCheckingEnabled())
640 return;
641
642 // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
643 TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
644 if (markGrammar && isGrammarCheckingEnabled())
645 textCheckingOptions |= TextCheckingTypeGrammar;
646 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSe lection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
647 return;
648 }
649
650 RefPtrWillBeRawPtr<Range> firstMisspellingRange = nullptr;
651 markMisspellings(spellingSelection, firstMisspellingRange);
652 if (markGrammar)
653 markBadGrammar(grammarSelection);
654 }
655
656 void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSele ctionAtWordBoundary)
657 {
658 if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpe lling))
659 return;
660
661 TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing" );
662
663 // We want to remove the markers from a word if an editing command will chan ge the word. This can happen in one of
664 // several scenarios:
665 // 1. Insert in the middle of a word.
666 // 2. Appending non whitespace at the beginning of word.
667 // 3. Appending non whitespace at the end of word.
668 // Note that, appending only whitespaces at the beginning or end of word won 't change the word, so we don't need to
669 // remove the markers on that word.
670 // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of
671 // selection, and remove words between the selection boundaries.
672 //
673 VisiblePosition startOfSelection = frame().selection().selection().visibleSt art();
674 VisiblePosition endOfSelection = frame().selection().selection().visibleEnd( );
675 if (startOfSelection.isNull())
676 return;
677 // First word is the word that ends after or on the start of selection.
678 VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfO nBoundary);
679 VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBou ndary);
680 // Last word is the word that begins before or on the end of selection
681 VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnB oundary);
682 VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBound ary);
683
684 if (startOfFirstWord.isNull()) {
685 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
686 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
687 }
688
689 if (endOfLastWord.isNull()) {
690 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
691 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
692 }
693
694 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at t he start of selection,
695 // we choose next word as the first word.
696 if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) {
697 startOfFirstWord = nextWordPosition(startOfFirstWord);
698 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
699 if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent() )
700 return;
701 }
702
703 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection,
704 // we choose previous word as the last word.
705 if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) {
706 startOfLastWord = previousWordPosition(startOfLastWord);
707 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
708 if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent())
709 return;
710 }
711
712 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord. isNull() || endOfLastWord.isNull())
713 return;
714
715 // Now we remove markers on everything between startOfFirstWord and endOfLas tWord.
716 // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the
717 // resulted words even we only edit one of them. For example, assuming autoc orrection changes "avantgarde" to "avant
718 // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde,
719 // we would like to remove the marker from word "avant" and whitespace as we ll. So we need to get the continous range of
720 // of marker that contains the word in question, and remove marker on that w hole range.
721 Document* document = frame().document();
722 ASSERT(document);
723 Node* startNode = startOfFirstWord.deepEquivalent().computeContainerNode();
724 int startOffset = startOfFirstWord.deepEquivalent().computeOffsetInContainer Node();
725 int endOffset = endOfLastWord.deepEquivalent().computeOffsetInContainerNode( );
726 document->markers().removeMarkers(startNode, startOffset, endOffset - startO ffset, DocumentMarker::MisspellingMarkers(), DocumentMarkerController::RemovePar tiallyOverlappingMarker);
727 }
728
729 void SpellChecker::didEndEditingOnTextField(Element* e)
730 {
731 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField");
732
733 // Remove markers when deactivating a selection in an <input type="text"/>.
734 // Prevent new ones from appearing too.
735 m_spellCheckRequester->cancelCheck();
736 HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlEl ement(e);
737 HTMLElement* innerEditor = textFormControlElement->innerEditorElement();
738 DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling);
739 if (isGrammarCheckingEnabled() || unifiedTextCheckerEnabled())
740 markerTypes.add(DocumentMarker::Grammar);
741 for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor))
742 frame().document()->markers().removeMarkers(&node, markerTypes);
743 }
744
745 void SpellChecker::replaceMisspelledRange(const String& text)
746 {
747 EphemeralRange caretRange = frame().selection().selection().toNormalizedEphe meralRange();
748 if (caretRange.isNull())
749 return;
750 DocumentMarkerVector markers = frame().document()->markers().markersInRange( caretRange, DocumentMarker::MisspellingMarkers());
751 if (markers.size() < 1 || markers[0]->startOffset() >= markers[0]->endOffset ())
752 return;
753 EphemeralRange markerRange = EphemeralRange(Position(caretRange.startPositio n().computeContainerNode(), markers[0]->startOffset()), Position(caretRange.endP osition().computeContainerNode(), markers[0]->endOffset()));
754 if (markerRange.isNull())
755 return;
756 frame().selection().setSelection(VisibleSelection(markerRange), CharacterGra nularity);
757 frame().editor().replaceSelectionWithText(text, false, false);
758 }
759
760 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelectio n, FrameSelection::SetSelectionOptions options)
761 {
762 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection");
763
764 bool closeTyping = options & FrameSelection::CloseTyping;
765 bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabl ed();
766 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled & & isGrammarCheckingEnabled();
767 if (isContinuousSpellCheckingEnabled) {
768 VisibleSelection newAdjacentWords;
769 VisibleSelection newSelectedSentence;
770 bool caretBrowsing = frame().settings() && frame().settings()->caretBrow singEnabled();
771 const VisibleSelection newSelection = frame().selection().selection();
772 if (isSelectionInTextFormControl(newSelection)) {
773 Position newStart = newSelection.start();
774 newAdjacentWords.setWithoutValidation(HTMLTextFormControlElement::st artOfWord(newStart), HTMLTextFormControlElement::endOfWord(newStart));
775 if (isContinuousGrammarCheckingEnabled)
776 newSelectedSentence.setWithoutValidation(HTMLTextFormControlElem ent::startOfSentence(newStart), HTMLTextFormControlElement::endOfSentence(newSta rt));
777 } else if (newSelection.isContentEditable() || caretBrowsing) {
778 VisiblePosition newStart(newSelection.visibleStart());
779 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIf OnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
780 if (isContinuousGrammarCheckingEnabled)
781 newSelectedSentence = VisibleSelection(startOfSentence(newStart) , endOfSentence(newStart));
782 }
783
784 // Don't check spelling and grammar if the change of selection is trigge red by spelling correction itself.
785 bool shouldCheckSpellingAndGrammar = !(options & FrameSelection::SpellCo rrectionTriggered);
786
787 // When typing we check spelling elsewhere, so don't redo it here.
788 // If this is a change in selection resulting from a delete operation,
789 // oldSelection may no longer be in the document.
790 // FIXME(http://crbug.com/382809): if oldSelection is on a textarea
791 // element, we cause synchronous layout.
792 if (shouldCheckSpellingAndGrammar
793 && closeTyping
794 && !isSelectionInTextField(oldSelection)
795 && (isSelectionInTextArea(oldSelection) || oldSelection.isContentEdi table())
796 && oldSelection.start().inDocument()) {
797 spellCheckOldSelection(oldSelection, newAdjacentWords);
798 }
799
800 // FIXME(http://crbug.com/382809):
801 // shouldEraseMarkersAfterChangeSelection is true, we cause synchronous
802 // layout.
803 if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTyp eSpelling))
804 removeMarkers(newAdjacentWords, DocumentMarker::Spelling);
805 if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTyp eGrammar))
806 removeMarkers(newSelectedSentence, DocumentMarker::Grammar);
807 }
808
809 // When continuous spell checking is off, existing markers disappear after t he selection changes.
810 if (!isContinuousSpellCheckingEnabled)
811 frame().document()->markers().removeMarkers(DocumentMarker::Spelling);
812 if (!isContinuousGrammarCheckingEnabled)
813 frame().document()->markers().removeMarkers(DocumentMarker::Grammar);
814 }
815
816 void SpellChecker::removeSpellingMarkers()
817 {
818 frame().document()->markers().removeMarkers(DocumentMarker::MisspellingMarke rs());
819 }
820
821 void SpellChecker::removeSpellingMarkersUnderWords(const Vector<String>& words)
822 {
823 MarkerRemoverPredicate removerPredicate(words);
824
825 DocumentMarkerController& markerController = frame().document()->markers();
826 markerController.removeMarkers(removerPredicate);
827 markerController.repaintMarkers();
828 }
829
830 void SpellChecker::spellCheckAfterBlur()
831 {
832 if (!frame().selection().selection().isContentEditable())
833 return;
834
835 if (isSelectionInTextField(frame().selection().selection())) {
836 // textFieldDidEndEditing() and textFieldDidBeginEditing() handle this.
837 return;
838 }
839
840 VisibleSelection empty;
841 spellCheckOldSelection(frame().selection().selection(), empty);
842 }
843
844 void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords)
845 {
846 TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection");
847
848 VisiblePosition oldStart(oldSelection.visibleStart());
849 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, L eftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
850 if (!VisibleSelection::InDOMTree::equalSelections(oldAdjacentWords, newAdjac entWords)) {
851 if (isContinuousSpellCheckingEnabled() && isGrammarCheckingEnabled()) {
852 VisibleSelection selectedSentence = VisibleSelection(startOfSentence (oldStart), endOfSentence(oldStart));
853 markMisspellingsAndBadGrammar(oldAdjacentWords, true, selectedSenten ce);
854 } else {
855 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWo rds);
856 }
857 }
858 }
859
860 static Node* findFirstMarkable(Node* node)
861 {
862 while (node) {
863 if (!node->layoutObject())
864 return 0;
865 if (node->layoutObject()->isText())
866 return node;
867 if (node->layoutObject()->isTextControl())
868 node = toLayoutTextControl(node->layoutObject())->textFormControlEle ment()->visiblePositionForIndex(1).deepEquivalent().anchorNode();
869 else if (node->hasChildren())
870 node = node->firstChild();
871 else
872 node = node->nextSibling();
873 }
874
875 return 0;
876 }
877
878 bool SpellChecker::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerT ype, int from, int length) const
879 {
880 Node* node = findFirstMarkable(frame().selection().start().anchorNode());
881 if (!node)
882 return false;
883
884 unsigned startOffset = static_cast<unsigned>(from);
885 unsigned endOffset = static_cast<unsigned>(from + length);
886 DocumentMarkerVector markers = frame().document()->markers().markersFor(node );
887 for (size_t i = 0; i < markers.size(); ++i) {
888 DocumentMarker* marker = markers[i];
889 if (marker->startOffset() <= startOffset && endOffset <= marker->endOffs et() && marker->type() == markerType)
890 return true;
891 }
892
893 return false;
894 }
895
896 bool SpellChecker::selectionStartHasSpellingMarkerFor(int from, int length) cons t
897 {
898 return selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length);
899 }
900
901 TextCheckingTypeMask SpellChecker::resolveTextCheckingTypeMask(TextCheckingTypeM ask textCheckingOptions)
902 {
903 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
904 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
905
906 TextCheckingTypeMask checkingTypes = 0;
907 if (shouldMarkSpelling)
908 checkingTypes |= TextCheckingTypeSpelling;
909 if (shouldMarkGrammar)
910 checkingTypes |= TextCheckingTypeGrammar;
911
912 return checkingTypes;
913 }
914
915 void SpellChecker::removeMarkers(const VisibleSelection& selection, DocumentMark er::MarkerTypes markerTypes)
916 {
917 const EphemeralRange range = selection.toNormalizedEphemeralRange();
918 if (range.isNull())
919 return;
920 frame().document()->markers().removeMarkers(range, markerTypes);
921 }
922
923 bool SpellChecker::unifiedTextCheckerEnabled() const
924 {
925 return blink::unifiedTextCheckerEnabled(m_frame);
926 }
927
928 void SpellChecker::cancelCheck()
929 {
930 m_spellCheckRequester->cancelCheck();
931 }
932
933 void SpellChecker::requestTextChecking(const Element& element)
934 {
935 const EphemeralRange range = EphemeralRange::rangeOfContents(element);
936 RefPtrWillBeRawPtr<Range> rangeToCheck = Range::create(element.document(), r ange.startPosition(), range.endPosition());
937 m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextChec kingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToChe ck, rangeToCheck));
938 }
939
940 DEFINE_TRACE(SpellChecker)
941 {
942 visitor->trace(m_frame);
943 visitor->trace(m_spellCheckRequester);
944 }
945
946 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/editing/SpellChecker.h ('k') | Source/core/editing/TextCheckingHelper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698