OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/bind.h" |
7 #include "base/file_util.h" | 8 #include "base/file_util.h" |
8 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/message_loop_proxy.h" |
9 #include "base/time.h" | 11 #include "base/time.h" |
10 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
11 #include "chrome/common/render_messages.h" | 13 #include "chrome/common/render_messages.h" |
12 #include "chrome/common/spellcheck_common.h" | 14 #include "chrome/common/spellcheck_common.h" |
13 #include "chrome/common/spellcheck_messages.h" | 15 #include "chrome/common/spellcheck_messages.h" |
14 #include "chrome/common/spellcheck_result.h" | 16 #include "chrome/common/spellcheck_result.h" |
15 #include "content/public/renderer/render_thread.h" | 17 #include "content/public/renderer/render_thread.h" |
16 #include "third_party/hunspell/src/hunspell/hunspell.hxx" | 18 #include "third_party/hunspell/src/hunspell/hunspell.hxx" |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCheckingComple
tion.h" |
| 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCheckingResult
.h" |
17 | 21 |
18 using base::TimeTicks; | 22 using base::TimeTicks; |
19 using content::RenderThread; | 23 using content::RenderThread; |
| 24 using WebKit::WebVector; |
| 25 using WebKit::WebTextCheckingResult; |
| 26 using WebKit::WebTextCheckingType; |
| 27 |
| 28 namespace spellcheck { |
| 29 void ToWebResultList( |
| 30 int offset, |
| 31 const std::vector<SpellCheckResult>& results, |
| 32 WebVector<WebTextCheckingResult>* web_results) { |
| 33 WebVector<WebTextCheckingResult> list(results.size()); |
| 34 for (size_t i = 0; i < results.size(); ++i) { |
| 35 list[i] = WebTextCheckingResult( |
| 36 static_cast<WebTextCheckingType>(results[i].type), |
| 37 results[i].location + offset, |
| 38 results[i].length, |
| 39 results[i].replacement); |
| 40 } |
| 41 |
| 42 list.swap(*web_results); |
| 43 } |
| 44 |
| 45 WebVector<WebTextCheckingResult> ToWebResultList( |
| 46 int offset, |
| 47 const std::vector<SpellCheckResult>& results) { |
| 48 WebVector<WebTextCheckingResult> web_results; |
| 49 ToWebResultList(offset, results, &web_results); |
| 50 return web_results; |
| 51 } |
| 52 } // namespace spellcheck |
| 53 |
| 54 class SpellCheck::SpellCheckRequestParam |
| 55 : public base::RefCountedThreadSafe<SpellCheck::SpellCheckRequestParam> { |
| 56 public: |
| 57 SpellCheckRequestParam(const string16& text, |
| 58 int offset, |
| 59 WebKit::WebTextCheckingCompletion* completion) |
| 60 : text_(text), |
| 61 offset_(offset), |
| 62 completion_(completion) { |
| 63 DCHECK(completion); |
| 64 } |
| 65 |
| 66 string16 text() { |
| 67 return text_; |
| 68 } |
| 69 |
| 70 int offset() { |
| 71 return offset_; |
| 72 } |
| 73 |
| 74 WebKit::WebTextCheckingCompletion* completion() { |
| 75 return completion_; |
| 76 } |
| 77 |
| 78 private: |
| 79 // Text to be checked in this task. |
| 80 string16 text_; |
| 81 |
| 82 // The text offset from the beginning. |
| 83 int offset_; |
| 84 |
| 85 // The interface to send the misspelled ranges to WebKit. |
| 86 WebKit::WebTextCheckingCompletion* completion_; |
| 87 |
| 88 DISALLOW_COPY_AND_ASSIGN(SpellCheckRequestParam); |
| 89 }; |
20 | 90 |
21 SpellCheck::SpellCheck() | 91 SpellCheck::SpellCheck() |
22 : file_(base::kInvalidPlatformFileValue), | 92 : file_(base::kInvalidPlatformFileValue), |
23 auto_spell_correct_turned_on_(false), | 93 auto_spell_correct_turned_on_(false), |
24 is_using_platform_spelling_engine_(false), | 94 is_using_platform_spelling_engine_(false), |
25 initialized_(false) { | 95 initialized_(false), |
| 96 dictionary_requested_(false) { |
26 // Wait till we check the first word before doing any initializing. | 97 // Wait till we check the first word before doing any initializing. |
27 } | 98 } |
28 | 99 |
29 SpellCheck::~SpellCheck() { | 100 SpellCheck::~SpellCheck() { |
30 } | 101 } |
31 | 102 |
32 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) { | 103 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) { |
33 bool handled = true; | 104 bool handled = true; |
34 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message) | 105 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message) |
35 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit) | 106 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit) |
36 IPC_MESSAGE_HANDLER(SpellCheckMsg_WordAdded, OnWordAdded) | 107 IPC_MESSAGE_HANDLER(SpellCheckMsg_WordAdded, OnWordAdded) |
37 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect, | 108 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect, |
38 OnEnableAutoSpellCorrect) | 109 OnEnableAutoSpellCorrect) |
39 IPC_MESSAGE_UNHANDLED(handled = false) | 110 IPC_MESSAGE_UNHANDLED(handled = false) |
40 IPC_END_MESSAGE_MAP() | 111 IPC_END_MESSAGE_MAP() |
41 | 112 |
42 return handled; | 113 return handled; |
43 } | 114 } |
44 | 115 |
45 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file, | 116 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file, |
46 const std::vector<std::string>& custom_words, | 117 const std::vector<std::string>& custom_words, |
47 const std::string& language, | 118 const std::string& language, |
48 bool auto_spell_correct) { | 119 bool auto_spell_correct) { |
49 Init(IPC::PlatformFileForTransitToPlatformFile(bdict_file), | 120 Init(IPC::PlatformFileForTransitToPlatformFile(bdict_file), |
50 custom_words, language); | 121 custom_words, language); |
51 auto_spell_correct_turned_on_ = auto_spell_correct; | 122 auto_spell_correct_turned_on_ = auto_spell_correct; |
| 123 |
| 124 PostDelayedSpellCheckTask(); |
52 } | 125 } |
53 | 126 |
54 void SpellCheck::OnWordAdded(const std::string& word) { | 127 void SpellCheck::OnWordAdded(const std::string& word) { |
55 if (is_using_platform_spelling_engine_) | 128 if (is_using_platform_spelling_engine_) |
56 return; | 129 return; |
57 | 130 |
58 if (!hunspell_.get()) { | 131 if (!hunspell_.get()) { |
59 // Save it for later---add it when hunspell is initialized. | 132 // Save it for later---add it when hunspell is initialized. |
60 custom_words_.push_back(word); | 133 custom_words_.push_back(word); |
61 } else { | 134 } else { |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 if (optional_suggestions) | 213 if (optional_suggestions) |
141 FillSuggestionList(word, optional_suggestions); | 214 FillSuggestionList(word, optional_suggestions); |
142 return false; | 215 return false; |
143 } | 216 } |
144 | 217 |
145 return true; | 218 return true; |
146 } | 219 } |
147 | 220 |
148 bool SpellCheck::SpellCheckParagraph( | 221 bool SpellCheck::SpellCheckParagraph( |
149 const string16& text, | 222 const string16& text, |
150 int tag, | |
151 std::vector<SpellCheckResult>* results) { | 223 std::vector<SpellCheckResult>* results) { |
152 #if !defined(OS_MACOSX) | 224 #if !defined(OS_MACOSX) |
153 // Mac has its own spell checker, so this method will not be used. | 225 // Mac has its own spell checker, so this method will not be used. |
154 | 226 |
155 DCHECK(results); | 227 DCHECK(results); |
156 | 228 |
157 size_t length = text.length(); | 229 size_t length = text.length(); |
158 size_t offset = 0; | 230 size_t offset = 0; |
159 | 231 |
160 // Spellcheck::SpellCheckWord() automatically breaks text into words and | 232 // Spellcheck::SpellCheckWord() automatically breaks text into words and |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 break; | 303 break; |
232 } | 304 } |
233 } | 305 } |
234 | 306 |
235 // Restore the swapped characters. | 307 // Restore the swapped characters. |
236 std::swap(misspelled_word[i], misspelled_word[i + 1]); | 308 std::swap(misspelled_word[i], misspelled_word[i + 1]); |
237 } | 309 } |
238 return autocorrect_word; | 310 return autocorrect_word; |
239 } | 311 } |
240 | 312 |
| 313 void SpellCheck::RequestTextChecking( |
| 314 const string16& text, |
| 315 int offset, |
| 316 WebKit::WebTextCheckingCompletion* completion) { |
| 317 #if !defined(OS_MACOSX) |
| 318 // Commented out on Mac, because SpellCheckRequest::PerformSpellCheck is not |
| 319 // implemented on Mac. Mac uses its own spellchecker, so this method |
| 320 // will not be used. |
| 321 |
| 322 DCHECK(!is_using_platform_spelling_engine_); |
| 323 |
| 324 // Clean up the previous request before starting a new request. |
| 325 if (pending_request_param_.get()) { |
| 326 pending_request_param_->completion()->didFinishCheckingText( |
| 327 WebKit::WebVector<WebKit::WebTextCheckingResult>()); |
| 328 pending_request_param_ = NULL; |
| 329 } |
| 330 |
| 331 if (InitializeIfNeeded()) { |
| 332 // We will check this text after we finish loading the hunspell dictionary. |
| 333 // Save parameters so that we can use them when we receive an init message |
| 334 // from the browser process. |
| 335 pending_request_param_ = new SpellCheckRequestParam( |
| 336 text, offset, completion); |
| 337 return; |
| 338 } |
| 339 |
| 340 requested_params_.push(new SpellCheckRequestParam(text, offset, completion)); |
| 341 base::MessageLoopProxy::current()->PostTask(FROM_HERE, |
| 342 base::Bind(&SpellCheck::PerformSpellCheck, AsWeakPtr())); |
| 343 #else |
| 344 NOTREACHED(); |
| 345 #endif |
| 346 } |
| 347 |
241 void SpellCheck::InitializeHunspell() { | 348 void SpellCheck::InitializeHunspell() { |
242 if (hunspell_.get()) | 349 if (hunspell_.get()) |
243 return; | 350 return; |
244 | 351 |
245 bdict_file_.reset(new file_util::MemoryMappedFile); | 352 bdict_file_.reset(new file_util::MemoryMappedFile); |
246 | 353 |
247 if (bdict_file_->Initialize(file_)) { | 354 if (bdict_file_->Initialize(file_)) { |
248 TimeTicks debug_start_time = base::Histogram::DebugNow(); | 355 TimeTicks debug_start_time = base::Histogram::DebugNow(); |
249 | 356 |
250 hunspell_.reset( | 357 hunspell_.reset( |
(...skipping 14 matching lines...) Expand all Loading... |
265 | 372 |
266 void SpellCheck::AddWordToHunspell(const std::string& word) { | 373 void SpellCheck::AddWordToHunspell(const std::string& word) { |
267 if (!word.empty() && word.length() < MAXWORDUTF8LEN) | 374 if (!word.empty() && word.length() < MAXWORDUTF8LEN) |
268 hunspell_->add(word.c_str()); | 375 hunspell_->add(word.c_str()); |
269 } | 376 } |
270 | 377 |
271 bool SpellCheck::InitializeIfNeeded() { | 378 bool SpellCheck::InitializeIfNeeded() { |
272 if (is_using_platform_spelling_engine_) | 379 if (is_using_platform_spelling_engine_) |
273 return false; | 380 return false; |
274 | 381 |
275 if (!initialized_) { | 382 if (!initialized_ && !dictionary_requested_) { |
276 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary); | 383 // RenderThread will not exist in test. |
277 initialized_ = true; | 384 if (RenderThread::Get()) |
| 385 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary); |
| 386 dictionary_requested_ = true; |
278 return true; | 387 return true; |
279 } | 388 } |
280 | 389 |
281 // Don't initialize if hunspell is disabled. | 390 // Don't initialize if hunspell is disabled. |
282 if (file_ != base::kInvalidPlatformFileValue) | 391 if (file_ != base::kInvalidPlatformFileValue) |
283 InitializeHunspell(); | 392 InitializeHunspell(); |
284 | 393 |
285 return false; | 394 return !initialized_; |
286 } | 395 } |
287 | 396 |
288 // When called, relays the request to check the spelling to the proper | 397 // When called, relays the request to check the spelling to the proper |
289 // backend, either hunspell or a platform-specific backend. | 398 // backend, either hunspell or a platform-specific backend. |
290 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { | 399 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { |
291 bool word_correct = false; | 400 bool word_correct = false; |
292 | 401 |
293 if (is_using_platform_spelling_engine_) { | 402 if (is_using_platform_spelling_engine_) { |
294 #if defined(OS_MACOSX) | 403 #if defined(OS_MACOSX) |
295 RenderThread::Get()->Send(new SpellCheckHostMsg_CheckSpelling( | 404 RenderThread::Get()->Send(new SpellCheckHostMsg_CheckSpelling( |
(...skipping 11 matching lines...) Expand all Loading... |
307 // If |hunspell_| is NULL here, an error has occurred, but it's better | 416 // If |hunspell_| is NULL here, an error has occurred, but it's better |
308 // to check rather than crash. | 417 // to check rather than crash. |
309 word_correct = true; | 418 word_correct = true; |
310 } | 419 } |
311 } | 420 } |
312 } | 421 } |
313 | 422 |
314 return word_correct; | 423 return word_correct; |
315 } | 424 } |
316 | 425 |
| 426 void SpellCheck::PostDelayedSpellCheckTask() { |
| 427 if (!pending_request_param_) |
| 428 return; |
| 429 |
| 430 if (file_ == base::kInvalidPlatformFileValue) { |
| 431 pending_request_param_->completion()->didFinishCheckingText( |
| 432 WebKit::WebVector<WebKit::WebTextCheckingResult>()); |
| 433 } else { |
| 434 requested_params_.push(pending_request_param_); |
| 435 base::MessageLoopProxy::current()->PostTask(FROM_HERE, |
| 436 base::Bind(&SpellCheck::PerformSpellCheck, AsWeakPtr())); |
| 437 } |
| 438 |
| 439 pending_request_param_ = NULL; |
| 440 } |
| 441 |
| 442 void SpellCheck::PerformSpellCheck() { |
| 443 #if !defined(OS_MACOSX) |
| 444 DCHECK(!requested_params_.empty()); |
| 445 scoped_refptr<SpellCheckRequestParam> param = requested_params_.front(); |
| 446 DCHECK(param); |
| 447 requested_params_.pop(); |
| 448 |
| 449 std::vector<SpellCheckResult> results; |
| 450 SpellCheckParagraph(param->text(), &results); |
| 451 param->completion()->didFinishCheckingText( |
| 452 spellcheck::ToWebResultList(param->offset(), results)); |
| 453 #else |
| 454 // SpellCheck::SpellCheckParagraph is not implemented on Mac, |
| 455 // so we return without spellchecking. Note that Mac uses its own |
| 456 // spellchecker, this function won't be used. |
| 457 NOTREACHED(); |
| 458 #endif |
| 459 } |
| 460 |
317 void SpellCheck::FillSuggestionList( | 461 void SpellCheck::FillSuggestionList( |
318 const string16& wrong_word, | 462 const string16& wrong_word, |
319 std::vector<string16>* optional_suggestions) { | 463 std::vector<string16>* optional_suggestions) { |
320 if (is_using_platform_spelling_engine_) { | 464 if (is_using_platform_spelling_engine_) { |
321 #if defined(OS_MACOSX) | 465 #if defined(OS_MACOSX) |
322 RenderThread::Get()->Send(new SpellCheckHostMsg_FillSuggestionList( | 466 RenderThread::Get()->Send(new SpellCheckHostMsg_FillSuggestionList( |
323 wrong_word, optional_suggestions)); | 467 wrong_word, optional_suggestions)); |
324 #endif | 468 #endif |
325 return; | 469 return; |
326 } | 470 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 | 504 |
361 string16 word; | 505 string16 word; |
362 int word_start; | 506 int word_start; |
363 int word_length; | 507 int word_length; |
364 while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) { | 508 while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) { |
365 if (!CheckSpelling(word, tag)) | 509 if (!CheckSpelling(word, tag)) |
366 return false; | 510 return false; |
367 } | 511 } |
368 return true; | 512 return true; |
369 } | 513 } |
OLD | NEW |