OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/spellchecker/spellcheck.h" | 5 #include "chrome/renderer/spellchecker/spellcheck.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/histogram.h" | 8 #include "base/histogram.h" |
9 #include "base/time.h" | 9 #include "base/time.h" |
| 10 #include "chrome/common/render_messages.h" |
| 11 #include "chrome/common/spellcheck_common.h" |
10 #include "chrome/renderer/render_thread.h" | 12 #include "chrome/renderer/render_thread.h" |
11 #include "third_party/hunspell/src/hunspell/hunspell.hxx" | 13 #include "third_party/hunspell/src/hunspell/hunspell.hxx" |
12 | 14 |
13 static const int kMaxAutoCorrectWordSize = 8; | |
14 static const int kMaxSuggestions = 5; | |
15 | |
16 using base::TimeTicks; | 15 using base::TimeTicks; |
17 | 16 |
18 SpellCheck::SpellCheck() | 17 SpellCheck::SpellCheck() |
19 : file_(base::kInvalidPlatformFileValue), | 18 : file_(base::kInvalidPlatformFileValue), |
20 auto_spell_correct_turned_on_(false), | 19 auto_spell_correct_turned_on_(false), |
21 // TODO(estade): initialize this properly. | |
22 is_using_platform_spelling_engine_(false), | 20 is_using_platform_spelling_engine_(false), |
23 initialized_(false) { | 21 initialized_(false) { |
24 // Wait till we check the first word before doing any initializing. | 22 // Wait till we check the first word before doing any initializing. |
25 } | 23 } |
26 | 24 |
27 SpellCheck::~SpellCheck() { | 25 SpellCheck::~SpellCheck() { |
28 } | 26 } |
29 | 27 |
30 void SpellCheck::Init(base::PlatformFile file, | 28 void SpellCheck::Init(base::PlatformFile file, |
31 const std::vector<std::string>& custom_words, | 29 const std::vector<std::string>& custom_words, |
32 const std::string language) { | 30 const std::string language) { |
33 initialized_ = true; | 31 initialized_ = true; |
34 hunspell_.reset(); | 32 hunspell_.reset(); |
35 bdict_file_.reset(); | 33 bdict_file_.reset(); |
36 file_ = file; | 34 file_ = file; |
| 35 is_using_platform_spelling_engine_ = |
| 36 file == base::kInvalidPlatformFileValue && !language.empty(); |
| 37 |
37 character_attributes_.SetDefaultLanguage(language); | 38 character_attributes_.SetDefaultLanguage(language); |
38 | 39 |
39 custom_words_.insert(custom_words_.end(), | 40 custom_words_.insert(custom_words_.end(), |
40 custom_words.begin(), custom_words.end()); | 41 custom_words.begin(), custom_words.end()); |
41 | 42 |
42 // We delay the actual initialization of hunspell until it is needed. | 43 // We delay the actual initialization of hunspell until it is needed. |
43 } | 44 } |
44 | 45 |
45 bool SpellCheck::SpellCheckWord( | 46 bool SpellCheck::SpellCheckWord( |
46 const char16* in_word, | 47 const char16* in_word, |
47 int in_word_len, | 48 int in_word_len, |
48 int tag, | 49 int tag, |
49 int* misspelling_start, | 50 int* misspelling_start, |
50 int* misspelling_len, | 51 int* misspelling_len, |
51 std::vector<string16>* optional_suggestions) { | 52 std::vector<string16>* optional_suggestions) { |
52 DCHECK(in_word_len >= 0); | 53 DCHECK(in_word_len >= 0); |
53 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given."; | 54 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given."; |
54 | 55 |
55 // Do nothing if we need to delay initialization. (Rather than blocking, | 56 // Do nothing if we need to delay initialization. (Rather than blocking, |
56 // report the word as correctly spelled.) | 57 // report the word as correctly spelled.) |
57 if (InitializeIfNeeded()) | 58 if (InitializeIfNeeded()) |
58 return true; | 59 return true; |
59 | 60 |
60 // Do nothing if spell checking is disabled. | 61 // Do nothing if spell checking is disabled. |
61 if (initialized_ && file_ == base::kInvalidPlatformFileValue) | 62 if (initialized_ && file_ == base::kInvalidPlatformFileValue && |
| 63 !is_using_platform_spelling_engine_) { |
62 return true; | 64 return true; |
| 65 } |
63 | 66 |
64 *misspelling_start = 0; | 67 *misspelling_start = 0; |
65 *misspelling_len = 0; | 68 *misspelling_len = 0; |
66 if (in_word_len == 0) | 69 if (in_word_len == 0) |
67 return true; // No input means always spelled correctly. | 70 return true; // No input means always spelled correctly. |
68 | 71 |
69 SpellcheckWordIterator word_iterator; | 72 SpellcheckWordIterator word_iterator; |
70 string16 word; | 73 string16 word; |
71 int word_start; | 74 int word_start; |
72 int word_length; | 75 int word_length; |
(...skipping 20 matching lines...) Expand all Loading... |
93 | 96 |
94 return true; | 97 return true; |
95 } | 98 } |
96 | 99 |
97 string16 SpellCheck::GetAutoCorrectionWord(const string16& word, int tag) { | 100 string16 SpellCheck::GetAutoCorrectionWord(const string16& word, int tag) { |
98 string16 autocorrect_word; | 101 string16 autocorrect_word; |
99 if (!auto_spell_correct_turned_on_) | 102 if (!auto_spell_correct_turned_on_) |
100 return autocorrect_word; // Return the empty string. | 103 return autocorrect_word; // Return the empty string. |
101 | 104 |
102 int word_length = static_cast<int>(word.size()); | 105 int word_length = static_cast<int>(word.size()); |
103 if (word_length < 2 || word_length > kMaxAutoCorrectWordSize) | 106 if (word_length < 2 || word_length > SpellCheckCommon::kMaxAutoCorrectWordSize
) |
104 return autocorrect_word; | 107 return autocorrect_word; |
105 | 108 |
106 if (InitializeIfNeeded()) | 109 if (InitializeIfNeeded()) |
107 return autocorrect_word; | 110 return autocorrect_word; |
108 | 111 |
109 char16 misspelled_word[kMaxAutoCorrectWordSize + 1]; | 112 char16 misspelled_word[SpellCheckCommon::kMaxAutoCorrectWordSize + 1]; |
110 const char16* word_char = word.c_str(); | 113 const char16* word_char = word.c_str(); |
111 for (int i = 0; i <= kMaxAutoCorrectWordSize; i++) { | 114 for (int i = 0; i <= SpellCheckCommon::kMaxAutoCorrectWordSize; i++) { |
112 if (i >= word_length) | 115 if (i >= word_length) |
113 misspelled_word[i] = NULL; | 116 misspelled_word[i] = NULL; |
114 else | 117 else |
115 misspelled_word[i] = word_char[i]; | 118 misspelled_word[i] = word_char[i]; |
116 } | 119 } |
117 | 120 |
118 // Swap adjacent characters and spellcheck. | 121 // Swap adjacent characters and spellcheck. |
119 int misspelling_start, misspelling_len; | 122 int misspelling_start, misspelling_len; |
120 for (int i = 0; i < word_length - 1; i++) { | 123 for (int i = 0; i < word_length - 1; i++) { |
121 // Swap. | 124 // Swap. |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
181 TimeTicks::Now() - start_time); | 184 TimeTicks::Now() - start_time); |
182 } | 185 } |
183 } | 186 } |
184 | 187 |
185 void SpellCheck::AddWordToHunspell(const std::string& word) { | 188 void SpellCheck::AddWordToHunspell(const std::string& word) { |
186 if (!word.empty() && word.length() < MAXWORDUTF8LEN) | 189 if (!word.empty() && word.length() < MAXWORDUTF8LEN) |
187 hunspell_->add(word.c_str()); | 190 hunspell_->add(word.c_str()); |
188 } | 191 } |
189 | 192 |
190 bool SpellCheck::InitializeIfNeeded() { | 193 bool SpellCheck::InitializeIfNeeded() { |
| 194 if (is_using_platform_spelling_engine_) |
| 195 return false; |
| 196 |
191 if (!initialized_) { | 197 if (!initialized_) { |
192 RenderThread::current()->RequestSpellCheckDictionary(); | 198 RenderThread::current()->Send( |
| 199 new ViewHostMsg_SpellChecker_RequestDictionary); |
193 initialized_ = true; | 200 initialized_ = true; |
194 return true; | 201 return true; |
195 } | 202 } |
196 | 203 |
197 // Check if the platform spellchecker is being used. | 204 // Check if the platform spellchecker is being used. |
198 if (!is_using_platform_spelling_engine_ && | 205 if (file_ != base::kInvalidPlatformFileValue) { |
199 file_ != base::kInvalidPlatformFileValue) { | |
200 // If it isn't, init hunspell. | 206 // If it isn't, init hunspell. |
201 InitializeHunspell(); | 207 InitializeHunspell(); |
202 } | 208 } |
203 | 209 |
204 return false; | 210 return false; |
205 } | 211 } |
206 | 212 |
207 // When called, relays the request to check the spelling to the proper | 213 // When called, relays the request to check the spelling to the proper |
208 // backend, either hunspell or a platform-specific backend. | 214 // backend, either hunspell or a platform-specific backend. |
209 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { | 215 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { |
210 bool word_correct = false; | 216 bool word_correct = false; |
211 | 217 |
212 if (is_using_platform_spelling_engine_) { | 218 if (is_using_platform_spelling_engine_) { |
213 // TODO(estade): sync IPC to browser. | 219 RenderThread::current()->Send( |
214 word_correct = true; | 220 new ViewHostMsg_SpellChecker_PlatformCheckSpelling(word_to_check, tag, |
| 221 &word_correct)); |
215 } else { | 222 } else { |
216 std::string word_to_check_utf8(UTF16ToUTF8(word_to_check)); | 223 std::string word_to_check_utf8(UTF16ToUTF8(word_to_check)); |
217 // Hunspell shouldn't let us exceed its max, but check just in case | 224 // Hunspell shouldn't let us exceed its max, but check just in case |
218 if (word_to_check_utf8.length() < MAXWORDUTF8LEN) { | 225 if (word_to_check_utf8.length() < MAXWORDUTF8LEN) { |
219 // |hunspell_->spell| returns 0 if the word is spelled correctly and | 226 // |hunspell_->spell| returns 0 if the word is spelled correctly and |
220 // non-zero otherwsie. | 227 // non-zero otherwsie. |
221 word_correct = (hunspell_->spell(word_to_check_utf8.c_str()) != 0); | 228 word_correct = (hunspell_->spell(word_to_check_utf8.c_str()) != 0); |
222 } | 229 } |
223 } | 230 } |
224 | 231 |
225 return word_correct; | 232 return word_correct; |
226 } | 233 } |
227 | 234 |
228 void SpellCheck::FillSuggestionList( | 235 void SpellCheck::FillSuggestionList( |
229 const string16& wrong_word, | 236 const string16& wrong_word, |
230 std::vector<string16>* optional_suggestions) { | 237 std::vector<string16>* optional_suggestions) { |
231 if (is_using_platform_spelling_engine_) { | 238 if (is_using_platform_spelling_engine_) { |
232 // TODO(estade): sync IPC to browser. | 239 RenderThread::current()->Send( |
| 240 new ViewHostMsg_SpellChecker_PlatformFillSuggestionList( |
| 241 wrong_word, optional_suggestions)); |
233 return; | 242 return; |
234 } | 243 } |
235 char** suggestions; | 244 char** suggestions; |
236 int number_of_suggestions = | 245 int number_of_suggestions = |
237 hunspell_->suggest(&suggestions, UTF16ToUTF8(wrong_word).c_str()); | 246 hunspell_->suggest(&suggestions, UTF16ToUTF8(wrong_word).c_str()); |
238 | 247 |
239 // Populate the vector of WideStrings. | 248 // Populate the vector of WideStrings. |
240 for (int i = 0; i < number_of_suggestions; i++) { | 249 for (int i = 0; i < number_of_suggestions; i++) { |
241 if (i < kMaxSuggestions) | 250 if (i < SpellCheckCommon::kMaxSuggestions) |
242 optional_suggestions->push_back(UTF8ToUTF16(suggestions[i])); | 251 optional_suggestions->push_back(UTF8ToUTF16(suggestions[i])); |
243 free(suggestions[i]); | 252 free(suggestions[i]); |
244 } | 253 } |
245 if (suggestions != NULL) | 254 if (suggestions != NULL) |
246 free(suggestions); | 255 free(suggestions); |
247 } | 256 } |
248 | 257 |
249 // Returns whether or not the given string is a valid contraction. | 258 // Returns whether or not the given string is a valid contraction. |
250 // This function is a fall-back when the SpellcheckWordIterator class | 259 // This function is a fall-back when the SpellcheckWordIterator class |
251 // returns a concatenated word which is not in the selected dictionary | 260 // returns a concatenated word which is not in the selected dictionary |
252 // (e.g. "in'n'out") but each word is valid. | 261 // (e.g. "in'n'out") but each word is valid. |
253 bool SpellCheck::IsValidContraction(const string16& contraction, int tag) { | 262 bool SpellCheck::IsValidContraction(const string16& contraction, int tag) { |
254 SpellcheckWordIterator word_iterator; | 263 SpellcheckWordIterator word_iterator; |
255 word_iterator.Initialize(&character_attributes_, contraction.c_str(), | 264 word_iterator.Initialize(&character_attributes_, contraction.c_str(), |
256 contraction.length(), false); | 265 contraction.length(), false); |
257 | 266 |
258 string16 word; | 267 string16 word; |
259 int word_start; | 268 int word_start; |
260 int word_length; | 269 int word_length; |
261 while (word_iterator.GetNextWord(&word, &word_start, &word_length)) { | 270 while (word_iterator.GetNextWord(&word, &word_start, &word_length)) { |
262 if (!CheckSpelling(word, tag)) | 271 if (!CheckSpelling(word, tag)) |
263 return false; | 272 return false; |
264 } | 273 } |
265 return true; | 274 return true; |
266 } | 275 } |
OLD | NEW |