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#include "chrome/browser/webdata/autocomplete_syncable_service.h"
6
7#include "base/location.h"
8#include "base/logging.h"
9#include "base/strings/utf_string_conversions.h"
10#include "components/autofill/core/browser/webdata/autofill_table.h"
11#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
12#include "components/webdata/common/web_database.h"
13#include "content/public/browser/browser_thread.h"
14#include "net/base/escape.h"
15#include "sync/api/sync_error.h"
16#include "sync/api/sync_error_factory.h"
17#include "sync/protocol/autofill_specifics.pb.h"
18#include "sync/protocol/sync.pb.h"
19
20using autofill::AutofillChange;
21using autofill::AutofillChangeList;
22using autofill::AutofillEntry;
23using autofill::AutofillKey;
24using autofill::AutofillTable;
25using autofill::AutofillWebDataService;
26using autofill::AutofillWebDataBackend;
27using content::BrowserThread;
28
29namespace {
30
31const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
32
33// Merges timestamps from the |sync_timestamps| and the |local_entry|.
34// Returns true if they were different, false if they were the same.  If the
35// timestamps were different, fills |date_created| and |date_last_used| with the
36// merged timestamps.  The |sync_timestamps| vector is assumed to be sorted.
37bool MergeTimestamps(
38    const google::protobuf::RepeatedField<int64_t>& sync_timestamps,
39    const AutofillEntry& local_entry,
40    base::Time* date_created,
41    base::Time* date_last_used) {
42  if (sync_timestamps.size() == 0) {
43    *date_created = local_entry.date_created();
44    *date_last_used = local_entry.date_last_used();
45    return true;
46  }
47
48  base::Time sync_date_created =
49      base::Time::FromInternalValue(*sync_timestamps.begin());
50  base::Time sync_date_last_used =
51      base::Time::FromInternalValue(*sync_timestamps.rbegin());
52
53  if (sync_date_created == local_entry.date_created() &&
54      sync_date_last_used == local_entry.date_last_used())
55    return false;
56
57  *date_created = std::min(local_entry.date_created(), sync_date_created);
58  *date_last_used = std::max(local_entry.date_last_used(), sync_date_last_used);
59  return true;
60}
61
62void* UserDataKey() {
63  // Use the address of a static that COMDAT folding won't ever fold
64  // with something else.
65  static int user_data_key = 0;
66  return reinterpret_cast<void*>(&user_data_key);
67}
68
69}  // namespace
70
71AutocompleteSyncableService::AutocompleteSyncableService(
72    AutofillWebDataBackend* web_data_backend)
73    : web_data_backend_(web_data_backend),
74      scoped_observer_(this) {
75  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
76  DCHECK(web_data_backend_);
77
78  scoped_observer_.Add(web_data_backend_);
79}
80
81AutocompleteSyncableService::~AutocompleteSyncableService() {
82  DCHECK(CalledOnValidThread());
83}
84
85// static
86void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
87    AutofillWebDataService* web_data_service,
88    AutofillWebDataBackend* web_data_backend) {
89  web_data_service->GetDBUserData()->SetUserData(
90      UserDataKey(), new AutocompleteSyncableService(web_data_backend));
91}
92
93// static
94AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
95    AutofillWebDataService* web_data_service) {
96  return static_cast<AutocompleteSyncableService*>(
97      web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
98}
99
100AutocompleteSyncableService::AutocompleteSyncableService()
101    : web_data_backend_(NULL),
102      scoped_observer_(this) {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
104}
105
106void AutocompleteSyncableService::InjectStartSyncFlare(
107    const syncer::SyncableService::StartSyncFlare& flare) {
108  flare_ = flare;
109}
110
111syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
112    syncer::ModelType type,
113    const syncer::SyncDataList& initial_sync_data,
114    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
115    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
116  DCHECK(CalledOnValidThread());
117  DCHECK(!sync_processor_);
118  DCHECK(sync_processor);
119  DCHECK(error_handler);
120
121  syncer::SyncMergeResult merge_result(type);
122  error_handler_ = error_handler.Pass();
123  std::vector<AutofillEntry> entries;
124  if (!LoadAutofillData(&entries)) {
125    merge_result.set_error(error_handler_->CreateAndUploadError(
126        FROM_HERE,
127        "Could not load autocomplete data from the WebDatabase."));
128    return merge_result;
129  }
130
131  AutocompleteEntryMap new_db_entries;
132  for (std::vector<AutofillEntry>::iterator it = entries.begin();
133       it != entries.end(); ++it) {
134    new_db_entries[it->key()] =
135        std::make_pair(syncer::SyncChange::ACTION_ADD, it);
136  }
137
138  sync_processor_ = sync_processor.Pass();
139
140  std::vector<AutofillEntry> new_synced_entries;
141  // Go through and check for all the entries that sync already knows about.
142  // CreateOrUpdateEntry() will remove entries that are same with the synced
143  // ones from |new_db_entries|.
144  for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
145       it != initial_sync_data.end(); ++it) {
146    CreateOrUpdateEntry(*it, &new_db_entries, &new_synced_entries);
147  }
148
149  if (!SaveChangesToWebData(new_synced_entries)) {
150    merge_result.set_error(error_handler_->CreateAndUploadError(
151        FROM_HERE,
152        "Failed to update webdata."));
153    return merge_result;
154  }
155
156  syncer::SyncChangeList new_changes;
157  for (AutocompleteEntryMap::iterator it = new_db_entries.begin();
158       it != new_db_entries.end(); ++it) {
159    new_changes.push_back(
160        syncer::SyncChange(FROM_HERE,
161                           it->second.first,
162                           CreateSyncData(*(it->second.second))));
163  }
164
165  merge_result.set_error(
166      sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
167
168  // This will schedule a deletion operation on the DB thread, which will
169  // trigger a notification to propagate the deletion to Sync.
170  // NOTE: This must be called *after* the ProcessSyncChanges call above.
171  // Otherwise, an item that Sync is not yet aware of might expire, causing a
172  // Sync error when that item's deletion is propagated to Sync.
173  web_data_backend_->RemoveExpiredFormElements();
174
175  return merge_result;
176}
177
178void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
179  DCHECK(CalledOnValidThread());
180  DCHECK_EQ(syncer::AUTOFILL, type);
181
182  sync_processor_.reset();
183  error_handler_.reset();
184}
185
186syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
187    syncer::ModelType type) const {
188  DCHECK(CalledOnValidThread());
189  DCHECK(sync_processor_);
190  DCHECK_EQ(type, syncer::AUTOFILL);
191
192  syncer::SyncDataList current_data;
193
194  std::vector<AutofillEntry> entries;
195  if (!LoadAutofillData(&entries))
196    return current_data;
197
198  for (std::vector<AutofillEntry>::iterator it = entries.begin();
199       it != entries.end(); ++it) {
200    current_data.push_back(CreateSyncData(*it));
201  }
202
203  return current_data;
204}
205
206syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
207    const tracked_objects::Location& from_here,
208    const syncer::SyncChangeList& change_list) {
209  DCHECK(CalledOnValidThread());
210  DCHECK(sync_processor_);
211
212  if (!sync_processor_) {
213    syncer::SyncError error(FROM_HERE,
214                            syncer::SyncError::DATATYPE_ERROR,
215                            "Models not yet associated.",
216                            syncer::AUTOFILL);
217    return error;
218  }
219
220  // Data is loaded only if we get new ADD/UPDATE change.
221  std::vector<AutofillEntry> entries;
222  scoped_ptr<AutocompleteEntryMap> db_entries;
223  std::vector<AutofillEntry> new_entries;
224
225  syncer::SyncError list_processing_error;
226
227  for (syncer::SyncChangeList::const_iterator i = change_list.begin();
228       i != change_list.end() && !list_processing_error.IsSet(); ++i) {
229    DCHECK(i->IsValid());
230    switch (i->change_type()) {
231      case syncer::SyncChange::ACTION_ADD:
232      case syncer::SyncChange::ACTION_UPDATE:
233        if (!db_entries) {
234          if (!LoadAutofillData(&entries)) {
235            return error_handler_->CreateAndUploadError(
236                FROM_HERE,
237                "Could not get the autocomplete data from WebDatabase.");
238          }
239          db_entries.reset(new AutocompleteEntryMap);
240          for (std::vector<AutofillEntry>::iterator it = entries.begin();
241               it != entries.end(); ++it) {
242            (*db_entries)[it->key()] =
243                std::make_pair(syncer::SyncChange::ACTION_ADD, it);
244          }
245        }
246        CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
247        break;
248
249      case syncer::SyncChange::ACTION_DELETE: {
250        DCHECK(i->sync_data().GetSpecifics().has_autofill())
251            << "Autofill specifics data not present on delete!";
252        const sync_pb::AutofillSpecifics& autofill =
253            i->sync_data().GetSpecifics().autofill();
254        if (autofill.has_value())
255          list_processing_error = AutofillEntryDelete(autofill);
256        else
257          DVLOG(1) << "Delete for old-style autofill profile being dropped!";
258        break;
259      }
260
261      case syncer::SyncChange::ACTION_INVALID:
262        NOTREACHED();
263        return error_handler_->CreateAndUploadError(
264            FROM_HERE,
265            "ProcessSyncChanges failed on ChangeType " +
266                 syncer::SyncChange::ChangeTypeToString(i->change_type()));
267    }
268  }
269
270  if (!SaveChangesToWebData(new_entries)) {
271    return error_handler_->CreateAndUploadError(
272        FROM_HERE,
273        "Failed to update webdata.");
274  }
275
276  // This will schedule a deletion operation on the DB thread, which will
277  // trigger a notification to propagate the deletion to Sync.
278  web_data_backend_->RemoveExpiredFormElements();
279
280  return list_processing_error;
281}
282
283void AutocompleteSyncableService::AutofillEntriesChanged(
284    const AutofillChangeList& changes) {
285  // Check if sync is on. If we receive this notification prior to sync being
286  // started, we'll notify sync to start as soon as it can and later process all
287  // entries when MergeDataAndStartSyncing() is called. If we receive this
288  // notification after sync has exited, it will be synced the next time Chrome
289  // starts.
290  if (sync_processor_) {
291    ActOnChanges(changes);
292  } else if (!flare_.is_null()) {
293    flare_.Run(syncer::AUTOFILL);
294    flare_.Reset();
295  }
296}
297
298bool AutocompleteSyncableService::LoadAutofillData(
299    std::vector<AutofillEntry>* entries) const {
300  return GetAutofillTable()->GetAllAutofillEntries(entries);
301}
302
303bool AutocompleteSyncableService::SaveChangesToWebData(
304    const std::vector<AutofillEntry>& new_entries) {
305  DCHECK(CalledOnValidThread());
306  if (!GetAutofillTable()->UpdateAutofillEntries(new_entries))
307    return false;
308
309  web_data_backend_->NotifyOfMultipleAutofillChanges();
310  return true;
311}
312
313// Creates or updates an autocomplete entry based on |data|.
314void AutocompleteSyncableService::CreateOrUpdateEntry(
315    const syncer::SyncData& data,
316    AutocompleteEntryMap* loaded_data,
317    std::vector<AutofillEntry>* new_entries) {
318  const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
319  const sync_pb::AutofillSpecifics& autofill_specifics(specifics.autofill());
320
321  if (!autofill_specifics.has_value()) {
322    DVLOG(1) << "Add/Update for old-style autofill profile being dropped!";
323    return;
324  }
325
326  AutofillKey key(autofill_specifics.name().c_str(),
327                  autofill_specifics.value().c_str());
328  AutocompleteEntryMap::iterator it = loaded_data->find(key);
329  const google::protobuf::RepeatedField<int64_t>& timestamps =
330      autofill_specifics.usage_timestamp();
331  if (it == loaded_data->end()) {
332    // New entry.
333    base::Time date_created, date_last_used;
334    if (timestamps.size() > 0) {
335      date_created = base::Time::FromInternalValue(*timestamps.begin());
336      date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
337    }
338    new_entries->push_back(AutofillEntry(key, date_created, date_last_used));
339  } else {
340    // Entry already present - merge if necessary.
341    base::Time date_created, date_last_used;
342    bool different = MergeTimestamps(timestamps, *it->second.second,
343                                     &date_created, &date_last_used);
344    if (different) {
345      AutofillEntry new_entry(
346          it->second.second->key(), date_created, date_last_used);
347      new_entries->push_back(new_entry);
348      // Update the sync db since the timestamps have changed.
349      *(it->second.second) = new_entry;
350      it->second.first = syncer::SyncChange::ACTION_UPDATE;
351    } else {
352      loaded_data->erase(it);
353    }
354  }
355}
356
357// static
358void AutocompleteSyncableService::WriteAutofillEntry(
359    const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
360  sync_pb::AutofillSpecifics* autofill =
361      autofill_specifics->mutable_autofill();
362  autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
363  autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
364  autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
365  if (entry.date_created() != entry.date_last_used())
366    autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
367}
368
369syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
370    const sync_pb::AutofillSpecifics& autofill) {
371  if (!GetAutofillTable()->RemoveFormElement(
372          base::UTF8ToUTF16(autofill.name()),
373          base::UTF8ToUTF16(autofill.value()))) {
374    return error_handler_->CreateAndUploadError(
375        FROM_HERE,
376        "Could not remove autocomplete entry from WebDatabase.");
377  }
378  return syncer::SyncError();
379}
380
381void AutocompleteSyncableService::ActOnChanges(
382     const AutofillChangeList& changes) {
383  DCHECK(sync_processor_);
384  syncer::SyncChangeList new_changes;
385  for (AutofillChangeList::const_iterator change = changes.begin();
386       change != changes.end(); ++change) {
387    switch (change->type()) {
388      case AutofillChange::ADD:
389      case AutofillChange::UPDATE: {
390        base::Time date_created, date_last_used;
391        bool success = GetAutofillTable()->GetAutofillTimestamps(
392            change->key().name(), change->key().value(),
393            &date_created, &date_last_used);
394        DCHECK(success);
395        AutofillEntry entry(change->key(), date_created, date_last_used);
396        syncer::SyncChange::SyncChangeType change_type =
397           (change->type() == AutofillChange::ADD) ?
398            syncer::SyncChange::ACTION_ADD :
399            syncer::SyncChange::ACTION_UPDATE;
400        new_changes.push_back(syncer::SyncChange(FROM_HERE,
401                                                 change_type,
402                                                 CreateSyncData(entry)));
403        break;
404      }
405
406      case AutofillChange::REMOVE: {
407        AutofillEntry entry(change->key(), base::Time(), base::Time());
408        new_changes.push_back(
409            syncer::SyncChange(FROM_HERE,
410                               syncer::SyncChange::ACTION_DELETE,
411                               CreateSyncData(entry)));
412        break;
413      }
414    }
415  }
416  syncer::SyncError error =
417      sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
418  if (error.IsSet()) {
419    DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change.  Error: "
420             << error.message();
421  }
422}
423
424syncer::SyncData AutocompleteSyncableService::CreateSyncData(
425    const AutofillEntry& entry) const {
426  sync_pb::EntitySpecifics autofill_specifics;
427  WriteAutofillEntry(entry, &autofill_specifics);
428  std::string tag(KeyToTag(base::UTF16ToUTF8(entry.key().name()),
429                           base::UTF16ToUTF8(entry.key().value())));
430  return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
431}
432
433AutofillTable* AutocompleteSyncableService::GetAutofillTable() const {
434  return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
435}
436
437// static
438std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
439                                                  const std::string& value) {
440  std::string prefix(kAutofillEntryNamespaceTag);
441  return prefix + net::EscapePath(name) + "|" + net::EscapePath(value);
442}
443