Chromium Code Reviews| 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 |