summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing/TextCheckingHelper.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/editing/TextCheckingHelper.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/editing/TextCheckingHelper.cpp')
-rw-r--r--Source/WebCore/editing/TextCheckingHelper.cpp359
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)