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 <string>
9
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/weak_ptr.h"
13#include "base/observer_list.h"
14#include "chrome/browser/spellchecker/spellcheck_dictionary.h"
15#include "chrome/common/spellcheck_common.h"
16#include "sync/api/syncable_service.h"
17
18// Defines a custom dictionary where users can add their own words. All words
19// must be UTF8, between 1 and 99 bytes long, and without leading or trailing
20// ASCII whitespace. The dictionary contains its own checksum when saved on
21// disk. Example dictionary file contents:
22//
23//   bar
24//   foo
25//   checksum_v1 = ec3df4034567e59e119fcf87f2d9bad4
26//
27class SpellcheckCustomDictionary : public SpellcheckDictionary,
28                                   public syncer::SyncableService {
29 public:
30  // A change to the dictionary.
31  class Change {
32   public:
33    Change();
34    Change(const Change& other);
35    explicit Change(const chrome::spellcheck_common::WordList& to_add);
36    ~Change();
37
38    // Adds |word| in this change.
39    void AddWord(const std::string& word);
40
41    // Removes |word| in this change.
42    void RemoveWord(const std::string& word);
43
44    // Prepares this change to be applied to |words| by removing duplicate and
45    // invalid words from words to be added, removing missing words from words
46    // to be removed, and sorting both lists of words. Assumes that |words| is
47    // sorted. Returns a bitmap of |ChangeSanitationResult| values.
48    int Sanitize(const chrome::spellcheck_common::WordSet& words);
49
50    // Returns the words to be added in this change.
51    const chrome::spellcheck_common::WordList& to_add() const;
52
53    // Returns the words to be removed in this change.
54    const chrome::spellcheck_common::WordList& to_remove() const;
55
56    // Returns true if there are no changes to be made. Otherwise returns false.
57    bool empty() const;
58
59   private:
60    // The words to be added.
61    chrome::spellcheck_common::WordList to_add_;
62
63    // The words to be removed.
64    chrome::spellcheck_common::WordList to_remove_;
65  };
66
67  // Interface to implement for dictionary load and change observers.
68  class Observer {
69   public:
70    // Called when the custom dictionary has been loaded.
71    virtual void OnCustomDictionaryLoaded() = 0;
72
73    // Called when the custom dictionary has been changed.
74    virtual void OnCustomDictionaryChanged(const Change& dictionary_change) = 0;
75  };
76
77  explicit SpellcheckCustomDictionary(const base::FilePath& path);
78  virtual ~SpellcheckCustomDictionary();
79
80  // Returns the in-memory cache of words in the custom dictionary.
81  const chrome::spellcheck_common::WordSet& GetWords() const;
82
83  // Adds |word| to the dictionary, schedules a write to disk, and notifies
84  // observers of the change. Returns true if |word| is valid and not a
85  // duplicate. Otherwise returns false.
86  bool AddWord(const std::string& word);
87
88  // Removes |word| from the dictionary, schedules a write to disk, and notifies
89  // observers of the change. Returns true if |word| was found. Otherwise
90  // returns false.
91  bool RemoveWord(const std::string& word);
92
93  // Returns true if the dictionary contains |word|. Otherwise returns false.
94  bool HasWord(const std::string& word) const;
95
96  // Adds |observer| to be notified of dictionary events and changes.
97  void AddObserver(Observer* observer);
98
99  // Removes |observer| to stop notifications of dictionary events and changes.
100  void RemoveObserver(Observer* observer);
101
102  // Returns true if the dictionary has been loaded. Otherwise returns false.
103  bool IsLoaded();
104
105  // Returns true if the dictionary is being synced. Otherwise returns false.
106  bool IsSyncing();
107
108  // Overridden from SpellcheckDictionary:
109  virtual void Load() OVERRIDE;
110
111  // Overridden from syncer::SyncableService:
112  virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
113      syncer::ModelType type,
114      const syncer::SyncDataList& initial_sync_data,
115      scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
116      scoped_ptr<syncer::SyncErrorFactory> sync_error_handler) OVERRIDE;
117  virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
118  virtual syncer::SyncDataList GetAllSyncData(
119      syncer::ModelType type) const OVERRIDE;
120  virtual syncer::SyncError ProcessSyncChanges(
121      const tracked_objects::Location& from_here,
122      const syncer::SyncChangeList& change_list) OVERRIDE;
123
124 private:
125  friend class DictionarySyncIntegrationTestHelper;
126  friend class SpellcheckCustomDictionaryTest;
127
128  // Returns the list of words in the custom spellcheck dictionary at |path|.
129  // Makes sure that the custom dictionary file does not have duplicates and
130  // contains only valid words.
131  static chrome::spellcheck_common::WordList LoadDictionaryFile(
132      const base::FilePath& path);
133
134  // Applies the change in |dictionary_change| to the custom spellcheck
135  // dictionary. Assumes that |dictionary_change| has been sanitized.
136  static void UpdateDictionaryFile(
137      const Change& dictionary_change,
138      const base::FilePath& path);
139
140  // The reply point for PostTaskAndReplyWithResult, called when
141  // LoadDictionaryFile finishes reading the dictionary file. Does not modify
142  // |custom_words|, but cannot be a const-ref due to the signature of
143  // PostTaskAndReplyWithResult.
144  void OnLoaded(chrome::spellcheck_common::WordList custom_words);
145
146  // Applies the |dictionary_change| to the in-memory copy of the dictionary.
147  // Assumes that words in |dictionary_change| are sorted.
148  void Apply(const Change& dictionary_change);
149
150  // Schedules a write of |dictionary_change| to disk. Assumes that words in
151  // |dictionary_change| are sorted.
152  void Save(const Change& dictionary_change);
153
154  // Notifies the sync service of the |dictionary_change|. Syncs up to the
155  // maximum syncable words on the server. Disables syncing of this dictionary
156  // if the server contains the maximum number of syncable words.
157  syncer::SyncError Sync(const Change& dictionary_change);
158
159  // Notifies observers of the dictionary change if the dictionary has been
160  // changed.
161  void Notify(const Change& dictionary_change);
162
163  // In-memory cache of the custom words file.
164  chrome::spellcheck_common::WordSet words_;
165
166  // A path for custom dictionary.
167  base::FilePath custom_dictionary_path_;
168
169  // Observers for changes in dictionary load status and content changes.
170  ObserverList<Observer> observers_;
171
172  // Used to send local changes to the sync infrastructure.
173  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
174
175  // Used to send sync-related errors to the sync infrastructure.
176  scoped_ptr<syncer::SyncErrorFactory> sync_error_handler_;
177
178  // True if the dictionary has been loaded. Otherwise false.
179  bool is_loaded_;
180
181  // Used to create weak pointers for an instance of this class.
182  base::WeakPtrFactory<SpellcheckCustomDictionary> weak_ptr_factory_;
183
184  DISALLOW_COPY_AND_ASSIGN(SpellcheckCustomDictionary);
185};
186
187#endif  // CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_CUSTOM_DICTIONARY_H_
188