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

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

Issue 11414282: Improve reliability of custom spelling dictionary file. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years 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
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698