Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: chrome/browser/spellchecker.cc

Issue 269020: Spellchecker:... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: tony comments Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/spellchecker.h ('k') | tools/valgrind/memcheck/suppressions.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-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/browser/spellchecker.h"
6
5 #include "app/l10n_util.h" 7 #include "app/l10n_util.h"
6 #include "chrome/browser/spellchecker.h"
7 #include "chrome/browser/spellchecker_common.h"
8 #include "chrome/browser/spellchecker_platform_engine.h"
9 #include "base/basictypes.h" 8 #include "base/basictypes.h"
10 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
11 #include "base/file_util.h" 10 #include "base/file_util.h"
12 #include "base/histogram.h" 11 #include "base/histogram.h"
13 #include "base/logging.h" 12 #include "base/logging.h"
14 #include "base/path_service.h" 13 #include "base/path_service.h"
15 #include "base/stats_counters.h" 14 #include "base/stats_counters.h"
16 #include "base/string_util.h" 15 #include "base/string_util.h"
17 #include "base/thread.h" 16 #include "base/thread.h"
18 #include "chrome/browser/browser_process.h" 17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_thread.h"
19 #include "chrome/browser/net/url_fetcher.h" 19 #include "chrome/browser/net/url_fetcher.h"
20 #include "chrome/browser/profile.h" 20 #include "chrome/browser/profile.h"
21 #include "chrome/browser/spellchecker_common.h"
22 #include "chrome/browser/spellchecker_platform_engine.h"
21 #include "chrome/common/chrome_constants.h" 23 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_counters.h" 24 #include "chrome/common/chrome_counters.h"
23 #include "chrome/common/chrome_paths.h" 25 #include "chrome/common/chrome_paths.h"
24 #include "chrome/common/pref_names.h" 26 #include "chrome/common/pref_names.h"
25 #include "chrome/common/pref_service.h" 27 #include "chrome/common/pref_service.h"
26 #include "third_party/hunspell/src/hunspell/hunspell.hxx"
27 #include "grit/generated_resources.h" 28 #include "grit/generated_resources.h"
28 #include "grit/locale_settings.h" 29 #include "grit/locale_settings.h"
29 #include "net/url_request/url_request.h" 30 #include "net/url_request/url_request.h"
31 #include "third_party/hunspell/src/hunspell/hunspell.hxx"
30 32
31 using base::TimeTicks; 33 using base::TimeTicks;
32 34
33
34
35 namespace { 35 namespace {
36 36
37 static const struct { 37 static const struct {
38 // The language. 38 // The language.
39 const char* language; 39 const char* language;
40 40
41 // The corresponding language and region, used by the dictionaries. 41 // The corresponding language and region, used by the dictionaries.
42 const char* language_region; 42 const char* language_region;
43 } g_supported_spellchecker_languages[] = { 43 } g_supported_spellchecker_languages[] = {
44 {"en-US", "en-US"}, 44 {"en-US", "en-US"},
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 return dict_dir_userdata; 86 return dict_dir_userdata;
87 } 87 }
88 88
89 bool SaveBufferToFile(const std::string& data, 89 bool SaveBufferToFile(const std::string& data,
90 FilePath file_to_write) { 90 FilePath file_to_write) {
91 int num_bytes = data.length(); 91 int num_bytes = data.length();
92 return file_util::WriteFile(file_to_write, data.data(), num_bytes) == 92 return file_util::WriteFile(file_to_write, data.data(), num_bytes) ==
93 num_bytes; 93 num_bytes;
94 } 94 }
95 95
96 } 96 } // namespace
97 97
98 // This is a helper class which acts as a proxy for invoking a task from the 98 // This is a helper class which acts as a proxy for invoking a task from the
99 // file loop back to the IO loop. Invoking a task from file loop to the IO 99 // file loop back to the IO loop. Invoking a task from file loop to the IO
100 // loop directly is not safe as during browser shutdown, the IO loop tears 100 // loop directly is not safe as during browser shutdown, the IO loop tears
101 // down before the file loop. To avoid a crash, this object is invoked in the 101 // down before the file loop. To avoid a crash, this object is invoked in the
102 // UI loop from the file loop, from where it gets the IO thread directly from 102 // UI loop from the file loop, from where it gets the IO thread directly from
103 // g_browser_process and invokes the given task in the IO loop if it is not 103 // g_browser_process and invokes the given task in the IO loop if it is not
104 // NULL. This object also takes ownership of the given task. 104 // NULL. This object also takes ownership of the given task.
105 class UIProxyForIOTask : public Task { 105 class UIProxyForIOTask : public Task {
106 public: 106 public:
107 explicit UIProxyForIOTask(Task* on_dictionary_save_complete_callback_task) 107 explicit UIProxyForIOTask(Task* callback_task, SpellChecker* spellchecker)
108 : on_dictionary_save_complete_callback_task_( 108 : callback_task_(callback_task),
109 on_dictionary_save_complete_callback_task) { 109 spellchecker_(spellchecker) {
110 } 110 }
111 111
112 private: 112 private:
113 void Run(); 113 void Run();
114 114
115 Task* on_dictionary_save_complete_callback_task_; 115 Task* callback_task_;
116 // The SpellChecker that invoked the file loop task. May be NULL. If not
117 // NULL, then we will Release() on it if we don't run |callback_task_|. This
118 // balances any refs the spellchecker might have had outstanding which it
119 // would have Released() when |callback_task_| was run.
120 SpellChecker* spellchecker_;
116 DISALLOW_COPY_AND_ASSIGN(UIProxyForIOTask); 121 DISALLOW_COPY_AND_ASSIGN(UIProxyForIOTask);
117 }; 122 };
118 123
119 void UIProxyForIOTask::Run() { 124 void UIProxyForIOTask::Run() {
120 // This has been invoked in the UI thread. 125 // This has been invoked in the UI thread.
121 base::Thread* io_thread = g_browser_process->io_thread(); 126 base::Thread* io_thread = g_browser_process->io_thread();
122 if (io_thread) { // io_thread has not been torn down yet. 127 if (io_thread) { // io_thread has not been torn down yet.
123 MessageLoop* io_loop = io_thread->message_loop(); 128 MessageLoop* io_loop = io_thread->message_loop();
124 io_loop->PostTask(FROM_HERE, 129 io_loop->PostTask(FROM_HERE, callback_task_);
125 on_dictionary_save_complete_callback_task_); 130 } else {
126 on_dictionary_save_complete_callback_task_ = NULL; 131 if (spellchecker_)
132 spellchecker_->Release();
133 delete callback_task_;
127 } 134 }
135
136 callback_task_ = NULL;
128 } 137 }
129 138
130 // Design: The spellchecker initializes hunspell_ in the Initialize() method. 139 // Design: The spellchecker initializes hunspell_ in the Initialize() method.
131 // This is done using the dictionary file on disk, e.g. "en-US_1_1.bdic". 140 // This is done using the dictionary file on disk, e.g. "en-US_1_1.bdic".
132 // Initialization of hunspell_ is held off during this process. If the 141 // Initialization of hunspell_ is held off during this process. If the
133 // dictionaryis not available, we first attempt to download and save it. After 142 // dictionary is not available, we first attempt to download and save it. After
134 // the dictionary is downloaded and saved to disk (or the attempt to do so 143 // the dictionary is downloaded and saved to disk (or the attempt to do so
135 // fails)), corresponding flags are set 144 // fails)), corresponding flags are set
136 // in spellchecker - in the IO thread. Since IO thread goes first during closing 145 // in spellchecker - in the IO thread. Since IO thread goes first during closing
137 // of browser, a proxy task |UIProxyForIOTask| is created in the UI thread, 146 // of browser, a proxy task |UIProxyForIOTask| is created in the UI thread,
138 // which obtains the IO thread independently and invokes the task in the IO 147 // which obtains the IO thread independently and invokes the task in the IO
139 // thread if it's not NULL. After the flags are cleared, a (final) attempt is 148 // thread if it's not NULL. After the flags are cleared, a (final) attempt is
140 // made to initialize hunspell_. If it fails even then (dictionary could not 149 // made to initialize hunspell_. If it fails even then (dictionary could not
141 // download), no more attempts are made to initialize it. 150 // download), no more attempts are made to initialize it.
142 class SaveDictionaryTask : public Task { 151 class SaveDictionaryTask : public Task {
143 public: 152 public:
144 SaveDictionaryTask(Task* on_dictionary_save_complete_callback_task, 153 SaveDictionaryTask(Task* on_dictionary_save_complete_callback_task,
145 const FilePath& first_attempt_file_name, 154 const FilePath& first_attempt_file_name,
146 const FilePath& fallback_file_name, 155 const FilePath& fallback_file_name,
147 const std::string& data, 156 const std::string& data)
148 MessageLoop* ui_loop)
149 : on_dictionary_save_complete_callback_task_( 157 : on_dictionary_save_complete_callback_task_(
150 on_dictionary_save_complete_callback_task), 158 on_dictionary_save_complete_callback_task),
151 first_attempt_file_name_(first_attempt_file_name), 159 first_attempt_file_name_(first_attempt_file_name),
152 fallback_file_name_(fallback_file_name), 160 fallback_file_name_(fallback_file_name),
153 data_(data), 161 data_(data) {
154 ui_loop_(ui_loop) {
155 } 162 }
156 163
157 private: 164 private:
158 void Run(); 165 void Run();
159 166
160 bool SaveBufferToFile(const std::string& data, 167 bool SaveBufferToFile(const std::string& data,
161 FilePath file_to_write) { 168 FilePath file_to_write) {
162 int num_bytes = data.length(); 169 int num_bytes = data.length();
163 return file_util::WriteFile(file_to_write, data.data(), num_bytes) == 170 return file_util::WriteFile(file_to_write, data.data(), num_bytes) ==
164 num_bytes; 171 num_bytes;
165 } 172 }
166 173
167 // factory object to invokelater back to spellchecker in io thread on 174 // factory object to invokelater back to spellchecker in io thread on
168 // download completion to change appropriate flags. 175 // download completion to change appropriate flags.
169 Task* on_dictionary_save_complete_callback_task_; 176 Task* on_dictionary_save_complete_callback_task_;
170 177
171 // The file which will be stored in the first attempt. 178 // The file which will be stored in the first attempt.
172 FilePath first_attempt_file_name_; 179 FilePath first_attempt_file_name_;
173 180
174 // The file which will be stored as a fallback. 181 // The file which will be stored as a fallback.
175 FilePath fallback_file_name_; 182 FilePath fallback_file_name_;
176 183
177 // The buffer which has to be stored to disk. 184 // The buffer which has to be stored to disk.
178 std::string data_; 185 std::string data_;
179 186
180 // This invokes back to io loop when downloading is over. 187 // This invokes back to io loop when downloading is over.
181 MessageLoop* ui_loop_;
182 DISALLOW_COPY_AND_ASSIGN(SaveDictionaryTask); 188 DISALLOW_COPY_AND_ASSIGN(SaveDictionaryTask);
183 }; 189 };
184 190
185 void SaveDictionaryTask::Run() { 191 void SaveDictionaryTask::Run() {
186 if (!SaveBufferToFile(data_, first_attempt_file_name_)) { 192 if (!SaveBufferToFile(data_, first_attempt_file_name_)) {
187 // Try saving it to |fallback_file_name_|, which almost surely has 193 // Try saving it to |fallback_file_name_|, which almost surely has
188 // write permission. If even this fails, there is nothing to be done. 194 // write permission. If even this fails, there is nothing to be done.
189 FilePath fallback_dir = fallback_file_name_.DirName(); 195 FilePath fallback_dir = fallback_file_name_.DirName();
190 // Create the directory if it does not exist. 196 // Create the directory if it does not exist.
191 if (!file_util::PathExists(fallback_dir)) 197 if (!file_util::PathExists(fallback_dir))
192 file_util::CreateDirectory(fallback_dir); 198 file_util::CreateDirectory(fallback_dir);
193 SaveBufferToFile(data_, fallback_file_name_); 199 SaveBufferToFile(data_, fallback_file_name_);
194 } // Unsuccessful save is taken care of in SpellChecker::Initialize(). 200 } // Unsuccessful save is taken care of in SpellChecker::Initialize().
195 201
196 // Set Flag that dictionary is not downloading anymore. 202 // Set Flag that dictionary is not downloading anymore.
197 ui_loop_->PostTask(FROM_HERE, 203 MessageLoop* ui_loop = ChromeThread::GetMessageLoop(ChromeThread::UI);
198 new UIProxyForIOTask(on_dictionary_save_complete_callback_task_)); 204 ui_loop->PostTask(FROM_HERE,
205 new UIProxyForIOTask(on_dictionary_save_complete_callback_task_, NULL));
199 } 206 }
200 207
208 // Design: this task tries to read the dictionary from disk and load it into
209 // memory. It is executed on the file thread, and posts the results back to
210 // the IO thread (via the UI thread---see UIProxyForIOTask).
211 // The task first checks for the existence of the dictionary in one of the two
212 // given locations. If it does not exist, the task informs the SpellChecker,
213 // which will try to download the directory and run a new ReadDictionaryTask.
214 class ReadDictionaryTask : public Task {
215 public:
216 ReadDictionaryTask(SpellChecker* spellchecker,
217 const FilePath& dict_file_name_app,
218 const FilePath& dict_file_name_usr)
219 : spellchecker_(spellchecker),
220 hunspell_(NULL),
221 bdict_file_(NULL),
222 custom_dictionary_file_name_(
223 spellchecker->custom_dictionary_file_name_),
224 dict_file_name_app_(dict_file_name_app),
225 dict_file_name_usr_(dict_file_name_usr) {
226 }
227
228 virtual void Run() {
229 FilePath bdict_file_path;
230 if (file_util::PathExists(dict_file_name_app_)) {
231 bdict_file_path = dict_file_name_app_;
232 } else if (file_util::PathExists(dict_file_name_usr_)) {
233 bdict_file_path = dict_file_name_usr_;
234 } else {
235 Finish(false);
236 return;
237 }
238
239 bdict_file_ = new file_util::MemoryMappedFile;
240 if (bdict_file_->Initialize(bdict_file_path)) {
241 TimeTicks start_time = TimeTicks::Now();
242
243 hunspell_ =
244 new Hunspell(bdict_file_->data(), bdict_file_->length());
245
246 // Add custom words to Hunspell.
247 std::string contents;
248 file_util::ReadFileToString(custom_dictionary_file_name_, &contents);
249 std::vector<std::string> list_of_words;
250 SplitString(contents, '\n', &list_of_words);
251 for (std::vector<std::string>::iterator it = list_of_words.begin();
252 it != list_of_words.end(); ++it) {
253 hunspell_->add(it->c_str());
254 }
255
256 DHISTOGRAM_TIMES("Spellcheck.InitTime",
257 TimeTicks::Now() - start_time);
258 } else {
259 delete bdict_file_;
260 bdict_file_ = NULL;
261 }
262
263 Finish(true);
264 }
265
266 private:
267 void Finish(bool file_existed) {
268 Task* task = NewRunnableMethod(spellchecker_, &SpellChecker::HunspellInited,
269 hunspell_, bdict_file_, file_existed);
270 if (spellchecker_->file_loop_) {
271 MessageLoop* ui_loop = ChromeThread::GetMessageLoop(ChromeThread::UI);
272 // We were called on the file loop. Post back to the IO loop.
273 // If this never gets posted to the IO loop, then we will leak |hunspell_|
274 // and |bdict_file_|. But that can only happen during shutdown, so it's
275 // not worth caring about.
276 ui_loop->PostTask(FROM_HERE, new UIProxyForIOTask(task, spellchecker_));
277 } else {
278 // We were called directly (e.g., during testing). Run the task directly.
279 task->Run();
280 delete task;
281 }
282 }
283
284 // The SpellChecker we are working for. We are guaranteed to be outlived
285 // by this object because it AddRefs() itself before calling us.
286 // Accessing it is not necessarily thread safe, but are careful to only access
287 // it in ways that are.
288 SpellChecker* spellchecker_;
289 Hunspell* hunspell_;
290 file_util::MemoryMappedFile* bdict_file_;
291
292 FilePath custom_dictionary_file_name_;
293 FilePath dict_file_name_app_;
294 FilePath dict_file_name_usr_;
295
296 DISALLOW_COPY_AND_ASSIGN(ReadDictionaryTask);
297 };
298
201 void SpellChecker::SpellCheckLanguages(std::vector<std::string>* languages) { 299 void SpellChecker::SpellCheckLanguages(std::vector<std::string>* languages) {
202 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(g_supported_spellchecker_languages); 300 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(g_supported_spellchecker_languages);
203 ++i) 301 ++i) {
204 languages->push_back(g_supported_spellchecker_languages[i].language); 302 languages->push_back(g_supported_spellchecker_languages[i].language);
303 }
205 } 304 }
206 305
207 // This function returns the language-region version of language name. 306 // This function returns the language-region version of language name.
208 // e.g. returns hi-IN for hi. 307 // e.g. returns hi-IN for hi.
209 std::string SpellChecker::GetSpellCheckLanguageRegion( 308 std::string SpellChecker::GetSpellCheckLanguageRegion(
210 std::string input_language) { 309 std::string input_language) {
211 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(g_supported_spellchecker_languages); 310 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(g_supported_spellchecker_languages);
212 ++i) { 311 ++i) {
213 std::string language( 312 std::string language(
214 g_supported_spellchecker_languages[i].language); 313 g_supported_spellchecker_languages[i].language);
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 } 462 }
364 463
365 SpellChecker::SpellChecker(const FilePath& dict_dir, 464 SpellChecker::SpellChecker(const FilePath& dict_dir,
366 const std::string& language, 465 const std::string& language,
367 URLRequestContext* request_context, 466 URLRequestContext* request_context,
368 const FilePath& custom_dictionary_file_name) 467 const FilePath& custom_dictionary_file_name)
369 : given_dictionary_directory_(dict_dir), 468 : given_dictionary_directory_(dict_dir),
370 custom_dictionary_file_name_(custom_dictionary_file_name), 469 custom_dictionary_file_name_(custom_dictionary_file_name),
371 tried_to_init_(false), 470 tried_to_init_(false),
372 language_(language), 471 language_(language),
373 #ifndef NDEBUG
374 worker_loop_(NULL), 472 worker_loop_(NULL),
375 #endif
376 tried_to_download_dictionary_file_(false), 473 tried_to_download_dictionary_file_(false),
377 file_loop_(NULL), 474 file_loop_(NULL),
378 ui_loop_(MessageLoop::current()),
379 url_request_context_(request_context), 475 url_request_context_(request_context),
380 obtaining_dictionary_(false), 476 obtaining_dictionary_(false),
381 auto_spell_correct_turned_on_(false), 477 auto_spell_correct_turned_on_(false),
382 is_using_platform_spelling_engine_(false), 478 is_using_platform_spelling_engine_(false),
383 fetcher_(NULL), 479 fetcher_(NULL),
384 ALLOW_THIS_IN_INITIALIZER_LIST( 480 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
385 on_dictionary_save_complete_callback_factory_(this)) {
386 if (SpellCheckerPlatform::SpellCheckerAvailable()) { 481 if (SpellCheckerPlatform::SpellCheckerAvailable()) {
387 SpellCheckerPlatform::Init(); 482 SpellCheckerPlatform::Init();
388 if (SpellCheckerPlatform::PlatformSupportsLanguage(language)) { 483 if (SpellCheckerPlatform::PlatformSupportsLanguage(language)) {
389 // If we have reached here, then we know that the current platform 484 // If we have reached here, then we know that the current platform
390 // supports the given language and we will use it instead of hunspell. 485 // supports the given language and we will use it instead of hunspell.
391 SpellCheckerPlatform::SetLanguage(language); 486 SpellCheckerPlatform::SetLanguage(language);
392 is_using_platform_spelling_engine_ = true; 487 is_using_platform_spelling_engine_ = true;
393 } 488 }
394 } 489 }
395 490
(...skipping 12 matching lines...) Expand all
408 custom_dictionary_file_name_ = 503 custom_dictionary_file_name_ =
409 personal_file_directory.Append(chrome::kCustomDictionaryFileName); 504 personal_file_directory.Append(chrome::kCustomDictionaryFileName);
410 } 505 }
411 506
412 // Use this dictionary language as the default one of the 507 // Use this dictionary language as the default one of the
413 // SpellcheckCharAttribute object. 508 // SpellcheckCharAttribute object.
414 character_attributes_.SetDefaultLanguage(language); 509 character_attributes_.SetDefaultLanguage(language);
415 } 510 }
416 511
417 SpellChecker::~SpellChecker() { 512 SpellChecker::~SpellChecker() {
418 #ifndef NDEBUG
419 // This must be deleted on the I/O thread (see the header). This is the same 513 // This must be deleted on the I/O thread (see the header). This is the same
420 // thread thatSpellCheckWord is called on, so we verify that they were all the 514 // thread that SpellCheckWord is called on, so we verify that they were all
421 // same thread. 515 // the same thread.
422 if (worker_loop_) 516 if (worker_loop_)
423 DCHECK(MessageLoop::current() == worker_loop_); 517 DCHECK(MessageLoop::current() == worker_loop_);
424 #endif
425 } 518 }
426 519
427 void SpellChecker::StartDictionaryDownload(const FilePath& file_name) { 520 void SpellChecker::StartDictionaryDownload(const FilePath& file_name) {
428 // Determine URL of file to download. 521 // Determine URL of file to download.
429 static const char kDownloadServerUrl[] = 522 static const char kDownloadServerUrl[] =
430 "http://cache.pack.google.com/edgedl/chrome/dict/"; 523 "http://cache.pack.google.com/edgedl/chrome/dict/";
431 GURL url = GURL(std::string(kDownloadServerUrl) + WideToUTF8( 524 GURL url = GURL(std::string(kDownloadServerUrl) + WideToUTF8(
432 l10n_util::ToLower(bdic_file_name_.ToWStringHack()))); 525 l10n_util::ToLower(bdic_file_name_.ToWStringHack())));
433 fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this)); 526 fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this));
434 fetcher_->set_request_context(url_request_context_); 527 fetcher_->set_request_context(url_request_context_);
(...skipping 13 matching lines...) Expand all
448 response_code == 407)) { 541 response_code == 407)) {
449 obtaining_dictionary_ = false; 542 obtaining_dictionary_ = false;
450 return; 543 return;
451 } 544 }
452 545
453 // Save the file in the file thread, and not here, the IO thread. 546 // Save the file in the file thread, and not here, the IO thread.
454 FilePath first_attempt_file_name = given_dictionary_directory_.Append( 547 FilePath first_attempt_file_name = given_dictionary_directory_.Append(
455 bdic_file_name_); 548 bdic_file_name_);
456 FilePath user_data_dir = GetFallbackDictionaryDownloadDirectory(); 549 FilePath user_data_dir = GetFallbackDictionaryDownloadDirectory();
457 FilePath fallback_file_name = user_data_dir.Append(bdic_file_name_); 550 FilePath fallback_file_name = user_data_dir.Append(bdic_file_name_);
458 Task* dic_task = on_dictionary_save_complete_callback_factory_. 551 Task* dic_task = method_factory_.
459 NewRunnableMethod(&SpellChecker::OnDictionarySaveComplete); 552 NewRunnableMethod(&SpellChecker::OnDictionarySaveComplete);
460 file_loop_->PostTask(FROM_HERE, new SaveDictionaryTask(dic_task, 553 file_loop_->PostTask(FROM_HERE, new SaveDictionaryTask(dic_task,
461 first_attempt_file_name, fallback_file_name, data, ui_loop_)); 554 first_attempt_file_name, fallback_file_name, data));
555 }
556
557 void SpellChecker::OnDictionarySaveComplete() {
558 obtaining_dictionary_ = false;
559 // Now that the dictionary is downloaded, continue trying to download.
560 Initialize();
462 } 561 }
463 562
464 // Initialize SpellChecker. In this method, if the dictionary is not present 563 // Initialize SpellChecker. In this method, if the dictionary is not present
465 // in the local disk, it is fetched asynchronously. 564 // in the local disk, it is fetched asynchronously.
466 // TODO(sidchat): After dictionary is downloaded, initialize hunspell in
467 // file loop - this is currently being done in the io loop.
468 // Bug: http://b/issue?id=1123096
469 bool SpellChecker::Initialize() { 565 bool SpellChecker::Initialize() {
566 if (!worker_loop_)
567 worker_loop_ = MessageLoop::current();
568 else
569 DCHECK(worker_loop_ == MessageLoop::current());
570
470 // Return false if the dictionary files are downloading. 571 // Return false if the dictionary files are downloading.
471 if (obtaining_dictionary_) 572 if (obtaining_dictionary_)
472 return false; 573 return false;
473 574
474 // Return false if tried to init and failed - don't try multiple times in 575 // Return false if tried to init and failed - don't try multiple times in
475 // this session. 576 // this session.
476 if (tried_to_init_) 577 if (tried_to_init_)
477 return hunspell_.get() != NULL; 578 return hunspell_.get() != NULL;
478 579
479 StatsScope<StatsCounterTimer> timer(chrome::Counters::spellcheck_init()); 580 StatsScope<StatsCounterTimer> timer(chrome::Counters::spellcheck_init());
(...skipping 10 matching lines...) Expand all
490 // File name for downloading in DIR_APP_DICTIONARIES. 591 // File name for downloading in DIR_APP_DICTIONARIES.
491 FilePath dictionary_file_name_app = GetVersionedFileName(language_, 592 FilePath dictionary_file_name_app = GetVersionedFileName(language_,
492 given_dictionary_directory_); 593 given_dictionary_directory_);
493 594
494 // Filename for downloading in the fallback dictionary download directory, 595 // Filename for downloading in the fallback dictionary download directory,
495 // DIR_USER_DATA. 596 // DIR_USER_DATA.
496 FilePath dict_dir_userdata = GetFallbackDictionaryDownloadDirectory(); 597 FilePath dict_dir_userdata = GetFallbackDictionaryDownloadDirectory();
497 FilePath dictionary_file_name_usr = GetVersionedFileName(language_, 598 FilePath dictionary_file_name_usr = GetVersionedFileName(language_,
498 dict_dir_userdata); 599 dict_dir_userdata);
499 600
500 // Check in both the directories to see whether the spellcheck dictionary 601 // Balances Release() in HunspellInited(), or in UIProxyForIOTask if the IO
501 // already resides in one of these. 602 // thread is torn down before the ReadDictionaryTask calls us back.
502 FilePath bdic_file_name; 603 AddRef();
503 if (file_util::PathExists(dictionary_file_name_app)) { 604 Task* task = new ReadDictionaryTask(this,
504 bdic_file_name = dictionary_file_name_app; 605 dictionary_file_name_app, dictionary_file_name_usr);
505 } else if (file_util::PathExists(dictionary_file_name_usr)) { 606 if (file_loop_) {
506 bdic_file_name = dictionary_file_name_usr; 607 file_loop_->PostTask(FROM_HERE, task);
507 } else { 608 } else {
508 // Download the dictionary file. 609 task->Run();
509 if (file_loop_ && url_request_context_) { 610 delete task;
510 if (!tried_to_download_dictionary_file_) {
511 StartDictionaryDownload(dictionary_file_name_app);
512 tried_to_download_dictionary_file_ = true;
513 return false;
514 } else { // There is no dictionary even after trying to download it.
515 // Stop trying to download the dictionary in this session.
516 tried_to_init_ = true;
517 return false;
518 }
519 }
520 } 611 }
521 612
522 // Control has come so far - the BDIC dictionary file probably exists. Now try 613 return hunspell_.get() != NULL;
523 // to initialize hunspell using the available bdic dictionary file. 614 }
524 TimeTicks begin_time = TimeTicks::Now(); 615
525 bdict_file_.reset(new file_util::MemoryMappedFile()); 616 void SpellChecker::HunspellInited(Hunspell* hunspell,
526 if (bdict_file_->Initialize(bdic_file_name)) { 617 file_util::MemoryMappedFile* bdict_file,
527 hunspell_.reset(new Hunspell(bdict_file_->data(), bdict_file_->length())); 618 bool file_existed) {
528 AddCustomWordsToHunspell(); 619 DCHECK(worker_loop_ == MessageLoop::current());
620
621 if (file_existed)
622 tried_to_init_ = true;
623
624 if (!hunspell) {
625 if (!file_existed) {
626 // File didn't exist. We need to download a dictionary.
627 DoDictionaryDownload();
628 }
629
630 // Balances AddRef() in Initialize().
631 Release();
632 return;
529 } 633 }
530 DHISTOGRAM_TIMES("Spellcheck.InitTime", TimeTicks::Now() - begin_time);
531 634
532 tried_to_init_ = true; 635
533 return false; 636 bdict_file_.reset(bdict_file);
637 hunspell_.reset(hunspell);
638 // Add all the custom words we've gotten while Hunspell was loading.
639 while (!custom_words_.empty()) {
640 hunspell_->add(custom_words_.front().c_str());
641 custom_words_.pop();
642 }
643
644 // Balances AddRef() in Initialize().
645 Release();
646 }
647
648 void SpellChecker::DoDictionaryDownload() {
649 // Download the dictionary file.
650 if (file_loop_ && url_request_context_) {
651 if (!tried_to_download_dictionary_file_) {
652 FilePath dictionary_file_name_app = GetVersionedFileName(language_,
653 given_dictionary_directory_);
654 StartDictionaryDownload(dictionary_file_name_app);
655 tried_to_download_dictionary_file_ = true;
656 } else {
657 // Don't try to download a dictionary more than once.
658 tried_to_init_ = true;
659 }
660 } else {
661 NOTREACHED();
662 }
534 } 663 }
535 664
536 void SpellChecker::GetAutoCorrectionWord(const std::wstring& word, int tag, 665 void SpellChecker::GetAutoCorrectionWord(const std::wstring& word, int tag,
537 std::wstring* autocorrect_word) { 666 std::wstring* autocorrect_word) {
538 autocorrect_word->clear(); 667 autocorrect_word->clear();
539 if (!auto_spell_correct_turned_on_) 668 if (!auto_spell_correct_turned_on_)
540 return; 669 return;
541 670
542 int word_length = static_cast<int>(word.size()); 671 int word_length = static_cast<int>(word.size());
543 if (word_length < 2 || word_length > kMaxAutoCorrectWordSize) 672 if (word_length < 2 || word_length > kMaxAutoCorrectWordSize)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 705
577 // Restore the swapped characters. 706 // Restore the swapped characters.
578 std::swap(misspelled_word[i], misspelled_word[i + 1]); 707 std::swap(misspelled_word[i], misspelled_word[i + 1]);
579 } 708 }
580 } 709 }
581 710
582 void SpellChecker::EnableAutoSpellCorrect(bool turn_on) { 711 void SpellChecker::EnableAutoSpellCorrect(bool turn_on) {
583 auto_spell_correct_turned_on_ = turn_on; 712 auto_spell_correct_turned_on_ = turn_on;
584 } 713 }
585 714
586 void SpellChecker::AddCustomWordsToHunspell() {
587 // Add custom words to Hunspell.
588 // This should be done in File Loop, but since Hunspell is in this IO Loop,
589 // this too has to be initialized here.
590 // TODO(sidchat): Work out a way to initialize Hunspell in the File Loop.
591 std::string contents;
592 file_util::ReadFileToString(custom_dictionary_file_name_, &contents);
593 std::vector<std::string> list_of_words;
594 SplitString(contents, '\n', &list_of_words);
595 if (hunspell_.get()) {
596 for (std::vector<std::string>::iterator it = list_of_words.begin();
597 it != list_of_words.end(); ++it) {
598 hunspell_->add(it->c_str());
599 }
600 }
601 }
602
603 // Returns whether or not the given string is a valid contraction. 715 // Returns whether or not the given string is a valid contraction.
604 // This function is a fall-back when the SpellcheckWordIterator class 716 // This function is a fall-back when the SpellcheckWordIterator class
605 // returns a concatenated word which is not in the selected dictionary 717 // returns a concatenated word which is not in the selected dictionary
606 // (e.g. "in'n'out") but each word is valid. 718 // (e.g. "in'n'out") but each word is valid.
607 bool SpellChecker::IsValidContraction(const string16& contraction, int tag) { 719 bool SpellChecker::IsValidContraction(const string16& contraction, int tag) {
608 SpellcheckWordIterator word_iterator; 720 SpellcheckWordIterator word_iterator;
609 word_iterator.Initialize(&character_attributes_, contraction.c_str(), 721 word_iterator.Initialize(&character_attributes_, contraction.c_str(),
610 contraction.length(), false); 722 contraction.length(), false);
611 723
612 string16 word; 724 string16 word;
613 int word_start; 725 int word_start;
614 int word_length; 726 int word_length;
615 while (word_iterator.GetNextWord(&word, &word_start, &word_length)) { 727 while (word_iterator.GetNextWord(&word, &word_start, &word_length)) {
616 if (!CheckSpelling(UTF16ToUTF8(word), tag)) 728 if (!CheckSpelling(UTF16ToUTF8(word), tag))
617 return false; 729 return false;
618 } 730 }
619 return true; 731 return true;
620 } 732 }
621 733
622 bool SpellChecker::SpellCheckWord( 734 bool SpellChecker::SpellCheckWord(
623 const wchar_t* in_word, 735 const wchar_t* in_word,
624 int in_word_len, 736 int in_word_len,
625 int tag, 737 int tag,
626 int* misspelling_start, 738 int* misspelling_start,
627 int* misspelling_len, 739 int* misspelling_len,
628 std::vector<std::wstring>* optional_suggestions) { 740 std::vector<std::wstring>* optional_suggestions) {
629 DCHECK(in_word_len >= 0); 741 DCHECK(in_word_len >= 0);
630 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given."; 742 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
631 743
632 #ifndef NDEBUG
633 // This must always be called on the same thread (normally the I/O thread). 744 // This must always be called on the same thread (normally the I/O thread).
634 if (worker_loop_) 745 if (worker_loop_)
635 DCHECK(MessageLoop::current() == worker_loop_); 746 DCHECK(MessageLoop::current() == worker_loop_);
636 else
637 worker_loop_ = MessageLoop::current();
638 #endif
639 747
640 // Check if the platform spellchecker is being used. 748 // Check if the platform spellchecker is being used.
641 if (!is_using_platform_spelling_engine_) { 749 if (!is_using_platform_spelling_engine_) {
642 // If it isn't, try and init hunspell. 750 // If it isn't, try and init hunspell.
643 Initialize(); 751 Initialize();
644 752
645 // Check to see if hunspell was successful. 753 // Check to see if hunspell was successfuly initialized.
646 if (!hunspell_.get()) 754 if (!hunspell_.get())
647 return true; // Unable to spellcheck, return word is OK. 755 return true; // Unable to spellcheck, return word is OK.
648 } 756 }
649 757
650 StatsScope<StatsRate> timer(chrome::Counters::spellcheck_lookup()); 758 StatsScope<StatsRate> timer(chrome::Counters::spellcheck_lookup());
651 759
652 *misspelling_start = 0; 760 *misspelling_start = 0;
653 *misspelling_len = 0; 761 *misspelling_len = 0;
654 if (in_word_len == 0) 762 if (in_word_len == 0)
655 return true; // No input means always spelled correctly. 763 return true; // No input means always spelled correctly.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 if (is_using_platform_spelling_engine_) { 829 if (is_using_platform_spelling_engine_) {
722 SpellCheckerPlatform::AddWord(word); 830 SpellCheckerPlatform::AddWord(word);
723 return; 831 return;
724 } 832 }
725 833
726 // Check if the |hunspell_| has been initialized at all. 834 // Check if the |hunspell_| has been initialized at all.
727 Initialize(); 835 Initialize();
728 836
729 // Add the word to hunspell. 837 // Add the word to hunspell.
730 std::string word_to_add = WideToUTF8(word); 838 std::string word_to_add = WideToUTF8(word);
731 if (!word_to_add.empty()) 839 if (!word_to_add.empty()) {
732 hunspell_->add(word_to_add.c_str()); 840 // Either add the word to |hunspell_|, or, if |hunspell_| is still loading,
841 // defer it till after the load completes.
842 if (hunspell_.get())
843 hunspell_->add(word_to_add.c_str());
844 else
845 custom_words_.push(word_to_add);
846 }
733 847
734 // Now add the word to the custom dictionary file. 848 // Now add the word to the custom dictionary file.
735 Task* write_word_task = 849 Task* write_word_task =
736 new AddWordToCustomDictionaryTask(custom_dictionary_file_name_, word); 850 new AddWordToCustomDictionaryTask(custom_dictionary_file_name_, word);
737 if (file_loop_) 851 if (file_loop_) {
738 file_loop_->PostTask(FROM_HERE, write_word_task); 852 file_loop_->PostTask(FROM_HERE, write_word_task);
739 else 853 } else {
740 write_word_task->Run(); 854 write_word_task->Run();
855 delete write_word_task;
856 }
741 } 857 }
742 858
743 bool SpellChecker::CheckSpelling(const std::string& word_to_check, int tag) { 859 bool SpellChecker::CheckSpelling(const std::string& word_to_check, int tag) {
744 bool word_correct = false; 860 bool word_correct = false;
745 861
746 TimeTicks begin_time = TimeTicks::Now(); 862 TimeTicks begin_time = TimeTicks::Now();
747 if (is_using_platform_spelling_engine_) { 863 if (is_using_platform_spelling_engine_) {
748 word_correct = SpellCheckerPlatform::CheckSpelling(word_to_check, tag); 864 word_correct = SpellCheckerPlatform::CheckSpelling(word_to_check, tag);
749 } else { 865 } else {
750 // |hunspell_->spell| returns 0 if the word is spelled correctly and 866 // |hunspell_->spell| returns 0 if the word is spelled correctly and
751 // non-zero otherwsie. 867 // non-zero otherwsie.
752 word_correct = (hunspell_->spell(word_to_check.c_str()) != 0); 868 word_correct = (hunspell_->spell(word_to_check.c_str()) != 0);
753 } 869 }
754 DHISTOGRAM_TIMES("Spellcheck.CheckTime", TimeTicks::Now() - begin_time); 870 DHISTOGRAM_TIMES("Spellcheck.CheckTime", TimeTicks::Now() - begin_time);
755 871
756 return word_correct; 872 return word_correct;
757 } 873 }
758 874
759
760 void SpellChecker::FillSuggestionList(const std::string& wrong_word, 875 void SpellChecker::FillSuggestionList(const std::string& wrong_word,
761 std::vector<std::wstring>* optional_suggestions) { 876 std::vector<std::wstring>* optional_suggestions) {
762 if (is_using_platform_spelling_engine_) { 877 if (is_using_platform_spelling_engine_) {
763 SpellCheckerPlatform::FillSuggestionList(wrong_word, optional_suggestions); 878 SpellCheckerPlatform::FillSuggestionList(wrong_word, optional_suggestions);
764 return; 879 return;
765 } 880 }
766 char** suggestions; 881 char** suggestions;
767 TimeTicks begin_time = TimeTicks::Now(); 882 TimeTicks begin_time = TimeTicks::Now();
768 int number_of_suggestions = hunspell_->suggest(&suggestions, 883 int number_of_suggestions = hunspell_->suggest(&suggestions,
769 wrong_word.c_str()); 884 wrong_word.c_str());
770 DHISTOGRAM_TIMES("Spellcheck.SuggestTime", 885 DHISTOGRAM_TIMES("Spellcheck.SuggestTime",
771 TimeTicks::Now() - begin_time); 886 TimeTicks::Now() - begin_time);
772 887
773 // Populate the vector of WideStrings. 888 // Populate the vector of WideStrings.
774 for (int i = 0; i < number_of_suggestions; i++) { 889 for (int i = 0; i < number_of_suggestions; i++) {
775 if (i < kMaxSuggestions) 890 if (i < kMaxSuggestions)
776 optional_suggestions->push_back(UTF8ToWide(suggestions[i])); 891 optional_suggestions->push_back(UTF8ToWide(suggestions[i]));
777 free(suggestions[i]); 892 free(suggestions[i]);
778 } 893 }
779 if (suggestions != NULL) 894 if (suggestions != NULL)
780 free(suggestions); 895 free(suggestions);
781 } 896 }
OLDNEW
« no previous file with comments | « chrome/browser/spellchecker.h ('k') | tools/valgrind/memcheck/suppressions.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698