1 // Copyright 2018 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 #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h"
6 
7 #include "base/strings/utf_string_conversions.h"
8 #include "components/autofill/core/browser/autofill_profile_sync_util.h"
9 #include "components/autofill/core/browser/data_model/autofill_profile.h"
10 #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
11 #include "components/autofill/core/browser/field_types.h"
12 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
13 #include "components/autofill/core/browser/webdata/autofill_table.h"
14 #include "components/sync/model/model_error.h"
15 
16 namespace autofill {
17 
18 using base::Optional;
19 using syncer::ModelError;
20 
AutofillProfileSyncDifferenceTracker(AutofillTable * table)21 AutofillProfileSyncDifferenceTracker::AutofillProfileSyncDifferenceTracker(
22     AutofillTable* table)
23     : table_(table) {}
24 
~AutofillProfileSyncDifferenceTracker()25 AutofillProfileSyncDifferenceTracker::~AutofillProfileSyncDifferenceTracker() {}
26 
27 Optional<ModelError>
IncorporateRemoteProfile(std::unique_ptr<AutofillProfile> remote)28 AutofillProfileSyncDifferenceTracker::IncorporateRemoteProfile(
29     std::unique_ptr<AutofillProfile> remote) {
30   const std::string remote_storage_key =
31       GetStorageKeyFromAutofillProfile(*remote);
32 
33   if (!GetLocalOnlyEntries()) {
34     return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
35   }
36 
37   Optional<AutofillProfile> local_with_same_storage_key =
38       ReadEntry(remote_storage_key);
39 
40   if (local_with_same_storage_key) {
41     // The remote profile already exists locally with the same key. Update
42     // the local entry with remote data.
43     std::unique_ptr<AutofillProfile> updated =
44         std::make_unique<AutofillProfile>(local_with_same_storage_key.value());
45     // We ignore remote updates to a verified profile because we want to keep
46     // the exact version that the user edited by hand.
47     if (local_with_same_storage_key->IsVerified() && !remote->IsVerified()) {
48       return base::nullopt;
49     }
50     updated->OverwriteDataFrom(*remote);
51     // TODO(jkrcal): if |updated| deviates from |remote|, we should sync it back
52     // up. The only way |updated| can differ is having some extra fields
53     // compared to |remote|. Thus, this cannot lead to an infinite loop of
54     // commits from two clients as each commit decreases the set of empty
55     // fields. This invariant depends on the implementation of
56     // OverwriteDataFrom() and thus should be enforced by a DCHECK.
57 
58     if (!updated->EqualsForSyncPurposes(*local_with_same_storage_key)) {
59       // We need to write back locally new changes in this entry.
60       update_to_local_.push_back(std::move(updated));
61     }
62     GetLocalOnlyEntries()->erase(remote_storage_key);
63     return base::nullopt;
64   }
65 
66   // Check if profile appears under a different storage key to be de-duplicated.
67   // TODO(crbug.com/1043683): Deal with rare cases when an remote update
68   // contains several exact duplicates (with different guids). We should not
69   // only search in local only entries but also in |update_to_local_| and
70   // |add_to_local_|. Likely needs a bit of refactoring to make the resulting
71   // code easy to understand.
72   for (const auto& pair : *GetLocalOnlyEntries()) {
73     const std::string& local_storage_key = pair.first;
74     const AutofillProfile& local = *pair.second;
75 
76     // Look for exact duplicates, compare only profile contents (and
77     // ignore origin and language code in comparison).
78     if (local.Compare(*remote) == 0) {
79       // A duplicate found: keep the version with the bigger storage key.
80       DVLOG(2)
81           << "[AUTOFILL SYNC] The profile "
82           << base::UTF16ToUTF8(local.GetRawInfo(NAME_FIRST))
83           << base::UTF16ToUTF8(local.GetRawInfo(NAME_LAST))
84           << " already exists with a different storage key; keep the bigger "
85           << (remote_storage_key > local_storage_key ? "remote" : "local")
86           << " key " << std::max(remote_storage_key, local_storage_key)
87           << " and delete the smaller key "
88           << std::min(remote_storage_key, local_storage_key);
89       if (remote_storage_key > local_storage_key) {
90         // We keep the remote entity and delete the local one.
91         // Ensure that a verified profile can never revert back to an unverified
92         // one. In such a case, take over the old origin for the new entry.
93         if (local.IsVerified() && !remote->IsVerified()) {
94           remote->set_origin(local.origin());
95           // Save a copy of the remote profile also to sync.
96           save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
97         }
98         add_to_local_.push_back(std::move(remote));
99         // Deleting from sync is a no-op if it is local-only so far.
100         // There are a few ways how a synced local entry A could theoretically
101         // receive a remote duplicate B with a larger GUID:
102         //  1) Remote entity B got uploaded by another client through initial
103         //     sync. That client thus also knew about A and issued a deletion of
104         //     A at the same time. This client (if receiving creation of B
105         //     first) resolves the conflict in the same way and re-issues the
106         //     deletion of A. In most cases the redundant deletion does not even
107         //     get sent as the processor already knows A got deleted remotely.
108         //  2) Remote entity B got uploaded by another client through race
109         //     condition (i.e. not knowing about A, yet). In practice, this only
110         //     happens when two clients simultaneously convert a server profile
111         //     into local profiles. If the other client goes offline before
112         //     receiving A, this client is responsible for deleting A from the
113         //     server and thus must issue a deletion. (In most cases, the other
114         //     client does not go offline and thus both clients issue a deletion
115         //     of A independently).
116         //  3) (a paranoid case) Remote entity B got uploaded by another client
117         //     by an error, i.e. already as a duplicate given their local state.
118         //     Through standard flows, it should be impossible (duplicates are
119         //     cought early in PDM code so such a change attempt does not even
120         //     propagate to the sync bridge). Still, it's good to treat this
121         //     case here for robustness.
122         delete_from_sync_.insert(local_storage_key);
123         DeleteFromLocal(local_storage_key);
124       } else {
125         // We keep the local entity and delete the remote one.
126         // Ensure that a verified profile can never revert back to an unverified
127         // one. In such a case, modify the origin and re-upload. Otherwise,
128         // there's no need to upload it: either is was already uploaded before
129         // (if this is incremental sync) or we'll upload it with all the
130         // remaining data in GetLocalOnlyEntries (if this is an initial sync).
131         if (remote->IsVerified() && !local.IsVerified()) {
132           auto modified_local = std::make_unique<AutofillProfile>(local);
133           modified_local->set_origin(remote->origin());
134           update_to_local_.push_back(
135               std::make_unique<AutofillProfile>(*modified_local));
136           save_to_sync_.push_back(std::move(modified_local));
137           // The local entity is already marked for upload so it is not local
138           // only anymore (we do not want to upload it once again while flushing
139           // if this is initial sync).
140           GetLocalOnlyEntries()->erase(local_storage_key);
141         }
142         delete_from_sync_.insert(remote_storage_key);
143       }
144       return base::nullopt;
145     }
146   }
147 
148   // If no duplicate was found, just add the remote profile.
149   add_to_local_.push_back(std::move(remote));
150   return base::nullopt;
151 }
152 
153 Optional<ModelError>
IncorporateRemoteDelete(const std::string & storage_key)154 AutofillProfileSyncDifferenceTracker::IncorporateRemoteDelete(
155     const std::string& storage_key) {
156   DCHECK(!storage_key.empty());
157   DeleteFromLocal(storage_key);
158   return base::nullopt;
159 }
160 
FlushToLocal(base::OnceClosure autofill_changes_callback)161 Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToLocal(
162     base::OnceClosure autofill_changes_callback) {
163   for (const std::string& storage_key : delete_from_local_) {
164     if (!table_->RemoveAutofillProfile(storage_key)) {
165       return ModelError(FROM_HERE, "Failed deleting from WebDatabase");
166     }
167   }
168   for (const std::unique_ptr<AutofillProfile>& entry : add_to_local_) {
169     if (!table_->AddAutofillProfile(*entry)) {
170       return ModelError(FROM_HERE, "Failed updating WebDatabase");
171     }
172   }
173   for (const std::unique_ptr<AutofillProfile>& entry : update_to_local_) {
174     if (!table_->UpdateAutofillProfile(*entry)) {
175       return ModelError(FROM_HERE, "Failed updating WebDatabase");
176     }
177   }
178   if (!delete_from_local_.empty() || !add_to_local_.empty() ||
179       !update_to_local_.empty()) {
180     std::move(autofill_changes_callback).Run();
181   }
182   return base::nullopt;
183 }
184 
FlushToSync(std::vector<std::unique_ptr<AutofillProfile>> * profiles_to_upload_to_sync,std::vector<std::string> * profiles_to_delete_from_sync)185 Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync(
186     std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
187     std::vector<std::string>* profiles_to_delete_from_sync) {
188   for (std::unique_ptr<AutofillProfile>& entry : save_to_sync_) {
189     profiles_to_upload_to_sync->push_back(std::move(entry));
190   }
191   for (const std::string& entry : delete_from_sync_) {
192     profiles_to_delete_from_sync->push_back(std::move(entry));
193   }
194   return base::nullopt;
195 }
196 
ReadEntry(const std::string & storage_key)197 Optional<AutofillProfile> AutofillProfileSyncDifferenceTracker::ReadEntry(
198     const std::string& storage_key) {
199   DCHECK(GetLocalOnlyEntries());
200   auto iter = GetLocalOnlyEntries()->find(storage_key);
201   if (iter != GetLocalOnlyEntries()->end()) {
202     return *iter->second;
203   }
204   return base::nullopt;
205 }
206 
DeleteFromLocal(const std::string & storage_key)207 void AutofillProfileSyncDifferenceTracker::DeleteFromLocal(
208     const std::string& storage_key) {
209   DCHECK(GetLocalOnlyEntries());
210   delete_from_local_.insert(storage_key);
211   GetLocalOnlyEntries()->erase(storage_key);
212 }
213 
214 std::map<std::string, std::unique_ptr<AutofillProfile>>*
GetLocalOnlyEntries()215 AutofillProfileSyncDifferenceTracker::GetLocalOnlyEntries() {
216   if (!InitializeLocalOnlyEntriesIfNeeded()) {
217     return nullptr;
218   }
219   return &local_only_entries_;
220 }
221 
222 bool AutofillProfileSyncDifferenceTracker::
InitializeLocalOnlyEntriesIfNeeded()223     InitializeLocalOnlyEntriesIfNeeded() {
224   if (local_only_entries_initialized_) {
225     return true;
226   }
227 
228   std::vector<std::unique_ptr<AutofillProfile>> entries;
229   if (!table_->GetAutofillProfiles(&entries)) {
230     return false;
231   }
232 
233   for (std::unique_ptr<AutofillProfile>& entry : entries) {
234     std::string storage_key = GetStorageKeyFromAutofillProfile(*entry);
235     local_only_entries_[storage_key] = std::move(entry);
236   }
237 
238   local_only_entries_initialized_ = true;
239   return true;
240 }
241 
242 AutofillProfileInitialSyncDifferenceTracker::
AutofillProfileInitialSyncDifferenceTracker(AutofillTable * table)243     AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table)
244     : AutofillProfileSyncDifferenceTracker(table) {}
245 
246 AutofillProfileInitialSyncDifferenceTracker::
~AutofillProfileInitialSyncDifferenceTracker()247     ~AutofillProfileInitialSyncDifferenceTracker() {}
248 
249 Optional<ModelError>
IncorporateRemoteDelete(const std::string & storage_key)250 AutofillProfileInitialSyncDifferenceTracker::IncorporateRemoteDelete(
251     const std::string& storage_key) {
252   // Remote delete is not allowed in initial sync.
253   NOTREACHED();
254   return base::nullopt;
255 }
256 
FlushToSync(std::vector<std::unique_ptr<AutofillProfile>> * profiles_to_upload_to_sync,std::vector<std::string> * profiles_to_delete_from_sync)257 Optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync(
258     std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync,
259     std::vector<std::string>* profiles_to_delete_from_sync) {
260   // First, flush standard updates to sync.
261   AutofillProfileSyncDifferenceTracker::FlushToSync(
262       profiles_to_upload_to_sync, profiles_to_delete_from_sync);
263 
264   // For initial sync, we additionally need to upload all local only entries.
265   if (!GetLocalOnlyEntries()) {
266     return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
267   }
268   for (auto& pair : *GetLocalOnlyEntries()) {
269     std::string storage_key = pair.first;
270     // No deletions coming from remote are allowed for initial sync.
271     DCHECK(delete_from_local_.count(storage_key) == 0);
272     profiles_to_upload_to_sync->push_back(std::move(pair.second));
273   }
274   return base::nullopt;
275 }
276 
277 Optional<ModelError>
MergeSimilarEntriesForInitialSync(const std::string & app_locale)278 AutofillProfileInitialSyncDifferenceTracker::MergeSimilarEntriesForInitialSync(
279     const std::string& app_locale) {
280   if (!GetLocalOnlyEntries()) {
281     return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
282   }
283 
284   // This merge cannot happen on the fly during IncorporateRemoteSpecifics().
285   // Namely, we do not want to merge a local entry with a _similar_ remote
286   // entry if anoter perfectly fitting remote entry comes later during the
287   // initial sync (a remote entry fits perfectly to a given local entry if
288   // it has fully equal data or even the same storage key). After all the calls
289   // to IncorporateRemoteSpecifics() are over, GetLocalOnlyEntries() only
290   // contains unmatched entries that can be safely merged with similar remote
291   // entries.
292 
293   AutofillProfileComparator comparator(app_locale);
294   // Loop over all new remote entries to find merge candidates. Using
295   // non-const reference because we want to update |remote| in place if
296   // needed.
297   for (std::unique_ptr<AutofillProfile>& remote : add_to_local_) {
298     Optional<AutofillProfile> local =
299         FindMergeableLocalEntry(*remote, comparator);
300     if (!local) {
301       continue;
302     }
303 
304     DVLOG(2)
305         << "[AUTOFILL SYNC] A similar profile to "
306         << base::UTF16ToUTF8(remote->GetRawInfo(NAME_FIRST))
307         << base::UTF16ToUTF8(remote->GetRawInfo(NAME_LAST))
308         << " already exists with a different storage key; keep the remote key"
309         << GetStorageKeyFromAutofillProfile(*remote)
310         << ", merge local data into it and delete the local key"
311         << GetStorageKeyFromAutofillProfile(*local);
312 
313     // For similar profile pairs, the local profile is always removed and its
314     // content merged (if applicable) in the profile that came from sync.
315     AutofillProfile remote_before_merge = *remote;
316     remote->MergeDataFrom(*local, app_locale);
317     if (!remote->EqualsForSyncPurposes(remote_before_merge)) {
318       // We need to sync new changes in the entry back to the server.
319       save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
320       // |remote| is updated in place within |add_to_local_| so the newest
321       // merged version is stored to local.
322     }
323 
324     DeleteFromLocal(GetStorageKeyFromAutofillProfile(*local));
325   }
326 
327   return base::nullopt;
328 }
329 
330 Optional<AutofillProfile>
FindMergeableLocalEntry(const AutofillProfile & remote,const AutofillProfileComparator & comparator)331 AutofillProfileInitialSyncDifferenceTracker::FindMergeableLocalEntry(
332     const AutofillProfile& remote,
333     const AutofillProfileComparator& comparator) {
334   DCHECK(GetLocalOnlyEntries());
335 
336   // Both the remote and the local entry need to be non-verified to be
337   // mergeable.
338   if (remote.IsVerified()) {
339     return base::nullopt;
340   }
341 
342   // Check if there is a mergeable local profile.
343   for (const auto& pair : *GetLocalOnlyEntries()) {
344     const AutofillProfile& local_candidate = *pair.second;
345     if (!local_candidate.IsVerified() &&
346         comparator.AreMergeable(local_candidate, remote)) {
347       return local_candidate;
348     }
349   }
350   return base::nullopt;
351 }
352 
353 }  // namespace autofill
354