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.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 "content/public/renderer/render_thread.h" | 16 #include "content/public/renderer/render_thread.h" |
15 #include "third_party/hunspell/src/hunspell/hunspell.hxx" | 17 #include "third_party/hunspell/src/hunspell/hunspell.hxx" |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCheckingComple tion.h" | |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCheckingResult .h" | 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCheckingResult .h" |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" | |
17 | 21 |
18 using base::TimeTicks; | 22 using base::TimeTicks; |
19 using content::RenderThread; | 23 using content::RenderThread; |
20 | 24 |
25 // A request that checks the spellings of the specified text. | |
26 class SpellCheck::SpellCheckRequest | |
27 : public base::RefCountedThreadSafe<SpellCheck::SpellCheckRequest> { | |
Hironori Bono
2012/01/27 06:17:41
I do not think we need a new class for this use-ca
shinyak
2012/01/30 06:53:05
Actually |text| is a temporary object, so the refe
| |
28 public: | |
29 SpellCheckRequest(const string16& text, | |
30 int tag, | |
31 WebKit::WebTextCheckingCompletion* completion, | |
32 SpellCheck* spellcheck) | |
33 : text_(text), | |
34 tag_(tag), | |
35 completion_(completion), | |
36 spellcheck_(spellcheck) { | |
37 DCHECK(completion); | |
38 DCHECK(spellcheck); | |
39 } | |
40 | |
41 // Performs spellchecking and calls the callback function. | |
42 void PerformSpellCheck() { | |
43 #if !defined(OS_MACOSX) | |
44 std::vector<WebKit::WebTextCheckingResult> results; | |
45 spellcheck_->SpellCheckParagraph(text_, tag_, &results); | |
Hironori Bono
2012/01/27 06:17:41
This call surely creates confusing crashes when a
shinyak
2012/01/30 06:53:05
Done.
| |
46 completion_->didFinishCheckingText(results); | |
47 #else | |
48 // SpellCheck::SpellCheckParagraph is not implemented on Mac, | |
49 // so we return without spellchecking. Note that Mac uses its own | |
50 // spellchecker, this function won't be used. | |
Hironori Bono
2012/01/27 06:17:41
Use NOTREACHED() and let a renderer crash when som
shinyak
2012/01/30 06:53:05
Done.
| |
51 completion_->didFinishCheckingText( | |
52 WebKit::WebVector<WebKit::WebTextCheckingResult>()); | |
53 #endif | |
54 } | |
55 | |
56 // Calls the callback function without doing spellchecking. | |
57 // Text is considered as having no misspellings. | |
58 void CancelSpellCheckRequest() { | |
59 completion_->didFinishCheckingText( | |
60 WebKit::WebVector<WebKit::WebTextCheckingResult>()); | |
61 } | |
62 | |
63 private: | |
64 friend class base::RefCountedThreadSafe<SpellCheckRequest>; | |
65 | |
66 ~SpellCheckRequest() { | |
67 } | |
68 | |
69 // Text to be checked in this task. | |
70 string16 text_; | |
71 | |
72 // The document tag provided by WebKit. | |
73 int tag_; | |
74 | |
75 // The interface to send the misspelled ranges to WebKit. | |
76 WebKit::WebTextCheckingCompletion* completion_; | |
77 | |
78 // The spellchecker shared in this process. | |
79 SpellCheck* spellcheck_; | |
80 }; | |
81 | |
21 SpellCheck::SpellCheck() | 82 SpellCheck::SpellCheck() |
22 : file_(base::kInvalidPlatformFileValue), | 83 : file_(base::kInvalidPlatformFileValue), |
23 auto_spell_correct_turned_on_(false), | 84 auto_spell_correct_turned_on_(false), |
24 is_using_platform_spelling_engine_(false), | 85 is_using_platform_spelling_engine_(false), |
25 initialized_(false) { | 86 initialized_(false), |
87 dictionary_requested_(false) { | |
26 // Wait till we check the first word before doing any initializing. | 88 // Wait till we check the first word before doing any initializing. |
27 } | 89 } |
28 | 90 |
29 SpellCheck::~SpellCheck() { | 91 SpellCheck::~SpellCheck() { |
30 } | 92 } |
31 | 93 |
32 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) { | 94 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) { |
33 bool handled = true; | 95 bool handled = true; |
34 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message) | 96 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message) |
35 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit) | 97 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit) |
36 IPC_MESSAGE_HANDLER(SpellCheckMsg_WordAdded, OnWordAdded) | 98 IPC_MESSAGE_HANDLER(SpellCheckMsg_WordAdded, OnWordAdded) |
37 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect, | 99 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect, |
38 OnEnableAutoSpellCorrect) | 100 OnEnableAutoSpellCorrect) |
39 IPC_MESSAGE_UNHANDLED(handled = false) | 101 IPC_MESSAGE_UNHANDLED(handled = false) |
40 IPC_END_MESSAGE_MAP() | 102 IPC_END_MESSAGE_MAP() |
41 | 103 |
42 return handled; | 104 return handled; |
43 } | 105 } |
44 | 106 |
45 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file, | 107 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file, |
46 const std::vector<std::string>& custom_words, | 108 const std::vector<std::string>& custom_words, |
47 const std::string& language, | 109 const std::string& language, |
48 bool auto_spell_correct) { | 110 bool auto_spell_correct) { |
49 Init(IPC::PlatformFileForTransitToPlatformFile(bdict_file), | 111 Init(IPC::PlatformFileForTransitToPlatformFile(bdict_file), |
50 custom_words, language); | 112 custom_words, language); |
51 auto_spell_correct_turned_on_ = auto_spell_correct; | 113 auto_spell_correct_turned_on_ = auto_spell_correct; |
114 | |
115 PostDelayedSpellCheckTask(); | |
52 } | 116 } |
53 | 117 |
54 void SpellCheck::OnWordAdded(const std::string& word) { | 118 void SpellCheck::OnWordAdded(const std::string& word) { |
55 if (is_using_platform_spelling_engine_) | 119 if (is_using_platform_spelling_engine_) |
56 return; | 120 return; |
57 | 121 |
58 if (!hunspell_.get()) { | 122 if (!hunspell_.get()) { |
59 // Save it for later---add it when hunspell is initialized. | 123 // Save it for later---add it when hunspell is initialized. |
60 custom_words_.push_back(word); | 124 custom_words_.push_back(word); |
61 } else { | 125 } else { |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 break; | 295 break; |
232 } | 296 } |
233 } | 297 } |
234 | 298 |
235 // Restore the swapped characters. | 299 // Restore the swapped characters. |
236 std::swap(misspelled_word[i], misspelled_word[i + 1]); | 300 std::swap(misspelled_word[i], misspelled_word[i + 1]); |
237 } | 301 } |
238 return autocorrect_word; | 302 return autocorrect_word; |
239 } | 303 } |
240 | 304 |
305 void SpellCheck::RequestTextChecking( | |
306 const string16& text, | |
307 int tag, | |
308 WebKit::WebTextCheckingCompletion* completion) { | |
309 #if !defined(OS_MACOSX) | |
310 // Commented out on Mac, because SpellCheckRequest::PerformSpellCheck is not | |
311 // implemented on Mac. Mac uses its own spellchecker, so this method | |
312 // will not be used. | |
313 | |
314 DCHECK(!is_using_platform_spelling_engine_); | |
315 | |
316 // Clean up the previous request before starting a new request. | |
317 if (pending_request_) { | |
318 pending_request_->CancelSpellCheckRequest(); | |
319 pending_request_ = NULL; | |
320 } | |
321 | |
322 if (InitializeIfNeeded()) { | |
323 // We will check this text after we finish loading the hunspell dictionary. | |
324 // Save parameters so that we can use them when we receive an init message | |
325 // from the browser process. | |
326 pending_request_ = new SpellCheckRequest(text, tag, completion, this); | |
327 return; | |
328 } | |
329 | |
330 scoped_refptr<SpellCheckRequest> request( | |
331 new SpellCheckRequest(text, tag, completion, this)); | |
332 MessageLoop::current()->PostTask(FROM_HERE, | |
333 base::Bind(&SpellCheckRequest::PerformSpellCheck, request)); | |
334 #endif | |
335 } | |
336 | |
241 void SpellCheck::InitializeHunspell() { | 337 void SpellCheck::InitializeHunspell() { |
242 if (hunspell_.get()) | 338 if (hunspell_.get()) |
243 return; | 339 return; |
244 | 340 |
245 bdict_file_.reset(new file_util::MemoryMappedFile); | 341 bdict_file_.reset(new file_util::MemoryMappedFile); |
246 | 342 |
247 if (bdict_file_->Initialize(file_)) { | 343 if (bdict_file_->Initialize(file_)) { |
248 TimeTicks debug_start_time = base::Histogram::DebugNow(); | 344 TimeTicks debug_start_time = base::Histogram::DebugNow(); |
249 | 345 |
250 hunspell_.reset( | 346 hunspell_.reset( |
(...skipping 14 matching lines...) Expand all Loading... | |
265 | 361 |
266 void SpellCheck::AddWordToHunspell(const std::string& word) { | 362 void SpellCheck::AddWordToHunspell(const std::string& word) { |
267 if (!word.empty() && word.length() < MAXWORDUTF8LEN) | 363 if (!word.empty() && word.length() < MAXWORDUTF8LEN) |
268 hunspell_->add(word.c_str()); | 364 hunspell_->add(word.c_str()); |
269 } | 365 } |
270 | 366 |
271 bool SpellCheck::InitializeIfNeeded() { | 367 bool SpellCheck::InitializeIfNeeded() { |
272 if (is_using_platform_spelling_engine_) | 368 if (is_using_platform_spelling_engine_) |
273 return false; | 369 return false; |
274 | 370 |
275 if (!initialized_) { | 371 if (!initialized_ && !dictionary_requested_) { |
276 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary); | 372 // RenderThread will not exist in test. |
277 initialized_ = true; | 373 if (RenderThread::Get()) |
374 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary); | |
375 dictionary_requested_ = true; | |
278 return true; | 376 return true; |
279 } | 377 } |
280 | 378 |
281 // Don't initialize if hunspell is disabled. | 379 // Don't initialize if hunspell is disabled. |
282 if (file_ != base::kInvalidPlatformFileValue) | 380 if (file_ != base::kInvalidPlatformFileValue) |
283 InitializeHunspell(); | 381 InitializeHunspell(); |
284 | 382 |
285 return false; | 383 return !initialized_; |
286 } | 384 } |
287 | 385 |
288 // When called, relays the request to check the spelling to the proper | 386 // When called, relays the request to check the spelling to the proper |
289 // backend, either hunspell or a platform-specific backend. | 387 // backend, either hunspell or a platform-specific backend. |
290 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { | 388 bool SpellCheck::CheckSpelling(const string16& word_to_check, int tag) { |
291 bool word_correct = false; | 389 bool word_correct = false; |
292 | 390 |
293 if (is_using_platform_spelling_engine_) { | 391 if (is_using_platform_spelling_engine_) { |
294 #if defined(OS_MACOSX) | 392 #if defined(OS_MACOSX) |
295 RenderThread::Get()->Send(new SpellCheckHostMsg_CheckSpelling( | 393 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 | 405 // If |hunspell_| is NULL here, an error has occurred, but it's better |
308 // to check rather than crash. | 406 // to check rather than crash. |
309 word_correct = true; | 407 word_correct = true; |
310 } | 408 } |
311 } | 409 } |
312 } | 410 } |
313 | 411 |
314 return word_correct; | 412 return word_correct; |
315 } | 413 } |
316 | 414 |
415 void SpellCheck::PostDelayedSpellCheckTask() { | |
416 if (!pending_request_) | |
417 return; | |
418 | |
419 if (file_ == base::kInvalidPlatformFileValue) { | |
420 pending_request_->CancelSpellCheckRequest(); | |
421 } else { | |
422 MessageLoop::current()->PostTask(FROM_HERE, | |
Hironori Bono
2012/01/27 06:17:41
This is an old way to post a task. (This code cras
shinyak
2012/01/30 06:53:05
Done.
| |
423 base::Bind(&SpellCheckRequest::PerformSpellCheck, pending_request_)); | |
424 } | |
425 | |
426 pending_request_ = NULL; | |
427 } | |
428 | |
317 void SpellCheck::FillSuggestionList( | 429 void SpellCheck::FillSuggestionList( |
318 const string16& wrong_word, | 430 const string16& wrong_word, |
319 std::vector<string16>* optional_suggestions) { | 431 std::vector<string16>* optional_suggestions) { |
320 if (is_using_platform_spelling_engine_) { | 432 if (is_using_platform_spelling_engine_) { |
321 #if defined(OS_MACOSX) | 433 #if defined(OS_MACOSX) |
322 RenderThread::Get()->Send(new SpellCheckHostMsg_FillSuggestionList( | 434 RenderThread::Get()->Send(new SpellCheckHostMsg_FillSuggestionList( |
323 wrong_word, optional_suggestions)); | 435 wrong_word, optional_suggestions)); |
324 #endif | 436 #endif |
325 return; | 437 return; |
326 } | 438 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
360 | 472 |
361 string16 word; | 473 string16 word; |
362 int word_start; | 474 int word_start; |
363 int word_length; | 475 int word_length; |
364 while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) { | 476 while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) { |
365 if (!CheckSpelling(word, tag)) | 477 if (!CheckSpelling(word, tag)) |
366 return false; | 478 return false; |
367 } | 479 } |
368 return true; | 480 return true; |
369 } | 481 } |
OLD | NEW |