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

Side by Side Diff: third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp

Issue 2202463002: ALL-IN-ONE Stop SpellChecker from checking or marking grammar (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix layout test accessibility/misspellings.html Created 4 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
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 if (spellingSearchStart == spellingSearchEnd) 219 if (spellingSearchStart == spellingSearchEnd)
220 return; // nothing to search in 220 return; // nothing to search in
221 221
222 // We go to the end of our first range instead of the start of it, just to b e sure 222 // We go to the end of our first range instead of the start of it, just to b e sure
223 // we don't get foiled by any word boundary problems at the start. It means we might 223 // we don't get foiled by any word boundary problems at the start. It means we might
224 // do a tiny bit more searching. 224 // do a tiny bit more searching.
225 Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode(); 225 Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode();
226 int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode(); 226 int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode();
227 227
228 int misspellingOffset = 0; 228 int misspellingOffset = 0;
229 GrammarDetail grammarDetail; 229 String misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSea rchStart, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false);
230 int grammarPhraseOffset = 0;
231 Position grammarSearchStart, grammarSearchEnd;
232 String badGrammarPhrase;
233 String misspelledWord;
234 230
235 bool isSpelling = true; 231 // If we did not find a misspelled word, wrap and try again (but don't bothe r if we started at the beginning of the
236 int foundOffset = 0;
237 String foundItem;
238 if (unifiedTextCheckerEnabled()) {
239 grammarSearchStart = spellingSearchStart;
240 grammarSearchEnd = spellingSearchEnd;
241 foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchStart , spellingSearchEnd).findFirstMisspellingOrBadGrammar(isSpelling, foundOffset, g rammarDetail);
242 if (isSpelling) {
243 misspelledWord = foundItem;
244 misspellingOffset = foundOffset;
245 } else {
246 badGrammarPhrase = foundItem;
247 grammarPhraseOffset = foundOffset;
248 }
249 } else {
250 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearch Start, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false);
251 grammarSearchStart = spellingSearchStart;
252 grammarSearchEnd = spellingSearchEnd;
253 if (!misspelledWord.isEmpty()) {
254 // Stop looking at start of next misspelled word
255 CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
256 chars.advance(misspellingOffset);
257 grammarSearchEnd = chars.startPosition();
258 }
259
260 badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearc hStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhraseOffset , false);
261 }
262
263 // 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
264 // block rather than at a selection). 232 // block rather than at a selection).
265 if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { 233 if (startedWithSelection && !misspelledWord) {
266 spellingSearchStart = Position::editingPositionOf(topNode, 0); 234 spellingSearchStart = Position::editingPositionOf(topNode, 0);
267 // going until the end of the very first chunk we tested is far enough 235 // going until the end of the very first chunk we tested is far enough
268 spellingSearchEnd = Position::editingPositionOf(searchEndNodeAfterWrap, searchEndOffsetAfterWrap); 236 spellingSearchEnd = Position::editingPositionOf(searchEndNodeAfterWrap, searchEndOffsetAfterWrap);
269 237
270 if (unifiedTextCheckerEnabled()) { 238 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearch Start, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false);
271 grammarSearchStart = spellingSearchStart;
272 grammarSearchEnd = spellingSearchEnd;
273 foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchS tart, spellingSearchEnd).findFirstMisspellingOrBadGrammar(isSpelling, foundOffse t, grammarDetail);
274 if (isSpelling) {
275 misspelledWord = foundItem;
276 misspellingOffset = foundOffset;
277 } else {
278 badGrammarPhrase = foundItem;
279 grammarPhraseOffset = foundOffset;
280 }
281 } else {
282 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSe archStart, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false);
283 grammarSearchStart = spellingSearchStart;
284 grammarSearchEnd = spellingSearchEnd;
285 if (!misspelledWord.isEmpty()) {
286 // Stop looking at start of next misspelled word
287 CharacterIterator chars(grammarSearchStart, grammarSearchEnd);
288 chars.advance(misspellingOffset);
289 grammarSearchEnd = chars.startPosition();
290 }
291
292 badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarS earchStart, grammarSearchEnd).findFirstBadGrammar(grammarDetail, grammarPhraseOf fset, false);
293 }
294 } 239 }
295 240
296 if (!badGrammarPhrase.isEmpty()) { 241 if (!misspelledWord.isEmpty()) {
297 // We found bad grammar. Since we only searched for bad grammar up to th e first misspelled word, the bad grammar 242 // We found a misspelling. Select the misspelling, update the spelling p anel, and store
298 // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
299 // panel, and store a marker so we draw the green squiggle later.
300
301 DCHECK_GT(badGrammarPhrase.length(), 0u);
302 DCHECK_NE(grammarDetail.location, -1);
303 DCHECK_GT(grammarDetail.length, 0);
304
305 // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
306 const EphemeralRange badGrammarRange = calculateCharacterSubrange(Epheme ralRange(grammarSearchStart, grammarSearchEnd), grammarPhraseOffset + grammarDet ail.location, grammarDetail.length);
307 frame().selection().setSelection(VisibleSelection(badGrammarRange));
308 frame().selection().revealSelection();
309 frame().document()->markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, grammarDetail.userDescr iption);
310 } else if (!misspelledWord.isEmpty()) {
311 // We found a misspelling, but not any earlier bad grammar. Select the m isspelling, update the spelling panel, and store
312 // a marker so we draw the red squiggle later. 243 // a marker so we draw the red squiggle later.
313 244
314 const EphemeralRange misspellingRange = calculateCharacterSubrange(Ephem eralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelled Word.length()); 245 const EphemeralRange misspellingRange = calculateCharacterSubrange(Ephem eralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelled Word.length());
315 frame().selection().setSelection(VisibleSelection(misspellingRange)); 246 frame().selection().setSelection(VisibleSelection(misspellingRange));
316 frame().selection().revealSelection(); 247 frame().selection().revealSelection();
317 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord); 248 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord);
318 frame().document()->markers().addMarker(misspellingRange.startPosition() , misspellingRange.endPosition(), DocumentMarker::Spelling); 249 frame().document()->markers().addMarker(misspellingRange.startPosition() , misspellingRange.endPosition(), DocumentMarker::Spelling);
319 } 250 }
320 } 251 }
321 252
322 void SpellChecker::showSpellingGuessPanel() 253 void SpellChecker::showSpellingGuessPanel()
323 { 254 {
324 if (spellCheckerClient().spellingUIIsShowing()) { 255 if (spellCheckerClient().spellingUIIsShowing()) {
325 spellCheckerClient().showSpellingUI(false); 256 spellCheckerClient().showSpellingUI(false);
326 return; 257 return;
327 } 258 }
328 259
329 advanceToNextMisspelling(true); 260 advanceToNextMisspelling(true);
330 spellCheckerClient().showSpellingUI(true); 261 spellCheckerClient().showSpellingUI(true);
331 } 262 }
332 263
333 void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &moving Selection) 264 void SpellChecker::clearMisspellingsAndBadGrammar(const VisibleSelection &moving Selection)
334 { 265 {
335 removeMarkers(movingSelection, DocumentMarker::MisspellingMarkers()); 266 removeMarkers(movingSelection, DocumentMarker::MisspellingMarkers());
336 } 267 }
337 268
338 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingS election) 269 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection &movingS election)
339 { 270 {
340 markMisspellingsAndBadGrammar(movingSelection, isContinuousSpellCheckingEnab led(), movingSelection); 271 if (!unifiedTextCheckerEnabled()) {
272 markMisspellings(movingSelection);
273 return;
274 }
275
276 if (!isContinuousSpellCheckingEnabled())
277 return;
278
279 // markMisspellings() is triggered by selection change, in which case we che ck spelling, but don't autocorrect misspellings.
280 markAllMisspellingsInRange(movingSelection.toNormalizedEphemeralRange());
341 } 281 }
342 282
343 void SpellChecker::markMisspellingsAfterLineBreak(const VisibleSelection& wordSe lection) 283 void SpellChecker::markMisspellingsAfterLineBreak(const VisibleSelection& wordSe lection)
344 { 284 {
345 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterLineBreak"); 285 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterLineBreak");
346 286
347 if (!unifiedTextCheckerEnabled()) { 287 if (!unifiedTextCheckerEnabled()) {
348 markMisspellings(wordSelection); 288 markMisspellings(wordSelection);
349 return; 289 return;
350 } 290 }
351 291
352 TextCheckingTypeMask textCheckingOptions = TextCheckingTypeGrammar;
353
354 if (isContinuousSpellCheckingEnabled()) 292 if (isContinuousSpellCheckingEnabled())
355 textCheckingOptions |= TextCheckingTypeSpelling; 293 markAllMisspellingsInRange(wordSelection.toNormalizedEphemeralRange());
356
357 VisibleSelection wholeParagraph(
358 startOfParagraph(wordSelection.visibleStart()),
359 endOfParagraph(wordSelection.visibleEnd()));
360
361 markAllMisspellingsAndBadGrammarInRanges(
362 textCheckingOptions, wordSelection.toNormalizedEphemeralRange(),
363 wholeParagraph.toNormalizedEphemeralRange());
364 } 294 }
365 295
366 void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &word Start, const VisibleSelection& selectionAfterTyping) 296 void SpellChecker::markMisspellingsAfterTypingToWord(const VisiblePosition &word Start, const VisibleSelection& selectionAfterTyping)
367 { 297 {
368 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterTypingToWord"); 298 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsAfterTypingToWord");
369 299
300 if (!isContinuousSpellCheckingEnabled())
301 return;
302
370 if (unifiedTextCheckerEnabled()) { 303 if (unifiedTextCheckerEnabled()) {
371 TextCheckingTypeMask textCheckingOptions = 0;
372
373 if (isContinuousSpellCheckingEnabled())
374 textCheckingOptions |= TextCheckingTypeSpelling;
375
376 if (!(textCheckingOptions & TextCheckingTypeSpelling))
377 return;
378
379 textCheckingOptions |= TextCheckingTypeGrammar;
380
381 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)); 304 VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
382 if (textCheckingOptions & TextCheckingTypeGrammar) { 305 markAllMisspellingsInRange(adjacentWords.toNormalizedEphemeralRange());
383 VisibleSelection selectedSentence = VisibleSelection(startOfSentence (wordStart), endOfSentence(wordStart));
384 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjace ntWords.toNormalizedEphemeralRange(), selectedSentence.toNormalizedEphemeralRang e());
385 } else {
386 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjace ntWords.toNormalizedEphemeralRange(), adjacentWords.toNormalizedEphemeralRange() );
387 }
388 return; 306 return;
389 } 307 }
390 308
391 if (!isContinuousSpellCheckingEnabled())
392 return;
393
394 // Check spelling of one word 309 // Check spelling of one word
395 bool result = markMisspellings(VisibleSelection(startOfWord(wordStart, LeftW ordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary))); 310 markMisspellings(VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundar y), endOfWord(wordStart, RightWordIfOnBoundary)));
396
397 if (!result)
398 return;
399
400 // Check grammar of entire sentence
401 markBadGrammar(VisibleSelection(startOfSentence(wordStart), endOfSentence(wo rdStart)));
402 }
403
404 bool SpellChecker::markMisspellingsOrBadGrammar(const VisibleSelection& selectio n, bool checkSpelling)
405 {
406 // This function is called with a selection already expanded to word boundar ies.
407 // Might be nice to assert that here.
408
409 // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
410 // grammar checking can only be on if spell checking is also on.
411 if (!isContinuousSpellCheckingEnabled())
412 return false;
413
414 TRACE_EVENT0("blink", "SpellChecker::markMisspellingsOrBadGrammar");
415
416 const EphemeralRange range = selection.toNormalizedEphemeralRange();
417 if (range.isNull())
418 return false;
419
420 // If we're not in an editable node, bail.
421 Node* editableNode = range.startPosition().computeContainerNode();
422 if (!editableNode || !hasEditableStyle(*editableNode))
423 return false;
424
425 if (!isSpellCheckingEnabledFor(editableNode))
426 return false;
427
428 TextCheckingHelper checker(spellCheckerClient(), range.startPosition(), rang e.endPosition());
429 if (checkSpelling)
430 return checker.markAllMisspellings();
431
432 checker.markAllBadGrammar();
433 return false;
434 } 311 }
435 312
436 bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const 313 bool SpellChecker::isSpellCheckingEnabledFor(Node* node) const
437 { 314 {
438 if (!node) 315 if (!node)
439 return false; 316 return false;
440 const Element* focusedElement = node->isElementNode() ? toElement(node) : no de->parentElement(); 317 const Element* focusedElement = node->isElementNode() ? toElement(node) : no de->parentElement();
441 if (!focusedElement) 318 if (!focusedElement)
442 return false; 319 return false;
443 return focusedElement->isSpellCheckingEnabled(); 320 return focusedElement->isSpellCheckingEnabled();
(...skipping 16 matching lines...) Expand all
460 } 337 }
461 if (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*sele ction.start().anchorNode())) { 338 if (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*sele ction.start().anchorNode())) {
462 if (element->spellcheck()) 339 if (element->spellcheck())
463 return true; 340 return true;
464 } 341 }
465 return false; 342 return false;
466 } 343 }
467 344
468 bool SpellChecker::markMisspellings(const VisibleSelection& selection) 345 bool SpellChecker::markMisspellings(const VisibleSelection& selection)
469 { 346 {
470 return markMisspellingsOrBadGrammar(selection, true); 347 // This function is called with a selection already expanded to word boundar ies.
348 // Might be nice to assert that here.
349
350 // This function is used only for as-you-type checking, so if that's off we do nothing.
351 if (!isContinuousSpellCheckingEnabled())
352 return false;
353
354 TRACE_EVENT0("blink", "SpellChecker::markMisspellings");
355
356 const EphemeralRange range = selection.toNormalizedEphemeralRange();
357 if (range.isNull())
358 return false;
359
360 // If we're not in an editable node, bail.
361 Node* editableNode = range.startPosition().computeContainerNode();
362 if (!editableNode || !hasEditableStyle(*editableNode))
363 return false;
364
365 if (!isSpellCheckingEnabledFor(editableNode))
366 return false;
367
368 TextCheckingHelper checker(spellCheckerClient(), range.startPosition(), rang e.endPosition());
369 return checker.markAllMisspellings();
471 } 370 }
472 371
473 void SpellChecker::markBadGrammar(const VisibleSelection& selection) 372 void SpellChecker::markAllMisspellingsInRange(const EphemeralRange& spellingRang e)
474 {
475 markMisspellingsOrBadGrammar(selection, false);
476 }
477
478 void SpellChecker::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, const EphemeralRange& spellingRange, const EphemeralRange& grammarRange)
479 { 373 {
480 DCHECK(unifiedTextCheckerEnabled()); 374 DCHECK(unifiedTextCheckerEnabled());
481 375
482 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
483
484 // This function is called with selections already expanded to word boundari es. 376 // This function is called with selections already expanded to word boundari es.
485 if (spellingRange.isNull() || (shouldMarkGrammar && grammarRange.isNull())) 377 if (spellingRange.isNull())
486 return; 378 return;
487 379
488 // If we're not in an editable node, bail. 380 // If we're not in an editable node, bail.
489 Node* editableNode = spellingRange.startPosition().computeContainerNode(); 381 Node* editableNode = spellingRange.startPosition().computeContainerNode();
490 if (!editableNode || !hasEditableStyle(*editableNode)) 382 if (!editableNode || !hasEditableStyle(*editableNode))
491 return; 383 return;
492 384
493 if (!isSpellCheckingEnabledFor(editableNode)) 385 if (!isSpellCheckingEnabledFor(editableNode))
494 return; 386 return;
495 387
496 TextCheckingParagraph fullParagraphToCheck(shouldMarkGrammar ? grammarRange : spellingRange); 388 TextCheckingParagraph fullParagraphToCheck(spellingRange);
497 chunkAndMarkAllMisspellingsAndBadGrammar(textCheckingOptions, fullParagraphT oCheck); 389 chunkAndMarkAllMisspellings(fullParagraphToCheck);
498 } 390 }
499 391
500 static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) 392 static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range)
501 { 393 {
502 DCHECK(range.isNotNull()); 394 DCHECK(range.isNotNull());
503 const VisiblePosition& visibleEnd = createVisiblePosition(range.endPosition( )); 395 const VisiblePosition& visibleEnd = createVisiblePosition(range.endPosition( ));
504 DCHECK(visibleEnd.isNotNull()); 396 DCHECK(visibleEnd.isNotNull());
505 const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent(); 397 const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent();
506 return EphemeralRange(range.startPosition(), sentenceEnd.isNotNull() ? sente nceEnd : range.endPosition()); 398 return EphemeralRange(range.startPosition(), sentenceEnd.isNotNull() ? sente nceEnd : range.endPosition());
507 } 399 }
508 400
509 static EphemeralRange expandRangeToSentenceBoundary(const EphemeralRange& range) 401 static EphemeralRange expandRangeToSentenceBoundary(const EphemeralRange& range)
510 { 402 {
511 DCHECK(range.isNotNull()); 403 DCHECK(range.isNotNull());
512 const VisiblePosition& visibleStart = createVisiblePosition(range.startPosit ion()); 404 const VisiblePosition& visibleStart = createVisiblePosition(range.startPosit ion());
513 DCHECK(visibleStart.isNotNull()); 405 DCHECK(visibleStart.isNotNull());
514 const Position& sentenceStart = startOfSentence(visibleStart).deepEquivalent (); 406 const Position& sentenceStart = startOfSentence(visibleStart).deepEquivalent ();
515 return expandEndToSentenceBoundary(EphemeralRange(sentenceStart.isNull() ? r ange.startPosition() : sentenceStart, range.endPosition())); 407 return expandEndToSentenceBoundary(EphemeralRange(sentenceStart.isNull() ? r ange.startPosition() : sentenceStart, range.endPosition()));
516 } 408 }
517 409
518 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node, const Ep hemeralRange& insertedRange) 410 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(Node* node, const Ep hemeralRange& insertedRange)
519 { 411 {
520 TRACE_EVENT0("blink", "SpellChecker::chunkAndMarkAllMisspellingsAndBadGramma r"); 412 TRACE_EVENT0("blink", "SpellChecker::chunkAndMarkAllMisspellingsAndBadGramma r");
521 if (!node) 413 if (!node)
522 return; 414 return;
523 EphemeralRange paragraphRange(Position::firstPositionInNode(node), Position: :lastPositionInNode(node)); 415 EphemeralRange paragraphRange(Position::firstPositionInNode(node), Position: :lastPositionInNode(node));
524 TextCheckingParagraph textToCheck(insertedRange, paragraphRange); 416 TextCheckingParagraph textToCheck(insertedRange, paragraphRange);
525 chunkAndMarkAllMisspellingsAndBadGrammar(resolveTextCheckingTypeMask(TextChe ckingTypeSpelling | TextCheckingTypeGrammar), textToCheck); 417 chunkAndMarkAllMisspellings(textToCheck);
526 } 418 }
527 419
528 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar(TextCheckingTypeMask textCheckingOptions, const TextCheckingParagraph& fullParagraphToCheck) 420 void SpellChecker::chunkAndMarkAllMisspellings(const TextCheckingParagraph& full ParagraphToCheck)
529 { 421 {
530 if (fullParagraphToCheck.isEmpty()) 422 if (fullParagraphToCheck.isEmpty())
531 return; 423 return;
532 const EphemeralRange& paragraphRange = fullParagraphToCheck.paragraphRange() ; 424 const EphemeralRange& paragraphRange = fullParagraphToCheck.paragraphRange() ;
533 425
534 // Since the text may be quite big chunk it up and adjust to the sentence bo undary. 426 // Since the text may be quite big chunk it up and adjust to the sentence bo undary.
535 const int kChunkSize = 16 * 1024; 427 const int kChunkSize = 16 * 1024;
536 428
537 // Check the full paragraph instead if the paragraph is short, which saves 429 // Check the full paragraph instead if the paragraph is short, which saves
538 // the cost on sentence boundary finding. 430 // the cost on sentence boundary finding.
539 if (fullParagraphToCheck.rangeLength() <= kChunkSize) { 431 if (fullParagraphToCheck.rangeLength() <= kChunkSize) {
540 SpellCheckRequest* request = SpellCheckRequest::create(resolveTextChecki ngTypeMask(textCheckingOptions), TextCheckingProcessBatch, paragraphRange, parag raphRange, 0); 432 SpellCheckRequest* request = SpellCheckRequest::create(TextCheckingTypeS pelling, TextCheckingProcessBatch, paragraphRange, paragraphRange, 0);
541 if (request) 433 if (request)
542 m_spellCheckRequester->requestCheckingFor(request); 434 m_spellCheckRequester->requestCheckingFor(request);
543 return; 435 return;
544 } 436 }
545 437
546 CharacterIterator checkRangeIterator(fullParagraphToCheck.checkingRange(), T extIteratorEmitsObjectReplacementCharacter); 438 CharacterIterator checkRangeIterator(fullParagraphToCheck.checkingRange(), T extIteratorEmitsObjectReplacementCharacter);
547 for (int requestNum = 0; !checkRangeIterator.atEnd(); requestNum++) { 439 for (int requestNum = 0; !checkRangeIterator.atEnd(); requestNum++) {
548 EphemeralRange chunkRange = checkRangeIterator.calculateCharacterSubrang e(0, kChunkSize); 440 EphemeralRange chunkRange = checkRangeIterator.calculateCharacterSubrang e(0, kChunkSize);
549 EphemeralRange checkRange = requestNum ? expandEndToSentenceBoundary(chu nkRange) : expandRangeToSentenceBoundary(chunkRange); 441 EphemeralRange checkRange = requestNum ? expandEndToSentenceBoundary(chu nkRange) : expandRangeToSentenceBoundary(chunkRange);
550 442
551 SpellCheckRequest* request = SpellCheckRequest::create(resolveTextChecki ngTypeMask(textCheckingOptions), TextCheckingProcessBatch, checkRange, paragraph Range, requestNum); 443 SpellCheckRequest* request = SpellCheckRequest::create(TextCheckingTypeS pelling, TextCheckingProcessBatch, checkRange, paragraphRange, requestNum);
552 if (request) 444 if (request)
553 m_spellCheckRequester->requestCheckingFor(request); 445 m_spellCheckRequester->requestCheckingFor(request);
554 446
555 if (!checkRangeIterator.atEnd()) { 447 if (!checkRangeIterator.atEnd()) {
556 checkRangeIterator.advance(1); 448 checkRangeIterator.advance(1);
557 // The layout should be already update due to the initialization of checkRangeIterator, 449 // The layout should be already update due to the initialization of checkRangeIterator,
558 // so comparePositions can be directly called. 450 // so comparePositions can be directly called.
559 if (comparePositions(chunkRange.endPosition(), checkRange.endPositio n()) < 0) 451 if (comparePositions(chunkRange.endPosition(), checkRange.endPositio n()) < 0)
560 checkRangeIterator.advance(TextIterator::rangeLength(chunkRange. endPosition(), checkRange.endPosition())); 452 checkRangeIterator.advance(TextIterator::rangeLength(chunkRange. endPosition(), checkRange.endPosition()));
561 } 453 }
(...skipping 13 matching lines...) Expand all
575 if (request->rootEditableElement()->document() != frame().selection().docume nt()) { 467 if (request->rootEditableElement()->document() != frame().selection().docume nt()) {
576 // we ignore |request| made for another document. 468 // we ignore |request| made for another document.
577 // "editing/spelling/spellcheck-sequencenum.html" and others reach here. 469 // "editing/spelling/spellcheck-sequencenum.html" and others reach here.
578 return; 470 return;
579 } 471 }
580 472
581 TextCheckingTypeMask textCheckingOptions = request->data().mask(); 473 TextCheckingTypeMask textCheckingOptions = request->data().mask();
582 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraph Range()); 474 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraph Range());
583 475
584 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling; 476 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
585 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
586 477
587 // Expand the range to encompass entire paragraphs, since text checking need s that much context. 478 // Expand the range to encompass entire paragraphs, since text checking need s that much context.
588 int selectionOffset = 0; 479 int selectionOffset = 0;
589 int ambiguousBoundaryOffset = -1; 480 int ambiguousBoundaryOffset = -1;
590 bool selectionChanged = false; 481 bool selectionChanged = false;
591 bool restoreSelectionAfterChange = false; 482 bool restoreSelectionAfterChange = false;
592 bool adjustSelectionForParagraphBoundaries = false; 483 bool adjustSelectionForParagraphBoundaries = false;
593 484
594 if (shouldMarkSpelling) { 485 if (shouldMarkSpelling) {
595 if (frame().selection().isCaret()) { 486 if (frame().selection().isCaret()) {
(...skipping 25 matching lines...) Expand all
621 // Only mark misspelling if: 512 // Only mark misspelling if:
622 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false. 513 // 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
623 // 2. Result falls within spellingRange. 514 // 2. Result falls within spellingRange.
624 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark 515 // 3. The word in question doesn't end at an ambiguous boundary. For instance, we would not mark
625 // "wouldn'" as misspelled right after apostrophe is typed. 516 // "wouldn'" as misspelled right after apostrophe is typed.
626 if (shouldMarkSpelling && result->decoration == TextDecorationTypeSp elling && resultLocation >= paragraph.checkingStart() && resultLocation + result Length <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) { 517 if (shouldMarkSpelling && result->decoration == TextDecorationTypeSp elling && resultLocation >= paragraph.checkingStart() && resultLocation + result Length <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
627 DCHECK_GT(resultLength, 0); 518 DCHECK_GT(resultLength, 0);
628 DCHECK_GE(resultLocation, 0); 519 DCHECK_GE(resultLocation, 0);
629 const EphemeralRange misspellingRange = calculateCharacterSubran ge(paragraph.paragraphRange(), resultLocation, resultLength); 520 const EphemeralRange misspellingRange = calculateCharacterSubran ge(paragraph.paragraphRange(), resultLocation, resultLength);
630 frame().document()->markers().addMarker(misspellingRange.startPo sition(), misspellingRange.endPosition(), DocumentMarker::Spelling, result->repl acement, result->hash); 521 frame().document()->markers().addMarker(misspellingRange.startPo sition(), misspellingRange.endPosition(), DocumentMarker::Spelling, result->repl acement, result->hash);
631 } else if (shouldMarkGrammar && result->decoration == TextDecoration TypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
632 DCHECK_GT(resultLength, 0);
633 DCHECK_GE(resultLocation, 0);
634 for (unsigned j = 0; j < result->details.size(); j++) {
635 const GrammarDetail* detail = &result->details[j];
636 DCHECK_GT(detail->length, 0);
637 DCHECK_GE(detail->location, 0);
638 if (paragraph.checkingRangeCovers(resultLocation + detail->l ocation, detail->length)) {
639 const EphemeralRange badGrammarRange = calculateCharacte rSubrange(paragraph.paragraphRange(), resultLocation + detail->location, detail- >length);
640 frame().document()->markers().addMarker(badGrammarRange. startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail- >userDescription, result->hash);
641 }
642 }
643 } else if (result->decoration == TextDecorationTypeInvisibleSpellche ck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLeng th <= spellingRangeEndOffset) { 522 } else if (result->decoration == TextDecorationTypeInvisibleSpellche ck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLeng th <= spellingRangeEndOffset) {
644 DCHECK_GT(resultLength, 0); 523 DCHECK_GT(resultLength, 0);
645 DCHECK_GE(resultLocation, 0); 524 DCHECK_GE(resultLocation, 0);
646 const EphemeralRange invisibleSpellcheckRange = calculateCharact erSubrange(paragraph.paragraphRange(), resultLocation, resultLength); 525 const EphemeralRange invisibleSpellcheckRange = calculateCharact erSubrange(paragraph.paragraphRange(), resultLocation, resultLength);
647 frame().document()->markers().addMarker(invisibleSpellcheckRange .startPosition(), invisibleSpellcheckRange.endPosition(), DocumentMarker::Invisi bleSpellcheck, result->replacement, result->hash); 526 frame().document()->markers().addMarker(invisibleSpellcheckRange .startPosition(), invisibleSpellcheckRange.endPosition(), DocumentMarker::Invisi bleSpellcheck, result->replacement, result->hash);
648 } 527 }
649 } 528 }
650 } 529 }
651 530
652 if (selectionChanged) { 531 if (selectionChanged) {
653 TextCheckingParagraph extendedParagraph(paragraph); 532 TextCheckingParagraph extendedParagraph(paragraph);
654 // Restore the caret position if we have made any replacements 533 // Restore the caret position if we have made any replacements
655 extendedParagraph.expandRangeToNextEnd(); 534 extendedParagraph.expandRangeToNextEnd();
656 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffs et <= extendedParagraph.rangeLength()) { 535 if (restoreSelectionAfterChange && selectionOffset >= 0 && selectionOffs et <= extendedParagraph.rangeLength()) {
657 EphemeralRange selectionRange = extendedParagraph.subrange(0, select ionOffset); 536 EphemeralRange selectionRange = extendedParagraph.subrange(0, select ionOffset);
658 frame().selection().moveTo(selectionRange.endPosition(), TextAffinit y::Downstream); 537 frame().selection().moveTo(selectionRange.endPosition(), TextAffinit y::Downstream);
659 if (adjustSelectionForParagraphBoundaries) 538 if (adjustSelectionForParagraphBoundaries)
660 frame().selection().modify(FrameSelection::AlterationMove, Direc tionForward, CharacterGranularity); 539 frame().selection().modify(FrameSelection::AlterationMove, Direc tionForward, CharacterGranularity);
661 } else { 540 } else {
662 // If this fails for any reason, the fallback is to go one position beyond the last replacement 541 // If this fails for any reason, the fallback is to go one position beyond the last replacement
663 frame().selection().moveTo(frame().selection().selection().visibleEn d()); 542 frame().selection().moveTo(frame().selection().selection().visibleEn d());
664 frame().selection().modify(FrameSelection::AlterationMove, Direction Forward, CharacterGranularity); 543 frame().selection().modify(FrameSelection::AlterationMove, Direction Forward, CharacterGranularity);
665 } 544 }
666 } 545 }
667 } 546 }
668 547
669 void SpellChecker::markMisspellingsAndBadGrammar(const VisibleSelection& spellin gSelection, bool markGrammar, const VisibleSelection& grammarSelection)
670 {
671 if (unifiedTextCheckerEnabled()) {
672 if (!isContinuousSpellCheckingEnabled())
673 return;
674
675 // markMisspellingsAndBadGrammar() is triggered by selection change, in which case we check spelling and grammar, but don't autocorrect misspellings.
676 TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
677 if (markGrammar)
678 textCheckingOptions |= TextCheckingTypeGrammar;
679 markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSe lection.toNormalizedEphemeralRange(), grammarSelection.toNormalizedEphemeralRang e());
680 return;
681 }
682
683 markMisspellings(spellingSelection);
684 if (markGrammar)
685 markBadGrammar(grammarSelection);
686 }
687
688 void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSele ctionAtWordBoundary) 548 void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSele ctionAtWordBoundary)
689 { 549 {
690 DCHECK(frame().selection().isAvailable()); 550 DCHECK(frame().selection().isAvailable());
691 TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing" ); 551 TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing" );
692 if (!isSpellCheckingEnabledFor(frame().selection().selection())) 552 if (!isSpellCheckingEnabledFor(frame().selection().selection()))
693 return; 553 return;
694 554
695 // We want to remove the markers from a word if an editing command will chan ge the word. This can happen in one of 555 // We want to remove the markers from a word if an editing command will chan ge the word. This can happen in one of
696 // several scenarios: 556 // several scenarios:
697 // 1. Insert in the middle of a word. 557 // 1. Insert in the middle of a word.
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
770 void SpellChecker::didEndEditingOnTextField(Element* e) 630 void SpellChecker::didEndEditingOnTextField(Element* e)
771 { 631 {
772 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField"); 632 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField");
773 633
774 // Remove markers when deactivating a selection in an <input type="text"/>. 634 // Remove markers when deactivating a selection in an <input type="text"/>.
775 // Prevent new ones from appearing too. 635 // Prevent new ones from appearing too.
776 m_spellCheckRequester->cancelCheck(); 636 m_spellCheckRequester->cancelCheck();
777 HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlEl ement(e); 637 HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlEl ement(e);
778 HTMLElement* innerEditor = textFormControlElement->innerEditorElement(); 638 HTMLElement* innerEditor = textFormControlElement->innerEditorElement();
779 DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling); 639 DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling);
780 if (unifiedTextCheckerEnabled())
781 markerTypes.add(DocumentMarker::Grammar);
782 for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor)) 640 for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor))
783 frame().document()->markers().removeMarkers(&node, markerTypes); 641 frame().document()->markers().removeMarkers(&node, markerTypes);
784 } 642 }
785 643
786 void SpellChecker::replaceMisspelledRange(const String& text) 644 void SpellChecker::replaceMisspelledRange(const String& text)
787 { 645 {
788 EphemeralRange caretRange = frame().selection().selection().toNormalizedEphe meralRange(); 646 EphemeralRange caretRange = frame().selection().selection().toNormalizedEphe meralRange();
789 if (caretRange.isNull()) 647 if (caretRange.isNull())
790 return; 648 return;
791 DocumentMarkerVector markers = frame().document()->markers().markersInRange( caretRange, DocumentMarker::MisspellingMarkers()); 649 DocumentMarkerVector markers = frame().document()->markers().markersInRange( caretRange, DocumentMarker::MisspellingMarkers());
(...skipping 20 matching lines...) Expand all
812 670
813 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelectio n, FrameSelection::SetSelectionOptions options) 671 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelectio n, FrameSelection::SetSelectionOptions options)
814 { 672 {
815 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); 673 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection");
816 if (!isSpellCheckingEnabledFor(oldSelection)) 674 if (!isSpellCheckingEnabledFor(oldSelection))
817 return; 675 return;
818 676
819 // When continuous spell checking is off, existing markers disappear after t he selection changes. 677 // When continuous spell checking is off, existing markers disappear after t he selection changes.
820 if (!isContinuousSpellCheckingEnabled()) { 678 if (!isContinuousSpellCheckingEnabled()) {
821 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); 679 frame().document()->markers().removeMarkers(DocumentMarker::Spelling);
822 frame().document()->markers().removeMarkers(DocumentMarker::Grammar);
823 return; 680 return;
824 } 681 }
825 682
826 if (!(options & FrameSelection::CloseTyping)) 683 if (!(options & FrameSelection::CloseTyping))
827 return; 684 return;
828 if (!shouldCheckOldSelection(oldSelection)) 685 if (!shouldCheckOldSelection(oldSelection))
829 return; 686 return;
830 687
831 VisibleSelection newAdjacentWords; 688 VisibleSelection newAdjacentWords;
832 const VisibleSelection newSelection = frame().selection().selection(); 689 const VisibleSelection newSelection = frame().selection().selection();
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
879 } 736 }
880 737
881 void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords) 738 void SpellChecker::spellCheckOldSelection(const VisibleSelection& oldSelection, const VisibleSelection& newAdjacentWords)
882 { 739 {
883 TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection"); 740 TRACE_EVENT0("blink", "SpellChecker::spellCheckOldSelection");
884 741
885 VisiblePosition oldStart(oldSelection.visibleStart()); 742 VisiblePosition oldStart(oldSelection.visibleStart());
886 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, L eftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary)); 743 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, L eftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
887 if (oldAdjacentWords == newAdjacentWords) 744 if (oldAdjacentWords == newAdjacentWords)
888 return; 745 return;
889 if (isContinuousSpellCheckingEnabled()) { 746
890 VisibleSelection selectedSentence = VisibleSelection(startOfSentence(old Start), endOfSentence(oldStart)); 747 markMisspellingsAndBadGrammar(oldAdjacentWords);
891 markMisspellingsAndBadGrammar(oldAdjacentWords, true, selectedSentence);
892 return;
893 }
894 markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
895 } 748 }
896 749
897 static Node* findFirstMarkable(Node* node) 750 static Node* findFirstMarkable(Node* node)
898 { 751 {
899 while (node) { 752 while (node) {
900 if (!node->layoutObject()) 753 if (!node->layoutObject())
901 return 0; 754 return 0;
902 if (node->layoutObject()->isText()) 755 if (node->layoutObject()->isText())
903 return node; 756 return node;
904 if (node->layoutObject()->isTextControl()) 757 if (node->layoutObject()->isTextControl())
(...skipping 23 matching lines...) Expand all
928 } 781 }
929 782
930 return false; 783 return false;
931 } 784 }
932 785
933 bool SpellChecker::selectionStartHasSpellingMarkerFor(int from, int length) cons t 786 bool SpellChecker::selectionStartHasSpellingMarkerFor(int from, int length) cons t
934 { 787 {
935 return selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length); 788 return selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length);
936 } 789 }
937 790
938 TextCheckingTypeMask SpellChecker::resolveTextCheckingTypeMask(TextCheckingTypeM ask textCheckingOptions)
939 {
940 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
941 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
942
943 TextCheckingTypeMask checkingTypes = 0;
944 if (shouldMarkSpelling)
945 checkingTypes |= TextCheckingTypeSpelling;
946 if (shouldMarkGrammar)
947 checkingTypes |= TextCheckingTypeGrammar;
948
949 return checkingTypes;
950 }
951
952 void SpellChecker::removeMarkers(const VisibleSelection& selection, DocumentMark er::MarkerTypes markerTypes) 791 void SpellChecker::removeMarkers(const VisibleSelection& selection, DocumentMark er::MarkerTypes markerTypes)
953 { 792 {
954 const EphemeralRange range = selection.toNormalizedEphemeralRange(); 793 const EphemeralRange range = selection.toNormalizedEphemeralRange();
955 if (range.isNull()) 794 if (range.isNull())
956 return; 795 return;
957 frame().document()->markers().removeMarkers(range, markerTypes); 796 frame().document()->markers().removeMarkers(range, markerTypes);
958 } 797 }
959 798
960 bool SpellChecker::unifiedTextCheckerEnabled() const 799 bool SpellChecker::unifiedTextCheckerEnabled() const
961 { 800 {
962 return blink::unifiedTextCheckerEnabled(m_frame); 801 return blink::unifiedTextCheckerEnabled(m_frame);
963 } 802 }
964 803
965 void SpellChecker::cancelCheck() 804 void SpellChecker::cancelCheck()
966 { 805 {
967 m_spellCheckRequester->cancelCheck(); 806 m_spellCheckRequester->cancelCheck();
968 } 807 }
969 808
970 void SpellChecker::requestTextChecking(const Element& element) 809 void SpellChecker::requestTextChecking(const Element& element)
971 { 810 {
972 const EphemeralRange rangeToCheck = EphemeralRange::rangeOfContents(element) ; 811 const EphemeralRange rangeToCheck = EphemeralRange::rangeOfContents(element) ;
973 m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextChec kingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToChe ck, rangeToCheck)); 812 m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextChec kingTypeSpelling, TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
974 } 813 }
975 814
976 DEFINE_TRACE(SpellChecker) 815 DEFINE_TRACE(SpellChecker)
977 { 816 {
978 visitor->trace(m_frame); 817 visitor->trace(m_frame);
979 visitor->trace(m_spellCheckRequester); 818 visitor->trace(m_spellCheckRequester);
980 } 819 }
981 820
982 void SpellChecker::prepareForLeakDetection() 821 void SpellChecker::prepareForLeakDetection()
983 { 822 {
984 m_spellCheckRequester->prepareForLeakDetection(); 823 m_spellCheckRequester->prepareForLeakDetection();
985 } 824 }
986 825
987 } // namespace blink 826 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.h ('k') | third_party/WebKit/Source/web/tests/WebFrameTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698