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

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

Issue 11445002: Sync user's custom spellcheck dictionary (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fix compile on some platforms Created 7 years, 11 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
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" 10 #include "base/files/important_file_writer.h"
11 #include "base/md5.h" 11 #include "base/md5.h"
12 #include "base/string_number_conversions.h"
12 #include "base/string_split.h" 13 #include "base/string_split.h"
13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_constants.h"
15 #include "chrome/common/spellcheck_messages.h" 16 #include "chrome/common/spellcheck_messages.h"
16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_process_host.h" 18 #include "sync/api/sync_change.h"
19 #include "sync/api/sync_data.h"
20 #include "sync/api/sync_error_factory.h"
21 #include "sync/protocol/sync.pb.h"
18 22
19 using content::BrowserThread; 23 using content::BrowserThread;
20 using chrome::spellcheck_common::WordList; 24 using chrome::spellcheck_common::WordList;
21 25
22 namespace { 26 namespace {
23 27
28 // Filename extension for backup dictionary file.
24 const FilePath::CharType BACKUP_EXTENSION[] = FILE_PATH_LITERAL("backup"); 29 const FilePath::CharType BACKUP_EXTENSION[] = FILE_PATH_LITERAL("backup");
30
31 // Prefix for the checksum in the dictionary file.
25 const char CHECKSUM_PREFIX[] = "checksum_v1 = "; 32 const char CHECKSUM_PREFIX[] = "checksum_v1 = ";
26 33
27 // Loads the lines from the file at |file_path| into the |lines| container. If 34 // The status of the checksum in a custom spellcheck dictionary.
28 // the file has a valid checksum, then returns |true|. If the file has an 35 enum ChecksumStatus {
29 // invalid checksum, then returns |false| and clears |lines|. 36 VALID_CHECKSUM,
30 bool LoadFile(FilePath file_path, std::vector<std::string>* lines) { 37 INVALID_CHECKSUM,
31 lines->clear(); 38 };
39
40 // The result of a dictionary sanitation. Can be used as a bitmap.
41 enum ChangeSanitationResult {
42 // The change is valid and can be applied as-is.
43 VALID_CHANGE = 0,
44
45 // The change contained words to be added that are not valid.
46 DETECTED_INVALID_WORDS = 1,
47
48 // The change contained words to be added that are already in the dictionary.
49 DETECTED_DUPLICATE_WORDS = 2,
50
51 // The change contained words to be removed that are not in the dictionary.
52 DETECTED_MISSING_WORDS = 4,
53 };
54
55 // Loads the file at |file_path| into the |words| container. If the file has a
56 // valid checksum, then returns ChecksumStatus::VALID. If the file has an
57 // invalid checksum, then returns ChecksumStatus::INVALID and clears |words|.
58 ChecksumStatus LoadFile(const FilePath& file_path, WordList& words) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
60 words.clear();
32 std::string contents; 61 std::string contents;
33 file_util::ReadFileToString(file_path, &contents); 62 file_util::ReadFileToString(file_path, &contents);
34 size_t pos = contents.rfind(CHECKSUM_PREFIX); 63 size_t pos = contents.rfind(CHECKSUM_PREFIX);
35 if (pos != std::string::npos) { 64 if (pos != std::string::npos) {
36 std::string checksum = contents.substr(pos + strlen(CHECKSUM_PREFIX)); 65 std::string checksum = contents.substr(pos + strlen(CHECKSUM_PREFIX));
37 contents = contents.substr(0, pos); 66 contents = contents.substr(0, pos);
38 if (checksum != base::MD5String(contents)) 67 if (checksum != base::MD5String(contents))
39 return false; 68 return INVALID_CHECKSUM;
40 } 69 }
41 TrimWhitespaceASCII(contents, TRIM_ALL, &contents); 70 TrimWhitespaceASCII(contents, TRIM_ALL, &contents);
42 base::SplitString(contents, '\n', lines); 71 base::SplitString(contents, '\n', &words);
43 return true; 72 return VALID_CHECKSUM;
44 } 73 }
45 74
46 bool IsValidWord(const std::string& word) { 75 // Returns true for invalid words and false for valid words.
47 return IsStringUTF8(word) && word.length() <= 128 && word.length() > 0 &&
48 std::string::npos == word.find_first_of(kWhitespaceASCII);
49 }
50
51 bool IsInvalidWord(const std::string& word) { 76 bool IsInvalidWord(const std::string& word) {
52 return !IsValidWord(word); 77 return !IsStringUTF8(word) ||
53 } 78 word.length() >
54 79 chrome::spellcheck_common::MAX_CUSTOM_DICTIONARY_WORD_BYTES ||
55 } // namespace 80 word.empty() ||
56 81 word.find_first_of(kWhitespaceASCII) != std::string::npos;
57 SpellcheckCustomDictionary::SpellcheckCustomDictionary(Profile* profile) 82 }
58 : SpellcheckDictionary(profile), 83
59 custom_dictionary_path_(), 84 // Loads the custom spellcheck dictionary from |path| into |custom_words|. If
60 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 85 // the dictionary checksum is not valid, but backup checksum is valid, then
61 DCHECK(profile); 86 // restores the backup and loads that into |custom_words| instead. If the backup
62 custom_dictionary_path_ = 87 // is invalid too, then clears |custom_words|. Must be called on the file
63 profile_->GetPath().Append(chrome::kCustomDictionaryFileName); 88 // thread.
64 } 89 void LoadDictionaryFileReliably(WordList& custom_words, const FilePath& path) {
65
66 SpellcheckCustomDictionary::~SpellcheckCustomDictionary() {
67 }
68
69 void SpellcheckCustomDictionary::Load() {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
71
72 BrowserThread::PostTaskAndReplyWithResult<WordList*>(
73 BrowserThread::FILE,
74 FROM_HERE,
75 base::Bind(&SpellcheckCustomDictionary::LoadDictionary,
76 base::Unretained(this)),
77 base::Bind(&SpellcheckCustomDictionary::SetCustomWordListAndDelete,
78 weak_ptr_factory_.GetWeakPtr()));
79 }
80
81 const WordList& SpellcheckCustomDictionary::GetWords() const {
82 return words_;
83 }
84
85 void SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList(
86 WordList* custom_words) {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
88 91 // Load the contents and verify the checksum.
89 LoadDictionaryFileReliably(custom_words); 92 if (LoadFile(path, custom_words) == VALID_CHECKSUM)
90 if (custom_words->empty())
91 return; 93 return;
92
93 // Clean up the dictionary file contents by removing duplicates and invalid
94 // words.
95 std::sort(custom_words->begin(), custom_words->end());
96 custom_words->erase(std::unique(custom_words->begin(), custom_words->end()),
97 custom_words->end());
98 custom_words->erase(std::remove_if(custom_words->begin(),
99 custom_words->end(),
100 IsInvalidWord),
101 custom_words->end());
102
103 SaveDictionaryFileReliably(*custom_words);
104 }
105
106 void SpellcheckCustomDictionary::SetCustomWordList(WordList* custom_words) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108
109 words_.clear();
110 if (custom_words)
111 std::swap(words_, *custom_words);
112
113 FOR_EACH_OBSERVER(Observer, observers_, OnCustomDictionaryLoaded());
114 }
115
116 bool SpellcheckCustomDictionary::AddWord(const std::string& word) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118 if (!IsValidWord(word))
119 return false;
120
121 if (!CustomWordAddedLocally(word))
122 return false;
123
124 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
125 base::Bind(&SpellcheckCustomDictionary::WriteWordToCustomDictionary,
126 base::Unretained(this), word));
127
128 for (content::RenderProcessHost::iterator i(
129 content::RenderProcessHost::AllHostsIterator());
130 !i.IsAtEnd(); i.Advance()) {
131 i.GetCurrentValue()->Send(new SpellCheckMsg_WordAdded(word));
132 }
133
134 FOR_EACH_OBSERVER(Observer, observers_, OnCustomDictionaryWordAdded(word));
135
136 return true;
137 }
138
139 bool SpellcheckCustomDictionary::CustomWordAddedLocally(
140 const std::string& word) {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 DCHECK(IsValidWord(word));
143
144 WordList::iterator it = std::find(words_.begin(), words_.end(), word);
145 if (it == words_.end()) {
146 words_.push_back(word);
147 return true;
148 }
149 return false;
150 // TODO(rlp): record metrics on custom word size
151 }
152
153 void SpellcheckCustomDictionary::WriteWordToCustomDictionary(
154 const std::string& word) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
156 DCHECK(IsValidWord(word));
157
158 WordList custom_words;
159 LoadDictionaryFileReliably(&custom_words);
160 custom_words.push_back(word);
161 SaveDictionaryFileReliably(custom_words);
162 }
163
164 bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
166 if (!IsValidWord(word))
167 return false;
168
169 if (!CustomWordRemovedLocally(word))
170 return false;
171
172 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
173 base::Bind(&SpellcheckCustomDictionary::EraseWordFromCustomDictionary,
174 base::Unretained(this), word));
175
176 for (content::RenderProcessHost::iterator i(
177 content::RenderProcessHost::AllHostsIterator());
178 !i.IsAtEnd(); i.Advance()) {
179 i.GetCurrentValue()->Send(new SpellCheckMsg_WordRemoved(word));
180 }
181
182 FOR_EACH_OBSERVER(Observer, observers_, OnCustomDictionaryWordRemoved(word));
183
184 return true;
185 }
186
187 bool SpellcheckCustomDictionary::CustomWordRemovedLocally(
188 const std::string& word) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
190 DCHECK(IsValidWord(word));
191
192 WordList::iterator it = std::find(words_.begin(), words_.end(), word);
193 if (it != words_.end()) {
194 words_.erase(it);
195 return true;
196 }
197 return false;
198 }
199
200 void SpellcheckCustomDictionary::EraseWordFromCustomDictionary(
201 const std::string& word) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
203 DCHECK(IsValidWord(word));
204
205 WordList custom_words;
206 LoadDictionaryFileReliably(&custom_words);
207 if (custom_words.empty())
208 return;
209
210 WordList::iterator it = std::find(custom_words.begin(),
211 custom_words.end(),
212 word);
213 if (it != custom_words.end())
214 custom_words.erase(it);
215
216 SaveDictionaryFileReliably(custom_words);
217 }
218
219 void SpellcheckCustomDictionary::AddObserver(Observer* observer) {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221
222 observers_.AddObserver(observer);
223 }
224
225 void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227
228 observers_.RemoveObserver(observer);
229 }
230
231 WordList* SpellcheckCustomDictionary::LoadDictionary() {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
233
234 WordList* custom_words = new WordList;
235 LoadDictionaryIntoCustomWordList(custom_words);
236 return custom_words;
237 }
238
239 void SpellcheckCustomDictionary::SetCustomWordListAndDelete(
240 WordList* custom_words) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242
243 SetCustomWordList(custom_words);
244 delete custom_words;
245 }
246
247 void SpellcheckCustomDictionary::LoadDictionaryFileReliably(
248 WordList* custom_words) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
250
251 // Load the contents and verify the checksum.
252 if (LoadFile(custom_dictionary_path_, custom_words))
253 return;
254
255 // Checksum is not valid. See if there's a backup. 94 // Checksum is not valid. See if there's a backup.
256 FilePath backup = custom_dictionary_path_.AddExtension(BACKUP_EXTENSION); 95 FilePath backup = path.AddExtension(BACKUP_EXTENSION);
257 if (!file_util::PathExists(backup)) 96 if (!file_util::PathExists(backup))
258 return; 97 return;
259
260 // Load the backup and verify its checksum. 98 // Load the backup and verify its checksum.
261 if (!LoadFile(backup, custom_words)) 99 if (LoadFile(backup, custom_words) != VALID_CHECKSUM)
262 return; 100 return;
263
264 // Backup checksum is valid. Restore the backup. 101 // Backup checksum is valid. Restore the backup.
265 file_util::CopyFile(backup, custom_dictionary_path_); 102 file_util::CopyFile(backup, path);
266 } 103 }
267 104
268 void SpellcheckCustomDictionary::SaveDictionaryFileReliably( 105 // Backs up the original dictionary, saves |custom_words| and its checksum into
269 const WordList& custom_words) { 106 // the custom spellcheck dictionary at |path|.
107 void SaveDictionaryFileReliably(
108 const WordList& custom_words,
109 const FilePath& path) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
271
272 std::stringstream content; 111 std::stringstream content;
273 for (WordList::const_iterator it = custom_words.begin(); 112 for (WordList::const_iterator it = custom_words.begin();
274 it != custom_words.end(); 113 it != custom_words.end();
275 ++it) { 114 ++it) {
276 content << *it << '\n'; 115 content << *it << '\n';
277 } 116 }
278 std::string checksum = base::MD5String(content.str()); 117 std::string checksum = base::MD5String(content.str());
279 content << CHECKSUM_PREFIX << checksum; 118 content << CHECKSUM_PREFIX << checksum;
280 119 file_util::CopyFile(path, path.AddExtension(BACKUP_EXTENSION));
281 file_util::CopyFile(custom_dictionary_path_, 120 base::ImportantFileWriter::WriteFileAtomically(path, content.str());
282 custom_dictionary_path_.AddExtension(BACKUP_EXTENSION)); 121 }
283 base::ImportantFileWriter::WriteFileAtomically(custom_dictionary_path_, 122
284 content.str()); 123 // Removes duplicate and invalid words from |to_add| word list and sorts it.
285 } 124 // Looks for duplicates in both |to_add| and |existing| word lists. Returns a
125 // bitmap of |ChangeSanitationResult| values.
126 int SanitizeWordsToAdd(const WordList& existing, WordList& to_add) {
127 // Do not add duplicate words.
128 std::sort(to_add.begin(), to_add.end());
129 WordList new_words;
130 std::set_difference(to_add.begin(),
131 to_add.end(),
132 existing.begin(),
133 existing.end(),
134 std::back_inserter(new_words));
135 new_words.erase(std::unique(new_words.begin(), new_words.end()),
136 new_words.end());
137 int result = VALID_CHANGE;
138 if (to_add.size() != new_words.size())
139 result |= DETECTED_DUPLICATE_WORDS;
140 // Do not add invalid words.
141 size_t size = new_words.size();
142 new_words.erase(std::remove_if(new_words.begin(),
143 new_words.end(),
144 IsInvalidWord),
145 new_words.end());
146 if (size != new_words.size())
147 result |= DETECTED_INVALID_WORDS;
148 // Save the sanitized words to be added.
149 std::swap(to_add, new_words);
150 return result;
151 }
152
153 // Removes word from |to_remove| that are missing from |existing| word list and
154 // sorts |to_remove|. Returns a bitmap of |ChangeSanitationResult| values.
155 int SanitizeWordsToRemove(const WordList& existing, WordList& to_remove) {
156 // Do not remove words that are missing from the dictionary.
157 std::sort(to_remove.begin(), to_remove.end());
158 WordList found_words;
159 std::set_intersection(existing.begin(),
160 existing.end(),
161 to_remove.begin(),
162 to_remove.end(),
163 std::back_inserter(found_words));
164 int result = VALID_CHANGE;
165 if (to_remove.size() > found_words.size())
166 result |= DETECTED_MISSING_WORDS;
167 // Save the sanitized words to be removed.
168 std::swap(to_remove, found_words);
169 return result;
170 }
171
172 } // namespace
173
174
175 SpellcheckCustomDictionary::Change::Change() {
176 }
177
178 SpellcheckCustomDictionary::Change::Change(
179 const SpellcheckCustomDictionary::Change& other)
180 : to_add_(other.to_add()),
181 to_remove_(other.to_remove()) {
182 }
183
184 SpellcheckCustomDictionary::Change::Change(const WordList& to_add)
185 : to_add_(to_add) {
186 }
187
188 SpellcheckCustomDictionary::Change::~Change() {
189 }
190
191 void SpellcheckCustomDictionary::Change::AddWord(const std::string& word) {
192 to_add_.push_back(word);
193 }
194
195 void SpellcheckCustomDictionary::Change::RemoveWord(const std::string& word) {
196 to_remove_.push_back(word);
197 }
198
199 int SpellcheckCustomDictionary::Change::Sanitize(const WordList& words) {
200 int result = VALID_CHANGE;
201 if (!to_add_.empty())
202 result |= SanitizeWordsToAdd(words, to_add_);
203 if (!to_remove_.empty())
204 result |= SanitizeWordsToRemove(words, to_remove_);
205 return result;
206 }
207
208 const WordList& SpellcheckCustomDictionary::Change::to_add() const {
209 return to_add_;
210 }
211
212 const WordList& SpellcheckCustomDictionary::Change::to_remove() const {
213 return to_remove_;
214 }
215
216 bool SpellcheckCustomDictionary::Change::empty() const {
217 return to_add_.empty() && to_remove_.empty();
218 }
219
220 SpellcheckCustomDictionary::SpellcheckCustomDictionary(Profile* profile)
221 : SpellcheckDictionary(profile),
222 custom_dictionary_path_(),
223 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
224 is_loaded_(false) {
225 DCHECK(profile);
226 custom_dictionary_path_ =
227 profile_->GetPath().Append(chrome::kCustomDictionaryFileName);
228 }
229
230 SpellcheckCustomDictionary::~SpellcheckCustomDictionary() {
231 }
232
233 const WordList& SpellcheckCustomDictionary::GetWords() const {
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235 return words_;
236 }
237
238 bool SpellcheckCustomDictionary::AddWord(const std::string& word) {
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
240 std::sort(words_.begin(), words_.end());
241 Change dictionary_change;
242 dictionary_change.AddWord(word);
243 int result = dictionary_change.Sanitize(GetWords());
244 Apply(dictionary_change);
245 Notify(dictionary_change);
246 Sync(dictionary_change);
247 Save(dictionary_change);
248 return result == VALID_CHANGE;
249 }
250
251 bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) {
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253 std::sort(words_.begin(), words_.end());
254 Change dictionary_change;
255 dictionary_change.RemoveWord(word);
256 int result = dictionary_change.Sanitize(GetWords());
257 Apply(dictionary_change);
258 Notify(dictionary_change);
259 Sync(dictionary_change);
260 Save(dictionary_change);
261 return result == VALID_CHANGE;
262 }
263
264 void SpellcheckCustomDictionary::AddObserver(Observer* observer) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 observers_.AddObserver(observer);
267 }
268
269 void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271 observers_.RemoveObserver(observer);
272 }
273
274 bool SpellcheckCustomDictionary::IsLoaded() {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276 return is_loaded_;
277 }
278
279 bool SpellcheckCustomDictionary::IsSyncing() {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
281 return !!sync_processor_.get();
282 }
283
284 void SpellcheckCustomDictionary::Load() {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286 BrowserThread::PostTaskAndReplyWithResult(
287 BrowserThread::FILE,
288 FROM_HERE,
289 base::Bind(&SpellcheckCustomDictionary::LoadDictionaryFile,
290 custom_dictionary_path_),
291 base::Bind(&SpellcheckCustomDictionary::OnLoaded,
292 weak_ptr_factory_.GetWeakPtr()));
293 }
294
295 syncer::SyncMergeResult SpellcheckCustomDictionary::MergeDataAndStartSyncing(
296 syncer::ModelType type,
297 const syncer::SyncDataList& initial_sync_data,
298 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
299 scoped_ptr<syncer::SyncErrorFactory> sync_error_handler) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 DCHECK(!sync_processor_.get());
302 DCHECK(!sync_error_handler_.get());
303 DCHECK(sync_processor.get());
304 DCHECK(sync_error_handler.get());
305 DCHECK_EQ(syncer::DICTIONARY, type);
306 sync_processor_ = sync_processor.Pass();
307 sync_error_handler_ = sync_error_handler.Pass();
308
309 // Build a list of words to add locally.
310 WordList to_add_locally;
311 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
312 it != initial_sync_data.end();
313 ++it) {
314 DCHECK_EQ(syncer::DICTIONARY, it->GetDataType());
315 to_add_locally.push_back(it->GetSpecifics().dictionary().word());
316 }
317
318 // Add remote words locally.
319 std::sort(words_.begin(), words_.end());
320 Change to_change_locally(to_add_locally);
321 to_change_locally.Sanitize(GetWords());
322 Apply(to_change_locally);
323 Notify(to_change_locally);
324 Save(to_change_locally);
325
326 // Add as many as possible local words remotely.
327 std::sort(words_.begin(), words_.end());
328 std::sort(to_add_locally.begin(), to_add_locally.end());
329 WordList to_add_remotely;
330 std::set_difference(words_.begin(),
331 words_.end(),
332 to_add_locally.begin(),
333 to_add_locally.end(),
334 std::back_inserter(to_add_remotely));
335
336 // Send local changes to the sync server.
337 Change to_change_remotely(to_add_remotely);
338 syncer::SyncMergeResult result(type);
339 result.set_error(Sync(to_change_remotely));
340 return result;
341 }
342
343 void SpellcheckCustomDictionary::StopSyncing(syncer::ModelType type) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345 DCHECK_EQ(syncer::DICTIONARY, type);
346 sync_processor_.reset();
347 sync_error_handler_.reset();
348 }
349
350 syncer::SyncDataList SpellcheckCustomDictionary::GetAllSyncData(
351 syncer::ModelType type) const {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 DCHECK_EQ(syncer::DICTIONARY, type);
354 syncer::SyncDataList data;
355 std::string word;
356 size_t i = 0;
357 for (WordList::const_iterator it = words_.begin();
358 it != words_.end() &&
359 i < chrome::spellcheck_common::MAX_SYNCABLE_DICTIONARY_WORDS;
360 ++it, ++i) {
361 word = *it;
362 sync_pb::EntitySpecifics specifics;
363 specifics.mutable_dictionary()->set_word(word);
364 data.push_back(syncer::SyncData::CreateLocalData(word, word, specifics));
365 }
366 return data;
367 }
368
369 syncer::SyncError SpellcheckCustomDictionary::ProcessSyncChanges(
370 const tracked_objects::Location& from_here,
371 const syncer::SyncChangeList& change_list) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 Change dictionary_change;
374 for (syncer::SyncChangeList::const_iterator it = change_list.begin();
375 it != change_list.end();
376 ++it) {
377 DCHECK(it->IsValid());
378 std::string word = it->sync_data().GetSpecifics().dictionary().word();
379 switch (it->change_type()) {
380 case syncer::SyncChange::ACTION_ADD:
381 dictionary_change.AddWord(word);
382 break;
383 case syncer::SyncChange::ACTION_DELETE:
384 dictionary_change.RemoveWord(word);
385 break;
386 default:
387 return sync_error_handler_->CreateAndUploadError(
388 FROM_HERE,
389 "Processing sync changes failed on change type " +
390 syncer::SyncChange::ChangeTypeToString(it->change_type()));
391 }
392 }
393
394 std::sort(words_.begin(), words_.end());
395 dictionary_change.Sanitize(GetWords());
396 Apply(dictionary_change);
397 Notify(dictionary_change);
398 Save(dictionary_change);
399
400 return syncer::SyncError();
401 }
402
403 // static
404 WordList SpellcheckCustomDictionary::LoadDictionaryFile(const FilePath& path) {
405 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
406 WordList words;
407 LoadDictionaryFileReliably(words, path);
408 if (!words.empty() && VALID_CHANGE != SanitizeWordsToAdd(WordList(), words))
409 SaveDictionaryFileReliably(words, path);
410 return words;
411 }
412
413 // static
414 void SpellcheckCustomDictionary::UpdateDictionaryFile(
415 const SpellcheckCustomDictionary::Change& dictionary_change,
416 const FilePath& path) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
418 if (dictionary_change.empty())
419 return;
420
421 WordList custom_words;
422 LoadDictionaryFileReliably(custom_words, path);
423
424 // Add words.
425 custom_words.insert(custom_words.end(),
426 dictionary_change.to_add().begin(),
427 dictionary_change.to_add().end());
428
429 // Remove words.
430 std::sort(custom_words.begin(), custom_words.end());
431 WordList remaining;
432 std::set_difference(custom_words.begin(),
433 custom_words.end(),
434 dictionary_change.to_remove().begin(),
435 dictionary_change.to_remove().end(),
436 std::back_inserter(remaining));
437 std::swap(custom_words, remaining);
438
439 SaveDictionaryFileReliably(custom_words, path);
440 }
441
442 void SpellcheckCustomDictionary::OnLoaded(WordList custom_words) {
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
444 std::sort(words_.begin(), words_.end());
445 Change dictionary_change(custom_words);
446 dictionary_change.Sanitize(GetWords());
447 Apply(dictionary_change);
448 Sync(dictionary_change);
449 is_loaded_ = true;
450 FOR_EACH_OBSERVER(Observer, observers_, OnCustomDictionaryLoaded());
451 }
452
453 // TODO(rlp): record metrics on custom word size
454 void SpellcheckCustomDictionary::Apply(
455 const SpellcheckCustomDictionary::Change& dictionary_change) {
456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
457 if (!dictionary_change.to_add().empty()) {
458 words_.insert(words_.end(),
459 dictionary_change.to_add().begin(),
460 dictionary_change.to_add().end());
461 }
462 if (!dictionary_change.to_remove().empty()) {
463 std::sort(words_.begin(), words_.end());
464 WordList updated_words;
465 std::set_difference(words_.begin(),
466 words_.end(),
467 dictionary_change.to_remove().begin(),
468 dictionary_change.to_remove().end(),
469 std::back_inserter(updated_words));
470 std::swap(words_, updated_words);
471 }
472 }
473
474 void SpellcheckCustomDictionary::Save(
475 const SpellcheckCustomDictionary::Change& dictionary_change) {
476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
477 BrowserThread::PostTask(
478 BrowserThread::FILE,
479 FROM_HERE,
480 base::Bind(&SpellcheckCustomDictionary::UpdateDictionaryFile,
481 dictionary_change,
482 custom_dictionary_path_));
483 }
484
485 syncer::SyncError SpellcheckCustomDictionary::Sync(
486 const SpellcheckCustomDictionary::Change& dictionary_change) {
487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488 syncer::SyncError error;
489 if (!IsSyncing() || dictionary_change.empty())
490 return error;
491
492 // The number of words on the sync server should not exceed the limits.
493 int server_size = static_cast<int>(words_.size()) -
494 static_cast<int>(dictionary_change.to_add().size());
495 int max_upload_size = std::max(
496 0,
497 static_cast<int>(
498 chrome::spellcheck_common::MAX_SYNCABLE_DICTIONARY_WORDS) -
499 server_size);
500 int upload_size = std::min(
501 static_cast<int>(dictionary_change.to_add().size()),
502 max_upload_size);
503
504 syncer::SyncChangeList sync_change_list;
505 int i = 0;
506
507 for (WordList::const_iterator it = dictionary_change.to_add().begin();
508 it != dictionary_change.to_add().end() && i < upload_size;
509 ++it, ++i) {
510 std::string word = *it;
511 sync_pb::EntitySpecifics specifics;
512 specifics.mutable_dictionary()->set_word(word);
513 sync_change_list.push_back(syncer::SyncChange(
514 FROM_HERE,
515 syncer::SyncChange::ACTION_ADD,
516 syncer::SyncData::CreateLocalData(word, word, specifics)));
517 }
518
519 for (WordList::const_iterator it = dictionary_change.to_remove().begin();
520 it != dictionary_change.to_remove().end();
521 ++it) {
522 std::string word = *it;
523 sync_pb::EntitySpecifics specifics;
524 specifics.mutable_dictionary()->set_word(word);
525 sync_change_list.push_back(syncer::SyncChange(
526 FROM_HERE,
527 syncer::SyncChange::ACTION_DELETE,
528 syncer::SyncData::CreateLocalData(word, word, specifics)));
529 }
530
531 // Send the changes to the sync processor.
532 error = sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
533 if (error.IsSet())
534 return error;
535
536 // Turn off syncing of this dictionary if the server already has the maximum
537 // number of words.
538 if (words_.size() > chrome::spellcheck_common::MAX_SYNCABLE_DICTIONARY_WORDS)
539 StopSyncing(syncer::DICTIONARY);
540
541 return error;
542 }
543
544 void SpellcheckCustomDictionary::Notify(
545 const SpellcheckCustomDictionary::Change& dictionary_change) {
546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547 if (!IsLoaded() || dictionary_change.empty())
548 return;
549 FOR_EACH_OBSERVER(Observer,
550 observers_,
551 OnCustomDictionaryChanged(dictionary_change));
552 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698