| /* |
| * Copyright (C) 2010 Apple Inc. All rights reserved. |
| * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. |
| * Copyright (C) 2011-2013 Samsung Electronics |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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 PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "TextChecker.h" |
| |
| #include "TextCheckerState.h" |
| #include "WebProcessPool.h" |
| #include <WebCore/NotImplemented.h> |
| #include <WebCore/TextCheckerEnchant.h> |
| #include <unicode/ubrk.h> |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/text/TextBreakIterator.h> |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| #if ENABLE(SPELLCHECK) |
| static WebCore::TextCheckerEnchant& enchantTextChecker() |
| { |
| static NeverDestroyed<WebCore::TextCheckerEnchant> checker; |
| return checker; |
| } |
| #endif |
| |
| TextCheckerState& checkerState() |
| { |
| static TextCheckerState textCheckerState; |
| static std::once_flag onceFlag; |
| std::call_once(onceFlag, [] { |
| textCheckerState.isContinuousSpellCheckingEnabled = false; |
| textCheckerState.isGrammarCheckingEnabled = false; |
| }); |
| |
| return textCheckerState; |
| } |
| |
| const TextCheckerState& TextChecker::state() |
| { |
| return checkerState(); |
| } |
| |
| static bool testingModeEnabled = false; |
| |
| void TextChecker::setTestingMode(bool enabled) |
| { |
| testingModeEnabled = enabled; |
| } |
| |
| bool TextChecker::isTestingMode() |
| { |
| return testingModeEnabled; |
| } |
| |
| #if ENABLE(SPELLCHECK) |
| static void updateStateForAllProcessPools() |
| { |
| for (const auto& processPool : WebProcessPool::allProcessPools()) |
| processPool->textCheckerStateChanged(); |
| } |
| #endif |
| |
| bool TextChecker::isContinuousSpellCheckingAllowed() |
| { |
| #if ENABLE(SPELLCHECK) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled) |
| { |
| #if ENABLE(SPELLCHECK) |
| if (checkerState().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled) |
| return; |
| checkerState().isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled; |
| updateStateForAllProcessPools(); |
| #else |
| UNUSED_PARAM(isContinuousSpellCheckingEnabled); |
| #endif |
| } |
| |
| void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled) |
| { |
| #if ENABLE(SPELLCHECK) |
| if (checkerState().isGrammarCheckingEnabled == isGrammarCheckingEnabled) |
| return; |
| checkerState().isGrammarCheckingEnabled = isGrammarCheckingEnabled; |
| updateStateForAllProcessPools(); |
| #else |
| UNUSED_PARAM(isGrammarCheckingEnabled); |
| #endif |
| } |
| |
| void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled) |
| { |
| #if ENABLE(SPELLCHECK) |
| checkerState().isContinuousSpellCheckingEnabled = enabled; |
| #else |
| UNUSED_PARAM(enabled); |
| #endif |
| } |
| |
| void TextChecker::grammarCheckingEnabledStateChanged(bool enabled) |
| { |
| #if ENABLE(SPELLCHECK) |
| checkerState().isGrammarCheckingEnabled = enabled; |
| #else |
| UNUSED_PARAM(enabled); |
| #endif |
| } |
| |
| int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy*) |
| { |
| return 0; |
| } |
| |
| void TextChecker::closeSpellDocumentWithTag(int64_t /* tag */) |
| { |
| } |
| |
| void TextChecker::checkSpellingOfString(int64_t /* spellDocumentTag */, StringView text, int32_t& misspellingLocation, int32_t& misspellingLength) |
| { |
| #if ENABLE(SPELLCHECK) |
| misspellingLocation = -1; |
| misspellingLength = 0; |
| enchantTextChecker().checkSpellingOfString(text.toStringWithoutCopying(), misspellingLocation, misspellingLength); |
| #else |
| UNUSED_PARAM(text); |
| UNUSED_PARAM(misspellingLocation); |
| UNUSED_PARAM(misspellingLength); |
| #endif |
| } |
| |
| void TextChecker::checkGrammarOfString(int64_t /* spellDocumentTag */, StringView /* text */, Vector<WebCore::GrammarDetail>& /* grammarDetails */, int32_t& /* badGrammarLocation */, int32_t& /* badGrammarLength */) |
| { |
| } |
| |
| bool TextChecker::spellingUIIsShowing() |
| { |
| return false; |
| } |
| |
| void TextChecker::toggleSpellingUIIsShowing() |
| { |
| } |
| |
| void TextChecker::updateSpellingUIWithMisspelledWord(int64_t /* spellDocumentTag */, const String& /* misspelledWord */) |
| { |
| } |
| |
| void TextChecker::updateSpellingUIWithGrammarString(int64_t /* spellDocumentTag */, const String& /* badGrammarPhrase */, const GrammarDetail& /* grammarDetail */) |
| { |
| } |
| |
| void TextChecker::getGuessesForWord(int64_t /* spellDocumentTag */, const String& word, const String& /* context */, int32_t /* insertionPoint */, Vector<String>& guesses, bool) |
| { |
| #if ENABLE(SPELLCHECK) |
| guesses = enchantTextChecker().getGuessesForWord(word); |
| #else |
| UNUSED_PARAM(word); |
| UNUSED_PARAM(guesses); |
| #endif |
| } |
| |
| void TextChecker::learnWord(int64_t /* spellDocumentTag */, const String& word) |
| { |
| #if ENABLE(SPELLCHECK) |
| enchantTextChecker().learnWord(word); |
| #else |
| UNUSED_PARAM(word); |
| #endif |
| } |
| |
| void TextChecker::ignoreWord(int64_t /* spellDocumentTag */, const String& word) |
| { |
| #if ENABLE(SPELLCHECK) |
| enchantTextChecker().ignoreWord(word); |
| #else |
| UNUSED_PARAM(word); |
| #endif |
| } |
| |
| void TextChecker::requestCheckingOfString(Ref<TextCheckerCompletion>&& completion, int32_t insertionPoint) |
| { |
| #if ENABLE(SPELLCHECK) |
| TextCheckingRequestData request = completion->textCheckingRequestData(); |
| ASSERT(request.sequence() != unrequestedTextCheckingSequence); |
| ASSERT(request.mask() != TextCheckingTypeNone); |
| |
| completion->didFinishCheckingText(checkTextOfParagraph(completion->spellDocumentTag(), request.text(), insertionPoint, request.mask(), false)); |
| #else |
| UNUSED_PARAM(completion); |
| #endif |
| } |
| |
| #if USE(UNIFIED_TEXT_CHECKING) && ENABLE(SPELLCHECK) |
| static unsigned nextWordOffset(StringView text, unsigned currentOffset) |
| { |
| // FIXME: avoid creating textIterator object here, it could be passed as a parameter. |
| // ubrk_isBoundary() leaves the iterator pointing to the first boundary position at |
| // or after "offset" (ubrk_isBoundary side effect). |
| // For many word separators, the method doesn't properly determine the boundaries |
| // without resetting the iterator. |
| UBreakIterator* textIterator = wordBreakIterator(text); |
| if (!textIterator) |
| return currentOffset; |
| |
| unsigned wordOffset = currentOffset; |
| while (wordOffset < text.length() && ubrk_isBoundary(textIterator, wordOffset)) |
| ++wordOffset; |
| |
| // Do not treat the word's boundary as a separator. |
| if (!currentOffset && wordOffset == 1) |
| return currentOffset; |
| |
| // Omit multiple separators. |
| if ((wordOffset - currentOffset) > 1) |
| --wordOffset; |
| |
| return wordOffset; |
| } |
| #endif |
| |
| #if USE(UNIFIED_TEXT_CHECKING) |
| Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, StringView text, int32_t insertionPoint, uint64_t checkingTypes, bool) |
| { |
| UNUSED_PARAM(insertionPoint); |
| #if ENABLE(SPELLCHECK) |
| if (!(checkingTypes & TextCheckingTypeSpelling)) |
| return Vector<TextCheckingResult>(); |
| |
| UBreakIterator* textIterator = wordBreakIterator(text); |
| if (!textIterator) |
| return Vector<TextCheckingResult>(); |
| |
| // Omit the word separators at the beginning/end of the text to don't unnecessarily |
| // involve the client to check spelling for them. |
| unsigned offset = nextWordOffset(text, 0); |
| unsigned lengthStrip = text.length(); |
| while (lengthStrip > 0 && ubrk_isBoundary(textIterator, lengthStrip - 1)) |
| --lengthStrip; |
| |
| Vector<TextCheckingResult> paragraphCheckingResult; |
| while (offset < lengthStrip) { |
| int32_t misspellingLocation = -1; |
| int32_t misspellingLength = 0; |
| checkSpellingOfString(spellDocumentTag, text.substring(offset, lengthStrip - offset), misspellingLocation, misspellingLength); |
| if (!misspellingLength) |
| break; |
| |
| TextCheckingResult misspellingResult; |
| misspellingResult.type = TextCheckingTypeSpelling; |
| misspellingResult.location = offset + misspellingLocation; |
| misspellingResult.length = misspellingLength; |
| paragraphCheckingResult.append(misspellingResult); |
| offset += misspellingLocation + misspellingLength; |
| // Generally, we end up checking at the word separator, move to the adjacent word. |
| offset = nextWordOffset(text.substring(0, lengthStrip), offset); |
| } |
| return paragraphCheckingResult; |
| #else |
| UNUSED_PARAM(spellDocumentTag); |
| UNUSED_PARAM(text); |
| UNUSED_PARAM(checkingTypes); |
| return Vector<TextCheckingResult>(); |
| #endif // ENABLE(SPELLCHECK) |
| } |
| #endif // USE(UNIFIED_TEXT_CHECKING) |
| |
| void TextChecker::setSpellCheckingLanguages(const Vector<String>& languages) |
| { |
| #if ENABLE(SPELLCHECK) |
| enchantTextChecker().updateSpellCheckingLanguages(languages); |
| #else |
| UNUSED_PARAM(languages); |
| #endif |
| } |
| |
| Vector<String> TextChecker::loadedSpellCheckingLanguages() |
| { |
| #if ENABLE(SPELLCHECK) |
| return enchantTextChecker().loadedSpellCheckingLanguages(); |
| #else |
| return Vector<String>(); |
| #endif |
| } |
| |
| } // namespace WebKit |