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 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_CUSTOM_DICTIONARY_H_ 6 #define CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_CUSTOM_DICTIONARY_H_ 7 8 #include <memory> 9 #include <set> 10 #include <string> 11 12 #include "base/cancelable_callback.h" 13 #include "base/files/file_path.h" 14 #include "base/macros.h" 15 #include "base/memory/ref_counted.h" 16 #include "base/memory/weak_ptr.h" 17 #include "base/observer_list.h" 18 #include "base/sequenced_task_runner.h" 19 #include "components/spellcheck/browser/spellcheck_dictionary.h" 20 #include "components/sync/model/model_error.h" 21 #include "components/sync/model/sync_data.h" 22 #include "components/sync/model/syncable_service.h" 23 24 namespace base { 25 class Location; 26 } 27 28 namespace syncer { 29 class SyncErrorFactory; 30 class SyncChangeProcessor; 31 } 32 33 // Defines a custom dictionary where users can add their own words. All words 34 // must be UTF8, between 1 and 99 bytes long, and without leading or trailing 35 // ASCII whitespace. The dictionary contains its own checksum when saved on 36 // disk. Example dictionary file contents: 37 // 38 // bar 39 // foo 40 // checksum_v1 = ec3df4034567e59e119fcf87f2d9bad4 41 // 42 class SpellcheckCustomDictionary : public SpellcheckDictionary, 43 public syncer::SyncableService { 44 public: 45 // A change to the dictionary. 46 class Change { 47 public: 48 Change(); 49 ~Change(); 50 51 // Adds |word| in this change. 52 void AddWord(const std::string& word); 53 54 // Adds |words| in this change. 55 void AddWords(const std::set<std::string>& words); 56 57 // Removes |word| in this change. 58 void RemoveWord(const std::string& word); 59 60 // Clear the whole dictionary before doing other operations. When saved, 61 // also deletes the backup file. 62 void Clear(); 63 64 // Prepares this change to be applied to |words| by removing duplicate and 65 // invalid words from words to be added and removing missing words from 66 // words to be removed. Returns a bitmap of |ChangeSanitationResult| values. 67 int Sanitize(const std::set<std::string>& words); 68 69 // Returns the words to be added in this change. to_add()70 const std::set<std::string>& to_add() const { return to_add_; } 71 72 // Returns the words to be removed in this change. to_remove()73 const std::set<std::string>& to_remove() const { 74 return to_remove_; 75 } 76 77 // Returns true if the dictionary should be cleared first. clear()78 bool clear() const { return clear_; } 79 80 // Returns true if there are no changes to be made. Otherwise returns false. empty()81 bool empty() const { 82 return !clear_ && to_add_.empty() && to_remove_.empty(); 83 } 84 85 private: 86 // The words to be added. 87 std::set<std::string> to_add_; 88 89 // The words to be removed. 90 std::set<std::string> to_remove_; 91 92 // Whether to clear everything before adding words. 93 bool clear_ = false; 94 95 DISALLOW_COPY_AND_ASSIGN(Change); 96 }; 97 98 // Interface to implement for dictionary load and change observers. 99 class Observer { 100 public: 101 // Called when the custom dictionary has been loaded. 102 virtual void OnCustomDictionaryLoaded() = 0; 103 104 // Called when the custom dictionary has been changed. 105 virtual void OnCustomDictionaryChanged(const Change& dictionary_change) = 0; 106 }; 107 108 struct LoadFileResult { 109 LoadFileResult(); 110 ~LoadFileResult(); 111 112 // The contents of the custom dictionary file or its backup. Does not 113 // contain data that failed checksum. Does not contain invalid words. 114 std::set<std::string> words; 115 116 // True when the custom dictionary file on disk has a valid checksum and 117 // contains only valid words. 118 bool is_valid_file; 119 120 private: 121 DISALLOW_COPY_AND_ASSIGN(LoadFileResult); 122 }; 123 124 // The dictionary will be saved in |dictionary_directory_name|. 125 explicit SpellcheckCustomDictionary( 126 const base::FilePath& dictionary_directory_name); 127 ~SpellcheckCustomDictionary() override; 128 129 // Returns the in-memory cache of words in the custom dictionary. 130 const std::set<std::string>& GetWords() const; 131 132 // Adds |word| to the dictionary, schedules a write to disk, and notifies 133 // observers of the change. Returns true if |word| is valid and not a 134 // duplicate. Otherwise returns false. 135 bool AddWord(const std::string& word); 136 137 // Removes |word| from the dictionary, schedules a write to disk, and notifies 138 // observers of the change. Returns true if |word| was found. Otherwise 139 // returns false. 140 bool RemoveWord(const std::string& word); 141 142 // Returns true if the dictionary contains |word|. Otherwise returns false. 143 bool HasWord(const std::string& word) const; 144 145 // Removes all words in the dictionary, and schedules a write to disk. 146 void Clear(); 147 148 // Adds |observer| to be notified of dictionary events and changes. 149 void AddObserver(Observer* observer); 150 151 // Removes |observer| to stop notifications of dictionary events and changes. 152 void RemoveObserver(Observer* observer); 153 154 // Returns true if the dictionary has been loaded. Otherwise returns false. 155 bool IsLoaded(); 156 157 // Returns true if the dictionary is being synced. Otherwise returns false. 158 bool IsSyncing(); 159 160 // Overridden from SpellcheckDictionary: 161 void Load() override; 162 163 // Overridden from syncer::SyncableService: 164 void WaitUntilReadyToSync(base::OnceClosure done) override; 165 base::Optional<syncer::ModelError> MergeDataAndStartSyncing( 166 syncer::ModelType type, 167 const syncer::SyncDataList& initial_sync_data, 168 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, 169 std::unique_ptr<syncer::SyncErrorFactory> sync_error_handler) override; 170 void StopSyncing(syncer::ModelType type) override; 171 syncer::SyncDataList GetAllSyncDataForTesting(syncer::ModelType type) const; 172 base::Optional<syncer::ModelError> ProcessSyncChanges( 173 const base::Location& from_here, 174 const syncer::SyncChangeList& change_list) override; 175 176 private: 177 friend class DictionarySyncIntegrationTestHelper; 178 friend class SpellcheckCustomDictionaryTest; 179 180 FRIEND_TEST_ALL_PREFIXES(ChromeBrowsingDataRemoverDelegateTest, 181 WipeCustomDictionaryData); 182 183 // Returns the list of words in the custom spellcheck dictionary at |path|. 184 // Validates that the custom dictionary file does not have duplicates and 185 // contains only valid words. Must be called on the FILE thread. 186 static std::unique_ptr<LoadFileResult> LoadDictionaryFile( 187 const base::FilePath& path); 188 189 // Applies the change in |dictionary_change| to the custom spellcheck 190 // dictionary. Assumes that |dictionary_change| has been sanitized. Must be 191 // called on the FILE thread. Takes ownership of |dictionary_change|. 192 static void UpdateDictionaryFile(std::unique_ptr<Change> dictionary_change, 193 const base::FilePath& path); 194 195 // The reply point for PostTaskAndReplyWithResult, called when 196 // LoadDictionaryFile finishes reading the dictionary file. 197 void OnLoaded(std::unique_ptr<LoadFileResult> result); 198 199 // Applies the |dictionary_change| to the in-memory copy of the dictionary. 200 void Apply(const Change& dictionary_change); 201 202 // Schedules a write of the words in |load_file_result| to disk when the 203 // custom dictionary file is invalid. 204 void FixInvalidFile(std::unique_ptr<LoadFileResult> load_file_result); 205 206 // Schedules a write of |dictionary_change| to disk. Takes ownership of 207 // |dictionary_change| to pass it to the FILE thread. 208 void Save(std::unique_ptr<Change> dictionary_change); 209 210 // Notifies the sync service of the |dictionary_change|. Syncs up to the 211 // maximum syncable words on the server. Disables syncing of this dictionary 212 // if the server contains the maximum number of syncable words. 213 base::Optional<syncer::ModelError> Sync(const Change& dictionary_change); 214 215 // Notifies observers of the dictionary change if the dictionary has been 216 // changed. 217 void Notify(const Change& dictionary_change); 218 219 // Task runner where the file operations takes place. 220 scoped_refptr<base::SequencedTaskRunner> task_runner_; 221 222 // In-memory cache of the custom words file. 223 std::set<std::string> words_; 224 225 // The path to the custom dictionary file. 226 base::FilePath custom_dictionary_path_; 227 228 // Observers for dictionary load and content changes. 229 base::ObserverList<Observer>::Unchecked observers_; 230 231 // Used to send local changes to the sync infrastructure. 232 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_; 233 234 // Used to send sync-related errors to the sync infrastructure. 235 std::unique_ptr<syncer::SyncErrorFactory> sync_error_handler_; 236 237 // True if the dictionary has been loaded. Otherwise false. 238 bool is_loaded_; 239 240 base::OnceClosure wait_until_ready_to_sync_cb_; 241 242 // A post-startup task to fix the invalid custom dictionary file. 243 base::CancelableOnceClosure fix_invalid_file_; 244 245 // Used to create weak pointers for an instance of this class. 246 base::WeakPtrFactory<SpellcheckCustomDictionary> weak_ptr_factory_{this}; 247 248 DISALLOW_COPY_AND_ASSIGN(SpellcheckCustomDictionary); 249 }; 250 251 #endif // CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_CUSTOM_DICTIONARY_H_ 252