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