35b3f39047c050daddd1f301a1dbf946d0093101
[WebKit.git] / Source / WebKit2 / UIProcess / gtk / TextCheckerGtk.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
4  * Copyright (C) 2011-2013 Samsung Electronics
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "TextChecker.h"
30
31 #include "TextBreakIterator.h"
32 #include "TextCheckerState.h"
33 #include "WebProcessPool.h"
34 #include "WebTextChecker.h"
35 #include <WebCore/NotImplemented.h>
36 #include <WebCore/TextCheckerEnchant.h>
37 #include <wtf/NeverDestroyed.h>
38
39 using namespace WebCore;
40
41 namespace WebKit {
42
43 static WebCore::TextCheckerEnchant& enchantTextChecker()
44 {
45     static NeverDestroyed<WebCore::TextCheckerEnchant> checker;
46     return checker;
47 }
48
49 TextCheckerState& checkerState()
50 {
51     static TextCheckerState textCheckerState;
52     static std::once_flag onceFlag;
53     std::call_once(onceFlag, [] {
54         textCheckerState.isContinuousSpellCheckingEnabled = false;
55         textCheckerState.isGrammarCheckingEnabled = false;
56     });
57
58     return textCheckerState;
59 }
60
61 const TextCheckerState& TextChecker::state()
62 {
63     return checkerState();
64 }
65
66 static void updateStateForAllProcessPools()
67 {
68     for (const auto& processPool : WebProcessPool::allProcessPools())
69         processPool->textCheckerStateChanged();
70 }
71
72 bool TextChecker::isContinuousSpellCheckingAllowed()
73 {
74     return true;
75 }
76
77 void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled)
78 {
79     if (checkerState().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled)
80         return;
81     checkerState().isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled;
82     updateStateForAllProcessPools();
83 }
84
85 void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled)
86 {
87     if (checkerState().isGrammarCheckingEnabled == isGrammarCheckingEnabled)
88         return;
89     checkerState().isGrammarCheckingEnabled = isGrammarCheckingEnabled;
90     updateStateForAllProcessPools();
91 }
92
93 void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled)
94 {
95     checkerState().isContinuousSpellCheckingEnabled = enabled;
96 }
97
98 void TextChecker::grammarCheckingEnabledStateChanged(bool enabled)
99 {
100     checkerState().isGrammarCheckingEnabled = enabled;
101 }
102
103 int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy*)
104 {
105     return 0;
106 }
107
108 void TextChecker::closeSpellDocumentWithTag(int64_t /* tag */)
109 {
110 }
111
112 void TextChecker::checkSpellingOfString(int64_t /* spellDocumentTag */, StringView text, int32_t& misspellingLocation, int32_t& misspellingLength)
113 {
114     misspellingLocation = -1;
115     misspellingLength = 0;
116     enchantTextChecker().checkSpellingOfString(text.toStringWithoutCopying(), misspellingLocation, misspellingLength);
117 }
118
119 void TextChecker::checkGrammarOfString(int64_t /* spellDocumentTag */, StringView /* text */, Vector<WebCore::GrammarDetail>& /* grammarDetails */, int32_t& /* badGrammarLocation */, int32_t& /* badGrammarLength */)
120 {
121 }
122
123 bool TextChecker::spellingUIIsShowing()
124 {
125     return false;
126 }
127
128 void TextChecker::toggleSpellingUIIsShowing()
129 {
130 }
131
132 void TextChecker::updateSpellingUIWithMisspelledWord(int64_t /* spellDocumentTag */, const String& /* misspelledWord */)
133 {
134 }
135
136 void TextChecker::updateSpellingUIWithGrammarString(int64_t /* spellDocumentTag */, const String& /* badGrammarPhrase */, const GrammarDetail& /* grammarDetail */)
137 {
138 }
139
140 void TextChecker::getGuessesForWord(int64_t /* spellDocumentTag */, const String& word, const String& /* context */, Vector<String>& guesses)
141 {
142     guesses = enchantTextChecker().getGuessesForWord(word);
143 }
144
145 void TextChecker::learnWord(int64_t /* spellDocumentTag */, const String& word)
146 {
147     enchantTextChecker().learnWord(word);
148 }
149
150 void TextChecker::ignoreWord(int64_t /* spellDocumentTag */, const String& word)
151 {
152     enchantTextChecker().ignoreWord(word);
153 }
154
155 void TextChecker::requestCheckingOfString(PassRefPtr<TextCheckerCompletion> completion)
156 {
157     if (!completion)
158         return;
159
160     TextCheckingRequestData request = completion->textCheckingRequestData();
161     ASSERT(request.sequence() != unrequestedTextCheckingSequence);
162     ASSERT(request.mask() != TextCheckingTypeNone);
163
164     completion->didFinishCheckingText(checkTextOfParagraph(completion->spellDocumentTag(), request.text(), request.mask()));
165 }
166
167 #if USE(UNIFIED_TEXT_CHECKING)
168 static unsigned nextWordOffset(StringView text, unsigned currentOffset)
169 {
170     // FIXME: avoid creating textIterator object here, it could be passed as a parameter.
171     //        isTextBreak() leaves the iterator pointing to the first boundary position at
172     //        or after "offset" (ubrk_isBoundary side effect).
173     //        For many word separators, the method doesn't properly determine the boundaries
174     //        without resetting the iterator.
175     TextBreakIterator* textIterator = wordBreakIterator(text);
176     if (!textIterator)
177         return currentOffset;
178
179     unsigned wordOffset = currentOffset;
180     while (wordOffset < text.length() && isTextBreak(textIterator, wordOffset))
181         ++wordOffset;
182
183     // Do not treat the word's boundary as a separator.
184     if (!currentOffset && wordOffset == 1)
185         return currentOffset;
186
187     // Omit multiple separators.
188     if ((wordOffset - currentOffset) > 1)
189         --wordOffset;
190
191     return wordOffset;
192 }
193
194 Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, StringView text, uint64_t checkingTypes)
195 {
196     if (!(checkingTypes & TextCheckingTypeSpelling))
197         return Vector<TextCheckingResult>();
198
199     TextBreakIterator* textIterator = wordBreakIterator(text);
200     if (!textIterator)
201         return Vector<TextCheckingResult>();
202
203     // Omit the word separators at the beginning/end of the text to don't unnecessarily
204     // involve the client to check spelling for them.
205     unsigned offset = nextWordOffset(text, 0);
206     unsigned lengthStrip = text.length();
207     while (lengthStrip > 0 && isTextBreak(textIterator, lengthStrip - 1))
208         --lengthStrip;
209
210     Vector<TextCheckingResult> paragraphCheckingResult;
211     while (offset < lengthStrip) {
212         int32_t misspellingLocation = -1;
213         int32_t misspellingLength = 0;
214         checkSpellingOfString(spellDocumentTag, text.substring(offset, lengthStrip - offset), misspellingLocation, misspellingLength);
215         if (!misspellingLength)
216             break;
217
218         TextCheckingResult misspellingResult;
219         misspellingResult.type = TextCheckingTypeSpelling;
220         misspellingResult.location = offset + misspellingLocation;
221         misspellingResult.length = misspellingLength;
222         paragraphCheckingResult.append(misspellingResult);
223         offset += misspellingLocation + misspellingLength;
224         // Generally, we end up checking at the word separator, move to the adjacent word.
225         offset = nextWordOffset(text.substring(0, lengthStrip), offset);
226     }
227     return paragraphCheckingResult;
228 }
229 #endif
230
231 void TextChecker::setSpellCheckingLanguages(const Vector<String>& languages)
232 {
233     enchantTextChecker().updateSpellCheckingLanguages(languages);
234 }
235
236 Vector<String> TextChecker::loadedSpellCheckingLanguages()
237 {
238     return enchantTextChecker().loadedSpellCheckingLanguages();
239 }
240
241 } // namespace WebKit