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/browser/spellchecker/spellcheck_custom_dictionary.h" | 5 #include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h" |
| 6 | 6 |
| 7 #include <functional> | 7 #include <functional> |
| 8 | 8 |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/files/important_file_writer.h" | |
| 11 #include "base/md5.h" | |
| 10 #include "base/string_split.h" | 12 #include "base/string_split.h" |
| 11 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
| 12 #include "chrome/common/chrome_constants.h" | 14 #include "chrome/common/chrome_constants.h" |
| 13 #include "chrome/common/spellcheck_messages.h" | 15 #include "chrome/common/spellcheck_messages.h" |
| 14 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 15 #include "content/public/browser/render_process_host.h" | 17 #include "content/public/browser/render_process_host.h" |
| 16 | 18 |
| 17 using content::BrowserThread; | 19 using content::BrowserThread; |
| 18 using chrome::spellcheck_common::WordList; | 20 using chrome::spellcheck_common::WordList; |
| 19 | 21 |
| 20 SpellcheckCustomDictionary::SpellcheckCustomDictionary(Profile* profile) | 22 SpellcheckCustomDictionary::SpellcheckCustomDictionary(Profile* profile) |
| 21 : SpellcheckDictionary(profile), | 23 : SpellcheckDictionary(profile), |
| 22 custom_dictionary_path_(), | 24 custom_dictionary_path_(profile->GetPath().Append( |
| 25 chrome::kCustomDictionaryFileName)), | |
| 26 custom_dictionary_checksum_path_(profile->GetPath().Append( | |
| 27 chrome::kCustomDictionaryFileName).AddExtension("checksum")), | |
| 28 custom_dictionary_backup_path_(profile->GetPath().Append( | |
| 29 chrome::kCustomDictionaryFileName).AddExtension("backup")), | |
| 30 custom_dictionary_checksum_backup_path_(profile->GetPath().Append( | |
| 31 chrome::kCustomDictionaryFileName).AddExtension("checksum"). | |
| 32 AddExtension("backup")), | |
| 23 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 33 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 24 DCHECK(profile); | |
|
groby-ooo-7-16
2012/12/03 17:12:44
Why'd you kill the DCHECK?
please use gerrit instead
2012/12/03 23:16:51
My mistake. It's back.
| |
| 25 custom_dictionary_path_ = | |
| 26 profile_->GetPath().Append(chrome::kCustomDictionaryFileName); | |
| 27 } | 34 } |
| 28 | 35 |
| 29 SpellcheckCustomDictionary::~SpellcheckCustomDictionary() { | 36 SpellcheckCustomDictionary::~SpellcheckCustomDictionary() { |
| 30 } | 37 } |
| 31 | 38 |
| 32 void SpellcheckCustomDictionary::Load() { | 39 void SpellcheckCustomDictionary::Load() { |
| 33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 34 | 41 |
| 35 BrowserThread::PostTaskAndReplyWithResult<WordList*>( | 42 BrowserThread::PostTaskAndReplyWithResult<WordList*>( |
| 36 BrowserThread::FILE, | 43 BrowserThread::FILE, |
| 37 FROM_HERE, | 44 FROM_HERE, |
| 38 base::Bind(&SpellcheckCustomDictionary::LoadDictionary, | 45 base::Bind(&SpellcheckCustomDictionary::LoadDictionary, |
| 39 base::Unretained(this)), | 46 base::Unretained(this)), |
| 40 base::Bind(&SpellcheckCustomDictionary::SetCustomWordListAndDelete, | 47 base::Bind(&SpellcheckCustomDictionary::SetCustomWordListAndDelete, |
| 41 weak_ptr_factory_.GetWeakPtr())); | 48 weak_ptr_factory_.GetWeakPtr())); |
| 42 } | 49 } |
| 43 | 50 |
| 44 const WordList& SpellcheckCustomDictionary::GetWords() const { | 51 const WordList& SpellcheckCustomDictionary::GetWords() const { |
| 45 return words_; | 52 return words_; |
| 46 } | 53 } |
| 47 | 54 |
| 48 void SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList( | 55 void SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList( |
| 49 WordList* custom_words) { | 56 WordList* custom_words) { |
| 50 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 51 | 58 |
| 52 std::string contents; | 59 std::string contents; |
| 53 file_util::ReadFileToString(custom_dictionary_path_, &contents); | 60 LoadDictionaryContentsReliably(&contents); |
| 61 | |
| 54 if (contents.empty()) { | 62 if (contents.empty()) { |
| 55 custom_words->clear(); | 63 custom_words->clear(); |
| 56 return; | 64 return; |
| 57 } | 65 } |
| 58 | 66 |
| 59 base::SplitString(contents, '\n', custom_words); | 67 base::SplitString(contents, '\n', custom_words); |
| 60 | 68 |
| 61 // Erase duplicates. | 69 // Clean up the dictionary file contents. |
|
groby-ooo-7-16
2012/12/03 17:12:44
... by removing duplicates and empty words. It's n
please use gerrit instead
2012/12/03 23:16:51
Done.
| |
| 62 std::sort(custom_words->begin(), custom_words->end()); | 70 std::sort(custom_words->begin(), custom_words->end()); |
| 63 custom_words->erase(std::unique(custom_words->begin(), custom_words->end()), | 71 custom_words->erase(std::unique(custom_words->begin(), custom_words->end()), |
| 64 custom_words->end()); | 72 custom_words->end()); |
| 65 | 73 custom_words->erase(std::remove_if(custom_words->begin(), |
| 66 // Clear out empty words. | 74 custom_words->end(), |
| 67 custom_words->erase(remove_if(custom_words->begin(), custom_words->end(), | 75 std::mem_fun_ref(&std::string::empty)), |
| 68 mem_fun_ref(&std::string::empty)), custom_words->end()); | 76 custom_words->end()); |
| 69 | |
| 70 // Write out the clean file. | |
| 71 std::stringstream ss; | 77 std::stringstream ss; |
| 72 for (WordList::iterator it = custom_words->begin(); | 78 for (WordList::iterator it = custom_words->begin(); |
| 73 it != custom_words->end(); | 79 it != custom_words->end(); |
| 74 ++it) { | 80 ++it) { |
| 75 ss << *it << '\n'; | 81 ss << *it << '\n'; |
| 76 } | 82 } |
| 77 contents = ss.str(); | 83 |
| 78 file_util::WriteFile(custom_dictionary_path_, | 84 SaveDictionryContentsReliably(ss.str()); |
| 79 contents.c_str(), | |
| 80 contents.length()); | |
| 81 } | 85 } |
| 82 | 86 |
| 83 void SpellcheckCustomDictionary::SetCustomWordList(WordList* custom_words) { | 87 void SpellcheckCustomDictionary::SetCustomWordList(WordList* custom_words) { |
| 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 85 | 89 |
| 86 words_.clear(); | 90 words_.clear(); |
| 87 if (custom_words) | 91 if (custom_words) |
| 88 std::swap(words_, *custom_words); | 92 std::swap(words_, *custom_words); |
| 89 | 93 |
| 90 std::vector<Observer*>::iterator it; | 94 std::vector<Observer*>::iterator it; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 words_.push_back(word); | 128 words_.push_back(word); |
| 125 return true; | 129 return true; |
| 126 } | 130 } |
| 127 return false; | 131 return false; |
| 128 // TODO(rlp): record metrics on custom word size | 132 // TODO(rlp): record metrics on custom word size |
| 129 } | 133 } |
| 130 | 134 |
| 131 void SpellcheckCustomDictionary::WriteWordToCustomDictionary( | 135 void SpellcheckCustomDictionary::WriteWordToCustomDictionary( |
| 132 const std::string& word) { | 136 const std::string& word) { |
| 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 134 | |
| 135 // Stored in UTF-8. | |
| 136 DCHECK(IsStringUTF8(word)); | 138 DCHECK(IsStringUTF8(word)); |
| 137 | 139 |
| 138 std::string word_to_add(word + "\n"); | 140 std::string contents; |
| 139 if (!file_util::PathExists(custom_dictionary_path_)) { | 141 LoadDictionaryContentsReliably(&contents); |
| 140 file_util::WriteFile(custom_dictionary_path_, word_to_add.c_str(), | 142 |
| 141 word_to_add.length()); | 143 std::stringstream ss; |
| 142 } else { | 144 ss << contents; |
| 143 file_util::AppendToFile(custom_dictionary_path_, word_to_add.c_str(), | 145 if (contents.length() > 0 && contents[contents.length() - 1] != '\n') |
| 144 word_to_add.length()); | 146 ss << '\n'; |
| 145 } | 147 ss << word << '\n'; |
| 148 | |
| 149 SaveDictionryContentsReliably(ss.str()); | |
| 146 } | 150 } |
| 147 | 151 |
| 148 bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) { | 152 bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) { |
| 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 150 | 154 |
| 151 if (!CustomWordRemovedLocally(word)) | 155 if (!CustomWordRemovedLocally(word)) |
| 152 return false; | 156 return false; |
| 153 | 157 |
| 154 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 158 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 155 base::Bind(&SpellcheckCustomDictionary::EraseWordFromCustomDictionary, | 159 base::Bind(&SpellcheckCustomDictionary::EraseWordFromCustomDictionary, |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 178 return true; | 182 return true; |
| 179 } | 183 } |
| 180 return false; | 184 return false; |
| 181 } | 185 } |
| 182 | 186 |
| 183 void SpellcheckCustomDictionary::EraseWordFromCustomDictionary( | 187 void SpellcheckCustomDictionary::EraseWordFromCustomDictionary( |
| 184 const std::string& word) { | 188 const std::string& word) { |
| 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 186 DCHECK(IsStringUTF8(word)); | 190 DCHECK(IsStringUTF8(word)); |
| 187 | 191 |
| 188 WordList custom_words; | 192 std::string contents; |
| 189 LoadDictionaryIntoCustomWordList(&custom_words); | 193 LoadDictionaryContentsReliably(&contents); |
| 190 | 194 |
| 191 const char empty[] = {'\0'}; | 195 size_t pos = 0; |
|
groby-ooo-7-16
2012/12/03 17:12:44
Can we just write out the whole dictionary here? F
please use gerrit instead
2012/12/03 23:16:51
Done.
| |
| 192 const char separator[] = {'\n', '\0'}; | 196 size_t found = contents.find(word, pos); |
| 193 file_util::WriteFile(custom_dictionary_path_, empty, 0); | 197 std::stringstream ss; |
| 194 for (WordList::iterator it = custom_words.begin(); | 198 while (found != std::string::npos) { |
| 195 it != custom_words.end(); | 199 if (found > pos) |
| 196 ++it) { | 200 ss.write(&contents[pos], found - pos ); |
| 197 std::string word_to_add = *it; | 201 if ((found > 0 && contents[found - 1] != '\n') || |
| 198 if (word.compare(word_to_add) != 0) { | 202 (found + word.length() < contents.length() && |
| 199 file_util::AppendToFile(custom_dictionary_path_, word_to_add.c_str(), | 203 contents[found + word.length()] != '\n')) { |
| 200 word_to_add.length()); | 204 ss.write(&contents[found], word.length()); |
| 201 file_util::AppendToFile(custom_dictionary_path_, separator, 1); | 205 pos = found + word.length(); |
| 206 } else { | |
| 207 // skip the word and the newline character that follows it. | |
| 208 pos = found + word.length() + 1; | |
| 202 } | 209 } |
| 210 found = contents.find(word, pos); | |
| 203 } | 211 } |
| 212 if (contents.length() > pos) | |
| 213 ss.write(&contents[pos], contents.length() - pos); | |
| 214 | |
| 215 SaveDictionryContentsReliably(ss.str()); | |
| 204 } | 216 } |
| 205 | 217 |
| 206 void SpellcheckCustomDictionary::AddObserver(Observer* observer) { | 218 void SpellcheckCustomDictionary::AddObserver(Observer* observer) { |
| 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 208 | 220 |
| 209 observers_.push_back(observer); | 221 observers_.push_back(observer); |
| 210 } | 222 } |
| 211 | 223 |
| 212 void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) { | 224 void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) { |
| 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 227 return custom_words; | 239 return custom_words; |
| 228 } | 240 } |
| 229 | 241 |
| 230 void SpellcheckCustomDictionary::SetCustomWordListAndDelete( | 242 void SpellcheckCustomDictionary::SetCustomWordListAndDelete( |
| 231 WordList* custom_words) { | 243 WordList* custom_words) { |
| 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 233 | 245 |
| 234 SetCustomWordList(custom_words); | 246 SetCustomWordList(custom_words); |
| 235 delete custom_words; | 247 delete custom_words; |
| 236 } | 248 } |
| 249 | |
| 250 void SpellcheckCustomDictionary::LoadDictionaryContentsReliably( | |
| 251 std::string* contents) { | |
| 252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 253 | |
| 254 file_util::ReadFileToString(custom_dictionary_path_, contents); | |
| 255 | |
| 256 if (!file_util::PathExists(custom_dictionary_backup_path_) || | |
| 257 !file_util::PathExists(custom_dictionary_checksum_backup_path_)) { | |
| 258 return; | |
| 259 } | |
| 260 | |
| 261 if (file_util::PathExists(custom_dictionary_checksum_path_)) { | |
| 262 std::string checksum; | |
| 263 file_util::ReadFileToString(custom_dictionary_checksum_path_, &checksum); | |
| 264 base::MD5Digest digest; | |
| 265 base::MD5Sum(contents->c_str(), contents->length(), &digest); | |
| 266 if (checksum.compare(base::MD5DigestToBase16(digest)) == 0) | |
| 267 return; | |
| 268 } | |
| 269 | |
| 270 std::string backup; | |
| 271 file_util::ReadFileToString(custom_dictionary_backup_path_, &backup); | |
| 272 std::string checksum_backup; | |
| 273 file_util::ReadFileToString(custom_dictionary_checksum_backup_path_, | |
| 274 &checksum_backup); | |
| 275 base::MD5Digest backup_digest; | |
| 276 base::MD5Sum(backup.c_str(), backup.length(), &backup_digest); | |
| 277 if (checksum_backup.compare(base::MD5DigestToBase16(backup_digest)) != 0) | |
| 278 return; | |
| 279 | |
| 280 *contents = backup; | |
| 281 file_util::CopyFile(custom_dictionary_checksum_backup_path_, | |
| 282 custom_dictionary_checksum_path_); | |
| 283 file_util::CopyFile(custom_dictionary_backup_path_, | |
| 284 custom_dictionary_path_); | |
| 285 } | |
| 286 | |
| 287 void SpellcheckCustomDictionary::SaveDictionryContentsReliably( | |
| 288 const std::string& contents) { | |
| 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 290 | |
| 291 if (file_util::PathExists(custom_dictionary_checksum_path_)) { | |
| 292 file_util::CopyFile(custom_dictionary_checksum_path_, | |
| 293 custom_dictionary_checksum_backup_path_); | |
| 294 } | |
| 295 if (file_util::PathExists(custom_dictionary_path_)) { | |
| 296 file_util::CopyFile(custom_dictionary_path_, | |
| 297 custom_dictionary_backup_path_); | |
| 298 } | |
| 299 base::MD5Digest digest; | |
| 300 base::MD5Sum(contents.c_str(), contents.length(), &digest); | |
| 301 base::ImportantFileWriter::WriteFileAtomically( | |
| 302 custom_dictionary_checksum_path_, base::MD5DigestToBase16(digest)); | |
| 303 base::ImportantFileWriter::WriteFileAtomically( | |
| 304 custom_dictionary_path_, contents); | |
| 305 } | |
| OLD | NEW |