diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/editing/TextCheckingHelper.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/editing/TextCheckingHelper.cpp')
-rw-r--r-- | Source/WebCore/editing/TextCheckingHelper.cpp | 359 |
1 files changed, 182 insertions, 177 deletions
diff --git a/Source/WebCore/editing/TextCheckingHelper.cpp b/Source/WebCore/editing/TextCheckingHelper.cpp index 0e89cfccc..379d7d299 100644 --- a/Source/WebCore/editing/TextCheckingHelper.cpp +++ b/Source/WebCore/editing/TextCheckingHelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,82 +29,92 @@ #include "Document.h" #include "DocumentMarkerController.h" +#include "ExceptionCode.h" #include "Frame.h" +#include "FrameSelection.h" #include "Settings.h" -#include "TextBreakIterator.h" #include "TextCheckerClient.h" #include "TextIterator.h" #include "VisiblePosition.h" #include "VisibleUnits.h" +#include <unicode/ubrk.h> #include <wtf/text/StringView.h> +#include <wtf/text/TextBreakIterator.h> namespace WebCore { #if !USE(UNIFIED_TEXT_CHECKING) #if USE(GRAMMAR_CHECKING) -static void findBadGrammars(TextCheckerClient* client, const UChar* text, int start, int length, Vector<TextCheckingResult>& results) -{ - int checkLocation = start; - int checkLength = length; - while (0 < checkLength) { +static void findGrammaticalErrors(TextCheckerClient& client, StringView text, Vector<TextCheckingResult>& results) +{ + for (unsigned checkLocation = 0; checkLocation < text.length(); ) { int badGrammarLocation = -1; int badGrammarLength = 0; Vector<GrammarDetail> badGrammarDetails; - client->checkGrammarOfString(text + checkLocation, checkLength, badGrammarDetails, &badGrammarLocation, &badGrammarLength); + client.checkGrammarOfString(text.substring(checkLocation), badGrammarDetails, &badGrammarLocation, &badGrammarLength); if (!badGrammarLength) break; - ASSERT(0 <= badGrammarLocation && badGrammarLocation <= checkLength); - ASSERT(0 < badGrammarLength && badGrammarLocation + badGrammarLength <= checkLength); + + ASSERT(badGrammarLocation >= 0); + ASSERT(static_cast<unsigned>(badGrammarLocation) <= text.length() - checkLocation); + ASSERT(badGrammarLength > 0); + ASSERT(static_cast<unsigned>(badGrammarLength) <= text.length() - checkLocation - badGrammarLocation); + TextCheckingResult badGrammar; badGrammar.type = TextCheckingTypeGrammar; badGrammar.location = checkLocation + badGrammarLocation; badGrammar.length = badGrammarLength; - badGrammar.details.swap(badGrammarDetails); + badGrammar.details = WTFMove(badGrammarDetails); results.append(badGrammar); - checkLocation += (badGrammarLocation + badGrammarLength); - checkLength -= (badGrammarLocation + badGrammarLength); + checkLocation += badGrammarLocation + badGrammarLength; } } + #endif -static void findMisspellings(TextCheckerClient* client, const UChar* text, int length, Vector<TextCheckingResult>& results) +static void findMisspellings(TextCheckerClient& client, StringView text, Vector<TextCheckingResult>& results) { - TextBreakIterator* iterator = wordBreakIterator(StringView(text, length)); + UBreakIterator* iterator = wordBreakIterator(text); if (!iterator) return; - int wordStart = textBreakCurrent(iterator); - while (0 <= wordStart) { - int wordEnd = textBreakNext(iterator); + for (int wordStart = ubrk_current(iterator); wordStart >= 0; ) { + int wordEnd = ubrk_next(iterator); if (wordEnd < 0) break; + int wordLength = wordEnd - wordStart; int misspellingLocation = -1; int misspellingLength = 0; - client->checkSpellingOfString(text + wordStart, wordLength, &misspellingLocation, &misspellingLength); - if (0 < misspellingLength) { - ASSERT(0 <= misspellingLocation && misspellingLocation <= wordLength); - ASSERT(0 < misspellingLength && misspellingLocation + misspellingLength <= wordLength); + client.checkSpellingOfString(text.substring(wordStart, wordLength), &misspellingLocation, &misspellingLength); + + if (misspellingLength > 0) { + ASSERT(misspellingLocation >= 0); + ASSERT(misspellingLocation <= wordLength); + ASSERT(misspellingLength > 0); + ASSERT(misspellingLocation + misspellingLength <= wordLength); + TextCheckingResult misspelling; misspelling.type = TextCheckingTypeSpelling; misspelling.location = wordStart + misspellingLocation; misspelling.length = misspellingLength; - misspelling.replacement = client->getAutoCorrectSuggestionForMisspelledWord(String(text + misspelling.location, misspelling.length)); + misspelling.replacement = client.getAutoCorrectSuggestionForMisspelledWord(text.substring(misspelling.location, misspelling.length).toStringWithoutCopying()); results.append(misspelling); } wordStart = wordEnd; } } + #endif -static PassRefPtr<Range> expandToParagraphBoundary(PassRefPtr<Range> range) +static Ref<Range> expandToParagraphBoundary(PassRefPtr<Range> range) { - RefPtr<Range> paragraphRange = range->cloneRange(IGNORE_EXCEPTION); - setStart(paragraphRange.get(), startOfParagraph(range->startPosition())); - setEnd(paragraphRange.get(), endOfParagraph(range->endPosition())); + Ref<Range> paragraphRange = range->cloneRange(); + setStart(paragraphRange.ptr(), startOfParagraph(range->startPosition())); + setEnd(paragraphRange.ptr(), endOfParagraph(range->endPosition())); return paragraphRange; } @@ -139,7 +149,7 @@ void TextCheckingParagraph::expandRangeToNextEnd() void TextCheckingParagraph::invalidateParagraphRangeValues() { m_checkingStart = m_checkingEnd = -1; - m_offsetAsRange = 0; + m_offsetAsRange = nullptr; m_text = String(); } @@ -153,7 +163,7 @@ PassRefPtr<Range> TextCheckingParagraph::paragraphRange() const { ASSERT(m_checkingRange); if (!m_paragraphRange) - m_paragraphRange = expandToParagraphBoundary(checkingRange()); + m_paragraphRange = expandToParagraphBoundary(m_checkingRange); return m_paragraphRange; } @@ -163,28 +173,31 @@ PassRefPtr<Range> TextCheckingParagraph::subrange(int characterOffset, int chara return TextIterator::subrange(paragraphRange().get(), characterOffset, characterCount); } -int TextCheckingParagraph::offsetTo(const Position& position, ExceptionCode& ec) const +ExceptionOr<int> TextCheckingParagraph::offsetTo(const Position& position) const { ASSERT(m_checkingRange); - RefPtr<Range> range = offsetAsRange()->cloneRange(ASSERT_NO_EXCEPTION); - range->setEnd(position.containerNode(), position.computeOffsetInContainerNode(), ec); - if (ec) - return 0; - return TextIterator::rangeLength(range.get()); + if (!position.containerNode()) + return Exception { TypeError }; + + auto range = offsetAsRange()->cloneRange(); + auto result = range->setEnd(*position.containerNode(), position.computeOffsetInContainerNode()); + if (result.hasException()) + return result.releaseException(); + return TextIterator::rangeLength(range.ptr()); } bool TextCheckingParagraph::isEmpty() const { // Both predicates should have same result, but we check both just for sure. // We need to investigate to remove this redundancy. - return isRangeEmpty() || isTextEmpty(); + return checkingStart() >= checkingEnd() || text().isEmpty(); } PassRefPtr<Range> TextCheckingParagraph::offsetAsRange() const { ASSERT(m_checkingRange); if (!m_offsetAsRange) - m_offsetAsRange = Range::create(paragraphRange()->startContainer()->document(), paragraphRange()->startPosition(), checkingRange()->startPosition()); + m_offsetAsRange = Range::create(paragraphRange()->startContainer().document(), paragraphRange()->startPosition(), m_checkingRange->startPosition()); return m_offsetAsRange; } @@ -209,7 +222,7 @@ int TextCheckingParagraph::checkingEnd() const { ASSERT(m_checkingRange); if (m_checkingEnd == -1) - m_checkingEnd = checkingStart() + TextIterator::rangeLength(checkingRange().get()); + m_checkingEnd = checkingStart() + TextIterator::rangeLength(m_checkingRange.get()); return m_checkingEnd; } @@ -217,7 +230,7 @@ int TextCheckingParagraph::checkingLength() const { ASSERT(m_checkingRange); if (-1 == m_checkingLength) - m_checkingLength = TextIterator::rangeLength(checkingRange().get()); + m_checkingLength = TextIterator::rangeLength(m_checkingRange.get()); return m_checkingLength; } @@ -233,68 +246,62 @@ TextCheckingHelper::~TextCheckingHelper() { } -#if !PLATFORM(IOS) String TextCheckingHelper::findFirstMisspelling(int& firstMisspellingOffset, bool markAll, RefPtr<Range>& firstMisspellingRange) { - WordAwareIterator it(m_range.get()); firstMisspellingOffset = 0; - + String firstMisspelling; int currentChunkOffset = 0; - while (!it.atEnd()) { - const UChar* chars = it.characters(); - int len = it.length(); - - // Skip some work for one-space-char hunks - if (!(len == 1 && chars[0] == ' ')) { - - int misspellingLocation = -1; - int misspellingLength = 0; - m_client->textChecker()->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength); - - // 5490627 shows that there was some code path here where the String constructor below crashes. - // We don't know exactly what combination of bad input caused this, so we're making this much - // more robust against bad input on release builds. - ASSERT(misspellingLength >= 0); - ASSERT(misspellingLocation >= -1); - ASSERT(!misspellingLength || misspellingLocation >= 0); - ASSERT(misspellingLocation < len); - ASSERT(misspellingLength <= len); - ASSERT(misspellingLocation + misspellingLength <= len); - - if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) { - - // Compute range of misspelled word - RefPtr<Range> misspellingRange = TextIterator::subrange(m_range.get(), currentChunkOffset + misspellingLocation, misspellingLength); - - // Remember first-encountered misspelling and its offset. - if (!firstMisspelling) { - firstMisspellingOffset = currentChunkOffset + misspellingLocation; - firstMisspelling = String(chars + misspellingLocation, misspellingLength); - firstMisspellingRange = misspellingRange; - } + for (WordAwareIterator it(*m_range); !it.atEnd(); currentChunkOffset += it.text().length(), it.advance()) { + StringView text = it.text(); + int textLength = text.length(); - // Store marker for misspelled word. - misspellingRange->startContainer()->document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling); + // Skip some work for one-space-char hunks. + if (textLength == 1 && text[0] == ' ') + continue; - // Bail out if we're marking only the first misspelling, and not all instances. - if (!markAll) - break; + int misspellingLocation = -1; + int misspellingLength = 0; + m_client->textChecker()->checkSpellingOfString(text, &misspellingLocation, &misspellingLength); + + // 5490627 shows that there was some code path here where the String constructor below crashes. + // We don't know exactly what combination of bad input caused this, so we're making this much + // more robust against bad input on release builds. + ASSERT(misspellingLength >= 0); + ASSERT(misspellingLocation >= -1); + ASSERT(!misspellingLength || misspellingLocation >= 0); + ASSERT(misspellingLocation < textLength); + ASSERT(misspellingLength <= textLength); + ASSERT(misspellingLocation + misspellingLength <= textLength); + + if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < textLength && misspellingLength <= textLength && misspellingLocation + misspellingLength <= textLength) { + // Compute range of misspelled word + RefPtr<Range> misspellingRange = TextIterator::subrange(m_range.get(), currentChunkOffset + misspellingLocation, misspellingLength); + + // Remember first-encountered misspelling and its offset. + if (!firstMisspelling) { + firstMisspellingOffset = currentChunkOffset + misspellingLocation; + firstMisspelling = text.substring(misspellingLocation, misspellingLength).toString(); + firstMisspellingRange = misspellingRange; } + + // Store marker for misspelled word. + misspellingRange->startContainer().document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling); + + // Bail out if we're marking only the first misspelling, and not all instances. + if (!markAll) + break; } - - currentChunkOffset += len; - it.advance(); } - + return firstMisspelling; } String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail) { if (!unifiedTextCheckerEnabled()) - return ""; + return emptyString(); String firstFoundItem; String misspelledWord; @@ -306,36 +313,36 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b outGrammarDetail.location = -1; outGrammarDetail.length = 0; outGrammarDetail.guesses.clear(); - outGrammarDetail.userDescription = ""; + outGrammarDetail.userDescription = emptyString(); // Expand the search range to encompass entire paragraphs, since text checking needs that much context. // Determine the character offset from the start of the paragraph to the start of the original search range, // since we will want to ignore results in this area. - RefPtr<Range> paragraphRange = m_range->cloneRange(IGNORE_EXCEPTION); - setStart(paragraphRange.get(), startOfParagraph(m_range->startPosition())); - int totalRangeLength = TextIterator::rangeLength(paragraphRange.get()); - setEnd(paragraphRange.get(), endOfParagraph(m_range->startPosition())); + Ref<Range> paragraphRange = m_range->cloneRange(); + setStart(paragraphRange.ptr(), startOfParagraph(m_range->startPosition())); + int totalRangeLength = TextIterator::rangeLength(paragraphRange.ptr()); + setEnd(paragraphRange.ptr(), endOfParagraph(m_range->startPosition())); - RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->startPosition()); - int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); + Ref<Range> offsetAsRange = Range::create(paragraphRange->startContainer().document(), paragraphRange->startPosition(), m_range->startPosition()); + int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.ptr()); int totalLengthProcessed = 0; bool firstIteration = true; bool lastIteration = false; while (totalLengthProcessed < totalRangeLength) { // Iterate through the search range by paragraphs, checking each one for spelling and grammar. - int currentLength = TextIterator::rangeLength(paragraphRange.get()); + int currentLength = TextIterator::rangeLength(paragraphRange.ptr()); int currentStartOffset = firstIteration ? rangeStartOffset : 0; int currentEndOffset = currentLength; if (inSameParagraph(paragraphRange->startPosition(), m_range->endPosition())) { // Determine the character offset from the end of the original search range to the end of the paragraph, // since we will want to ignore results in this area. - RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->endPosition()); + RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer().document(), paragraphRange->startPosition(), m_range->endPosition()); currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get()); lastIteration = true; } if (currentStartOffset < currentEndOffset) { - String paragraphString = plainText(paragraphRange.get()); + String paragraphString = plainText(paragraphRange.ptr()); if (paragraphString.length() > 0) { bool foundGrammar = false; int spellingLocation = 0; @@ -345,36 +352,41 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b Vector<TextCheckingResult> results; TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; - checkTextOfParagraph(m_client->textChecker(), paragraphString.deprecatedCharacters(), paragraphString.length(), checkingTypes, results); - - for (unsigned i = 0; i < results.size(); i++) { - const TextCheckingResult* result = &results[i]; - if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) { - ASSERT(result->length > 0 && result->location >= 0); - spellingLocation = result->location; - misspelledWord = paragraphString.substring(result->location, result->length); + VisibleSelection currentSelection; + if (Frame* frame = paragraphRange->ownerDocument().frame()) + currentSelection = frame->selection().selection(); + checkTextOfParagraph(*m_client->textChecker(), paragraphString, checkingTypes, results, currentSelection); + + for (auto& result : results) { + if (result.type == TextCheckingTypeSpelling && result.location >= currentStartOffset && result.location + result.length <= currentEndOffset) { + ASSERT(result.length > 0); + ASSERT(result.location >= 0); + spellingLocation = result.location; + misspelledWord = paragraphString.substring(result.location, result.length); ASSERT(misspelledWord.length()); break; } - if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) { - ASSERT(result->length > 0 && result->location >= 0); + if (checkGrammar && result.type == TextCheckingTypeGrammar && result.location < currentEndOffset && result.location + result.length > currentStartOffset) { + ASSERT(result.length > 0); + ASSERT(result.location >= 0); // We can't stop after the first grammar result, since there might still be a spelling result after // it begins but before the first detail in it, but we can stop if we find a second grammar result. if (foundGrammar) break; - for (unsigned j = 0; j < result->details.size(); j++) { - const GrammarDetail* detail = &result->details[j]; - ASSERT(detail->length > 0 && detail->location >= 0); - if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) { + for (unsigned j = 0; j < result.details.size(); j++) { + const GrammarDetail* detail = &result.details[j]; + ASSERT(detail->length > 0); + ASSERT(detail->location >= 0); + if (result.location + detail->location >= currentStartOffset && result.location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result.location + detail->location < grammarDetailLocation)) { grammarDetailIndex = j; - grammarDetailLocation = result->location + detail->location; + grammarDetailLocation = result.location + detail->location; foundGrammar = true; } } if (foundGrammar) { - grammarPhraseLocation = result->location; - outGrammarDetail = result->details[grammarDetailIndex]; - badGrammarPhrase = paragraphString.substring(result->location, result->length); + grammarPhraseLocation = result.location; + outGrammarDetail = result.details[grammarDetailIndex]; + badGrammarPhrase = paragraphString.substring(result.location, result.length); ASSERT(badGrammarPhrase.length()); } } @@ -383,7 +395,7 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) { int spellingOffset = spellingLocation - currentStartOffset; if (!firstIteration) { - RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); + RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer().document(), m_range->startPosition(), paragraphRange->startPosition()); spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = true; @@ -394,7 +406,7 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b if (checkGrammar && !badGrammarPhrase.isEmpty()) { int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset; if (!firstIteration) { - RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); + RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer().document(), m_range->startPosition(), paragraphRange->startPosition()); grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = false; @@ -407,16 +419,16 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength) break; VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition()); - setStart(paragraphRange.get(), newParagraphStart); - setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart)); + setStart(paragraphRange.ptr(), newParagraphStart); + setEnd(paragraphRange.ptr(), endOfParagraph(newParagraphStart)); firstIteration = false; totalLengthProcessed += currentLength; } return firstFoundItem; } -#endif // !PLATFORM(IOS) #if USE(GRAMMAR_CHECKING) + int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int startOffset, int endOffset, bool markAll) const { // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). @@ -425,7 +437,8 @@ int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& gram int earliestDetailIndex = -1; for (unsigned i = 0; i < grammarDetails.size(); i++) { const GrammarDetail* detail = &grammarDetails[i]; - ASSERT(detail->length > 0 && detail->location >= 0); + ASSERT(detail->length > 0); + ASSERT(detail->location >= 0); int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location; @@ -439,7 +452,7 @@ int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& gram if (markAll) { RefPtr<Range> badGrammarRange = TextIterator::subrange(m_range.get(), badGrammarPhraseLocation - startOffset + detail->location, detail->length); - badGrammarRange->startContainer()->document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription); + badGrammarRange->startContainer().document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription); } // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order) @@ -458,7 +471,7 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, outGrammarDetail.location = -1; outGrammarDetail.length = 0; outGrammarDetail.guesses.clear(); - outGrammarDetail.userDescription = ""; + outGrammarDetail.userDescription = emptyString(); outGrammarPhraseOffset = 0; String firstBadGrammarPhrase; @@ -469,12 +482,11 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, TextCheckingParagraph paragraph(m_range); // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range. - int startOffset = 0; - while (startOffset < paragraph.checkingEnd()) { + for (int startOffset = 0; startOffset < paragraph.checkingEnd(); ) { Vector<GrammarDetail> grammarDetails; int badGrammarPhraseLocation = -1; int badGrammarPhraseLength = 0; - m_client->textChecker()->checkGrammarOfString(paragraph.textDeprecatedCharacters() + startOffset, paragraph.textLength() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); + m_client->textChecker()->checkGrammarOfString(StringView(paragraph.text()).substring(startOffset), grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); if (!badGrammarPhraseLength) { ASSERT(badGrammarPhraseLocation == -1); @@ -484,7 +496,6 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, ASSERT(badGrammarPhraseLocation >= 0); badGrammarPhraseLocation += startOffset; - // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). int badGrammarIndex = findFirstGrammarDetail(grammarDetails, badGrammarPhraseLocation, paragraph.checkingStart(), paragraph.checkingEnd(), markAll); if (badGrammarIndex >= 0) { @@ -511,13 +522,12 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, return firstBadGrammarPhrase; } - bool TextCheckingHelper::isUngrammatical() const { if (!m_client) return false; - if (!m_range || m_range->collapsed(IGNORE_EXCEPTION)) + if (!m_range || m_range->collapsed()) return false; // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous @@ -535,9 +545,10 @@ bool TextCheckingHelper::isUngrammatical() const // Bad grammar, but phrase (e.g. sentence) starts beyond start of range. if (grammarPhraseOffset > 0) return false; - - ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0); - + + ASSERT(grammarDetail.location >= 0); + ASSERT(grammarDetail.length > 0); + // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range if (grammarDetail.location + grammarPhraseOffset) return false; @@ -554,7 +565,8 @@ bool TextCheckingHelper::isUngrammatical() const return true; } -#endif + +#endif // USE(GRAMMAR_CHECKING) Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool checkGrammar, bool& misspelled, bool& ungrammatical) const { @@ -565,7 +577,7 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool misspelled = false; ungrammatical = false; - if (!m_client || !m_range || m_range->collapsed(IGNORE_EXCEPTION)) + if (!m_client || !m_range || m_range->collapsed()) return guesses; // Expand the range to encompass entire paragraphs, since text checking needs that much context. @@ -575,14 +587,16 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool Vector<TextCheckingResult> results; TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; - checkTextOfParagraph(m_client->textChecker(), paragraph.textDeprecatedCharacters(), paragraph.textLength(), checkingTypes, results); - - for (unsigned i = 0; i < results.size(); i++) { - const TextCheckingResult* result = &results[i]; - if (result->type == TextCheckingTypeSpelling && paragraph.checkingRangeMatches(result->location, result->length)) { + VisibleSelection currentSelection; + if (Frame* frame = m_range->ownerDocument().frame()) + currentSelection = frame->selection().selection(); + checkTextOfParagraph(*m_client->textChecker(), paragraph.text(), checkingTypes, results, currentSelection); + + for (auto& result : results) { + if (result.type == TextCheckingTypeSpelling && paragraph.checkingRangeMatches(result.location, result.length)) { String misspelledWord = paragraph.checkingSubstring(); ASSERT(misspelledWord.length()); - m_client->textChecker()->getGuessesForWord(misspelledWord, String(), guesses); + m_client->textChecker()->getGuessesForWord(misspelledWord, String(), currentSelection, guesses); m_client->updateSpellingUIWithMisspelledWord(misspelledWord); misspelled = true; return guesses; @@ -591,19 +605,18 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool if (!checkGrammar) return guesses; - - for (unsigned i = 0; i < results.size(); i++) { - const TextCheckingResult* result = &results[i]; - if (result->type == TextCheckingTypeGrammar && paragraph.isCheckingRangeCoveredBy(result->location, result->length)) { - for (unsigned j = 0; j < result->details.size(); j++) { - const GrammarDetail* detail = &result->details[j]; - ASSERT(detail->length > 0 && detail->location >= 0); - if (paragraph.checkingRangeMatches(result->location + detail->location, detail->length)) { - String badGrammarPhrase = paragraph.textSubstring(result->location, result->length); + + for (auto& result : results) { + if (result.type == TextCheckingTypeGrammar && paragraph.isCheckingRangeCoveredBy(result.location, result.length)) { + for (auto& detail : result.details) { + ASSERT(detail.length > 0); + ASSERT(detail.location >= 0); + if (paragraph.checkingRangeMatches(result.location + detail.location, detail.length)) { + String badGrammarPhrase = paragraph.textSubstring(result.location, result.length); ASSERT(badGrammarPhrase.length()); - for (unsigned k = 0; k < detail->guesses.size(); k++) - guesses.append(detail->guesses[k]); - m_client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail); + for (auto& guess : detail.guesses) + guesses.append(guess); + m_client->updateSpellingUIWithGrammarString(badGrammarPhrase, detail); ungrammatical = true; return guesses; } @@ -613,8 +626,6 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool return guesses; } - -#if !PLATFORM(IOS) void TextCheckingHelper::markAllMisspellings(RefPtr<Range>& firstMisspellingRange) { // Use the "markAll" feature of findFirstMisspelling. Ignore the return value and the "out parameter"; @@ -633,47 +644,41 @@ void TextCheckingHelper::markAllBadGrammar() findFirstBadGrammar(ignoredGrammarDetail, ignoredOffset, true); } #endif -#endif // !PLATFORM(IOS) bool TextCheckingHelper::unifiedTextCheckerEnabled() const { return m_range && WebCore::unifiedTextCheckerEnabled(m_range->ownerDocument().frame()); } -void checkTextOfParagraph(TextCheckerClient* client, const UChar* text, int length, - TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results) +void checkTextOfParagraph(TextCheckerClient& client, StringView text, TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results, const VisibleSelection& currentSelection) { #if USE(UNIFIED_TEXT_CHECKING) - results = client->checkTextOfParagraph(StringView(text, length), checkingTypes); + results = client.checkTextOfParagraph(text, checkingTypes, currentSelection); #else - Vector<TextCheckingResult> spellingResult; + UNUSED_PARAM(currentSelection); + + Vector<TextCheckingResult> mispellings; if (checkingTypes & TextCheckingTypeSpelling) - findMisspellings(client, text, length, spellingResult); + findMisspellings(client, text, mispellings); #if USE(GRAMMAR_CHECKING) - Vector<TextCheckingResult> grammarResult; + // Look for grammatical errors that occur before the first misspelling. + Vector<TextCheckingResult> grammaticalErrors; if (checkingTypes & TextCheckingTypeGrammar) { - // Only checks grammartical error before the first misspellings - int grammarCheckLength = length; - for (size_t i = 0; i < spellingResult.size(); ++i) { - if (spellingResult[i].location < grammarCheckLength) - grammarCheckLength = spellingResult[i].location; - } - - findBadGrammars(client, text, 0, grammarCheckLength, grammarResult); + unsigned grammarCheckLength = text.length(); + for (auto& mispelling : mispellings) + grammarCheckLength = std::min<unsigned>(grammarCheckLength, mispelling.location); + findGrammaticalErrors(client, text.substring(0, grammarCheckLength), grammaticalErrors); } - if (grammarResult.size()) - results.swap(grammarResult); + results = WTFMove(grammaticalErrors); #endif - if (spellingResult.size()) { - if (results.isEmpty()) - results.swap(spellingResult); - else - results.appendVector(spellingResult); - } -#endif + if (results.isEmpty()) + results = WTFMove(mispellings); + else + results.appendVector(mispellings); +#endif // USE(UNIFIED_TEXT_CHECKING) } bool unifiedTextCheckerEnabled(const Frame* frame) |