| OLD | NEW |
| 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 Loading... |
| 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 misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearch
Start, spellingSearchEnd).findFirstMisspelling(misspellingOffset, false); |
| 270 if (unifiedTextCheckerEnabled()) { | |
| 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 } | 238 } |
| 295 | 239 |
| 296 if (!badGrammarPhrase.isEmpty()) { | 240 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 | |
| 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 | 241 // 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. | 242 // a marker so we draw the red squiggle later. |
| 313 | 243 |
| 314 const EphemeralRange misspellingRange = calculateCharacterSubrange(Ephem
eralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelled
Word.length()); | 244 const EphemeralRange misspellingRange = calculateCharacterSubrange(Ephem
eralRange(spellingSearchStart, spellingSearchEnd), misspellingOffset, misspelled
Word.length()); |
| 315 frame().selection().setSelection(VisibleSelection(misspellingRange)); | 245 frame().selection().setSelection(VisibleSelection(misspellingRange)); |
| 316 frame().selection().revealSelection(); | 246 frame().selection().revealSelection(); |
| 317 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord); | 247 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord); |
| 318 frame().document()->markers().addMarker(misspellingRange.startPosition()
, misspellingRange.endPosition(), DocumentMarker::Spelling); | 248 frame().document()->markers().addMarker(misspellingRange.startPosition()
, misspellingRange.endPosition(), DocumentMarker::Spelling); |
| 319 } | 249 } |
| 320 } | 250 } |
| (...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 575 if (request->rootEditableElement()->document() != frame().selection().docume
nt()) { | 505 if (request->rootEditableElement()->document() != frame().selection().docume
nt()) { |
| 576 // we ignore |request| made for another document. | 506 // we ignore |request| made for another document. |
| 577 // "editing/spelling/spellcheck-sequencenum.html" and others reach here. | 507 // "editing/spelling/spellcheck-sequencenum.html" and others reach here. |
| 578 return; | 508 return; |
| 579 } | 509 } |
| 580 | 510 |
| 581 TextCheckingTypeMask textCheckingOptions = request->data().mask(); | 511 TextCheckingTypeMask textCheckingOptions = request->data().mask(); |
| 582 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraph
Range()); | 512 TextCheckingParagraph paragraph(request->checkingRange(), request->paragraph
Range()); |
| 583 | 513 |
| 584 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling; | 514 bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling; |
| 585 bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar; | |
| 586 | 515 |
| 587 // Expand the range to encompass entire paragraphs, since text checking need
s that much context. | 516 // Expand the range to encompass entire paragraphs, since text checking need
s that much context. |
| 588 int selectionOffset = 0; | 517 int selectionOffset = 0; |
| 589 int ambiguousBoundaryOffset = -1; | 518 int ambiguousBoundaryOffset = -1; |
| 590 bool selectionChanged = false; | 519 bool selectionChanged = false; |
| 591 bool restoreSelectionAfterChange = false; | 520 bool restoreSelectionAfterChange = false; |
| 592 bool adjustSelectionForParagraphBoundaries = false; | 521 bool adjustSelectionForParagraphBoundaries = false; |
| 593 | 522 |
| 594 if (shouldMarkSpelling) { | 523 if (shouldMarkSpelling) { |
| 595 if (frame().selection().isCaret()) { | 524 if (frame().selection().isCaret()) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 621 // Only mark misspelling if: | 550 // Only mark misspelling if: |
| 622 // 1. Current text checking isn't done for autocorrection, in which
case shouldMarkSpelling is false. | 551 // 1. Current text checking isn't done for autocorrection, in which
case shouldMarkSpelling is false. |
| 623 // 2. Result falls within spellingRange. | 552 // 2. Result falls within spellingRange. |
| 624 // 3. The word in question doesn't end at an ambiguous boundary. For
instance, we would not mark | 553 // 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. | 554 // "wouldn'" as misspelled right after apostrophe is typed. |
| 626 if (shouldMarkSpelling && result->decoration == TextDecorationTypeSp
elling && resultLocation >= paragraph.checkingStart() && resultLocation + result
Length <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) { | 555 if (shouldMarkSpelling && result->decoration == TextDecorationTypeSp
elling && resultLocation >= paragraph.checkingStart() && resultLocation + result
Length <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) { |
| 627 DCHECK_GT(resultLength, 0); | 556 DCHECK_GT(resultLength, 0); |
| 628 DCHECK_GE(resultLocation, 0); | 557 DCHECK_GE(resultLocation, 0); |
| 629 const EphemeralRange misspellingRange = calculateCharacterSubran
ge(paragraph.paragraphRange(), resultLocation, resultLength); | 558 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); | 559 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) { | 560 } else if (result->decoration == TextDecorationTypeInvisibleSpellche
ck && resultLocation >= paragraph.checkingStart() && resultLocation + resultLeng
th <= spellingRangeEndOffset) { |
| 644 DCHECK_GT(resultLength, 0); | 561 DCHECK_GT(resultLength, 0); |
| 645 DCHECK_GE(resultLocation, 0); | 562 DCHECK_GE(resultLocation, 0); |
| 646 const EphemeralRange invisibleSpellcheckRange = calculateCharact
erSubrange(paragraph.paragraphRange(), resultLocation, resultLength); | 563 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); | 564 frame().document()->markers().addMarker(invisibleSpellcheckRange
.startPosition(), invisibleSpellcheckRange.endPosition(), DocumentMarker::Invisi
bleSpellcheck, result->replacement, result->hash); |
| 648 } | 565 } |
| 649 } | 566 } |
| 650 } | 567 } |
| 651 | 568 |
| 652 if (selectionChanged) { | 569 if (selectionChanged) { |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 void SpellChecker::didEndEditingOnTextField(Element* e) | 687 void SpellChecker::didEndEditingOnTextField(Element* e) |
| 771 { | 688 { |
| 772 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField"); | 689 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField"); |
| 773 | 690 |
| 774 // Remove markers when deactivating a selection in an <input type="text"/>. | 691 // Remove markers when deactivating a selection in an <input type="text"/>. |
| 775 // Prevent new ones from appearing too. | 692 // Prevent new ones from appearing too. |
| 776 m_spellCheckRequester->cancelCheck(); | 693 m_spellCheckRequester->cancelCheck(); |
| 777 HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlEl
ement(e); | 694 HTMLTextFormControlElement* textFormControlElement = toHTMLTextFormControlEl
ement(e); |
| 778 HTMLElement* innerEditor = textFormControlElement->innerEditorElement(); | 695 HTMLElement* innerEditor = textFormControlElement->innerEditorElement(); |
| 779 DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling); | 696 DocumentMarker::MarkerTypes markerTypes(DocumentMarker::Spelling); |
| 780 if (unifiedTextCheckerEnabled()) | |
| 781 markerTypes.add(DocumentMarker::Grammar); | |
| 782 for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor)) | 697 for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor)) |
| 783 frame().document()->markers().removeMarkers(&node, markerTypes); | 698 frame().document()->markers().removeMarkers(&node, markerTypes); |
| 784 } | 699 } |
| 785 | 700 |
| 786 void SpellChecker::replaceMisspelledRange(const String& text) | 701 void SpellChecker::replaceMisspelledRange(const String& text) |
| 787 { | 702 { |
| 788 EphemeralRange caretRange = frame().selection().selection().toNormalizedEphe
meralRange(); | 703 EphemeralRange caretRange = frame().selection().selection().toNormalizedEphe
meralRange(); |
| 789 if (caretRange.isNull()) | 704 if (caretRange.isNull()) |
| 790 return; | 705 return; |
| 791 DocumentMarkerVector markers = frame().document()->markers().markersInRange(
caretRange, DocumentMarker::MisspellingMarkers()); | 706 DocumentMarkerVector markers = frame().document()->markers().markersInRange(
caretRange, DocumentMarker::MisspellingMarkers()); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 812 | 727 |
| 813 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelectio
n, FrameSelection::SetSelectionOptions options) | 728 void SpellChecker::respondToChangedSelection(const VisibleSelection& oldSelectio
n, FrameSelection::SetSelectionOptions options) |
| 814 { | 729 { |
| 815 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); | 730 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); |
| 816 if (!isSpellCheckingEnabledFor(oldSelection)) | 731 if (!isSpellCheckingEnabledFor(oldSelection)) |
| 817 return; | 732 return; |
| 818 | 733 |
| 819 // When continuous spell checking is off, existing markers disappear after t
he selection changes. | 734 // When continuous spell checking is off, existing markers disappear after t
he selection changes. |
| 820 if (!isContinuousSpellCheckingEnabled()) { | 735 if (!isContinuousSpellCheckingEnabled()) { |
| 821 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); | 736 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); |
| 822 frame().document()->markers().removeMarkers(DocumentMarker::Grammar); | |
| 823 return; | 737 return; |
| 824 } | 738 } |
| 825 | 739 |
| 826 if (!(options & FrameSelection::CloseTyping)) | 740 if (!(options & FrameSelection::CloseTyping)) |
| 827 return; | 741 return; |
| 828 if (!shouldCheckOldSelection(oldSelection)) | 742 if (!shouldCheckOldSelection(oldSelection)) |
| 829 return; | 743 return; |
| 830 | 744 |
| 831 VisibleSelection newAdjacentWords; | 745 VisibleSelection newAdjacentWords; |
| 832 const VisibleSelection newSelection = frame().selection().selection(); | 746 const VisibleSelection newSelection = frame().selection().selection(); |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 } | 877 } |
| 964 | 878 |
| 965 void SpellChecker::cancelCheck() | 879 void SpellChecker::cancelCheck() |
| 966 { | 880 { |
| 967 m_spellCheckRequester->cancelCheck(); | 881 m_spellCheckRequester->cancelCheck(); |
| 968 } | 882 } |
| 969 | 883 |
| 970 void SpellChecker::requestTextChecking(const Element& element) | 884 void SpellChecker::requestTextChecking(const Element& element) |
| 971 { | 885 { |
| 972 const EphemeralRange rangeToCheck = EphemeralRange::rangeOfContents(element)
; | 886 const EphemeralRange rangeToCheck = EphemeralRange::rangeOfContents(element)
; |
| 973 m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextChec
kingTypeSpelling | TextCheckingTypeGrammar, TextCheckingProcessBatch, rangeToChe
ck, rangeToCheck)); | 887 m_spellCheckRequester->requestCheckingFor(SpellCheckRequest::create(TextChec
kingTypeSpelling, TextCheckingProcessBatch, rangeToCheck, rangeToCheck)); |
| 974 } | 888 } |
| 975 | 889 |
| 976 DEFINE_TRACE(SpellChecker) | 890 DEFINE_TRACE(SpellChecker) |
| 977 { | 891 { |
| 978 visitor->trace(m_frame); | 892 visitor->trace(m_frame); |
| 979 visitor->trace(m_spellCheckRequester); | 893 visitor->trace(m_spellCheckRequester); |
| 980 } | 894 } |
| 981 | 895 |
| 982 void SpellChecker::prepareForLeakDetection() | 896 void SpellChecker::prepareForLeakDetection() |
| 983 { | 897 { |
| 984 m_spellCheckRequester->prepareForLeakDetection(); | 898 m_spellCheckRequester->prepareForLeakDetection(); |
| 985 } | 899 } |
| 986 | 900 |
| 987 } // namespace blink | 901 } // namespace blink |
| OLD | NEW |