Home | History | Annotate | Download | only in webdata
      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 
     20 using autofill::AutofillChange;
     21 using autofill::AutofillChangeList;
     22 using autofill::AutofillEntry;
     23 using autofill::AutofillKey;
     24 using autofill::AutofillTable;
     25 using autofill::AutofillWebDataService;
     26 using autofill::AutofillWebDataBackend;
     27 using content::BrowserThread;
     28 
     29 namespace {
     30 
     31 const 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.
     37 bool 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 
     62 void* 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 
     71 AutocompleteSyncableService::AutocompleteSyncableService(
     72     AutofillWebDataBackend* webdata_backend)
     73     : webdata_backend_(webdata_backend),
     74       scoped_observer_(this) {
     75   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
     76   DCHECK(webdata_backend_);
     77 
     78   scoped_observer_.Add(webdata_backend_);
     79 }
     80 
     81 AutocompleteSyncableService::~AutocompleteSyncableService() {
     82   DCHECK(CalledOnValidThread());
     83 }
     84 
     85 // static
     86 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
     87     AutofillWebDataService* web_data_service,
     88     AutofillWebDataBackend* webdata_backend) {
     89   web_data_service->GetDBUserData()->SetUserData(
     90       UserDataKey(), new AutocompleteSyncableService(webdata_backend));
     91 }
     92 
     93 // static
     94 AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
     95     AutofillWebDataService* web_data_service) {
     96   return static_cast<AutocompleteSyncableService*>(
     97       web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
     98 }
     99 
    100 AutocompleteSyncableService::AutocompleteSyncableService()
    101     : webdata_backend_(NULL),
    102       scoped_observer_(this) {
    103   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    104 }
    105 
    106 void AutocompleteSyncableService::InjectStartSyncFlare(
    107     const syncer::SyncableService::StartSyncFlare& flare) {
    108   flare_ = flare;
    109 }
    110 
    111 syncer::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   webdata_backend_->NotifyOfMultipleAutofillChanges();
    157 
    158   syncer::SyncChangeList new_changes;
    159   for (AutocompleteEntryMap::iterator it = new_db_entries.begin();
    160        it != new_db_entries.end(); ++it) {
    161     new_changes.push_back(
    162         syncer::SyncChange(FROM_HERE,
    163                            it->second.first,
    164                            CreateSyncData(*(it->second.second))));
    165   }
    166 
    167   merge_result.set_error(
    168       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
    169 
    170   // This will schedule a deletion operation on the DB thread, which will
    171   // trigger a notification to propagate the deletion to Sync.
    172   // NOTE: This must be called *after* the ProcessSyncChanges call above.
    173   // Otherwise, an item that Sync is not yet aware of might expire, causing a
    174   // Sync error when that item's deletion is propagated to Sync.
    175   webdata_backend_->RemoveExpiredFormElements();
    176 
    177   return merge_result;
    178 }
    179 
    180 void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
    181   DCHECK(CalledOnValidThread());
    182   DCHECK_EQ(syncer::AUTOFILL, type);
    183 
    184   sync_processor_.reset(NULL);
    185   error_handler_.reset();
    186 }
    187 
    188 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
    189     syncer::ModelType type) const {
    190   DCHECK(CalledOnValidThread());
    191   DCHECK(sync_processor_);
    192   DCHECK_EQ(type, syncer::AUTOFILL);
    193 
    194   syncer::SyncDataList current_data;
    195 
    196   std::vector<AutofillEntry> entries;
    197   if (!LoadAutofillData(&entries))
    198     return current_data;
    199 
    200   for (std::vector<AutofillEntry>::iterator it = entries.begin();
    201        it != entries.end(); ++it) {
    202     current_data.push_back(CreateSyncData(*it));
    203   }
    204 
    205   return current_data;
    206 }
    207 
    208 syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
    209     const tracked_objects::Location& from_here,
    210     const syncer::SyncChangeList& change_list) {
    211   DCHECK(CalledOnValidThread());
    212   DCHECK(sync_processor_);
    213 
    214   if (!sync_processor_) {
    215     syncer::SyncError error(FROM_HERE,
    216                             syncer::SyncError::DATATYPE_ERROR,
    217                             "Models not yet associated.",
    218                             syncer::AUTOFILL);
    219     return error;
    220   }
    221 
    222   // Data is loaded only if we get new ADD/UPDATE change.
    223   std::vector<AutofillEntry> entries;
    224   scoped_ptr<AutocompleteEntryMap> db_entries;
    225   std::vector<AutofillEntry> new_entries;
    226 
    227   syncer::SyncError list_processing_error;
    228 
    229   for (syncer::SyncChangeList::const_iterator i = change_list.begin();
    230        i != change_list.end() && !list_processing_error.IsSet(); ++i) {
    231     DCHECK(i->IsValid());
    232     switch (i->change_type()) {
    233       case syncer::SyncChange::ACTION_ADD:
    234       case syncer::SyncChange::ACTION_UPDATE:
    235         if (!db_entries) {
    236           if (!LoadAutofillData(&entries)) {
    237             return error_handler_->CreateAndUploadError(
    238                 FROM_HERE,
    239                 "Could not get the autocomplete data from WebDatabase.");
    240           }
    241           db_entries.reset(new AutocompleteEntryMap);
    242           for (std::vector<AutofillEntry>::iterator it = entries.begin();
    243                it != entries.end(); ++it) {
    244             (*db_entries)[it->key()] =
    245                 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
    246           }
    247         }
    248         CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
    249         break;
    250 
    251       case syncer::SyncChange::ACTION_DELETE: {
    252         DCHECK(i->sync_data().GetSpecifics().has_autofill())
    253             << "Autofill specifics data not present on delete!";
    254         const sync_pb::AutofillSpecifics& autofill =
    255             i->sync_data().GetSpecifics().autofill();
    256         if (autofill.has_value())
    257           list_processing_error = AutofillEntryDelete(autofill);
    258         else
    259           VLOG(1) << "Delete for old-style autofill profile being dropped!";
    260         break;
    261       }
    262 
    263       default:
    264         NOTREACHED();
    265         return error_handler_->CreateAndUploadError(
    266             FROM_HERE,
    267             "ProcessSyncChanges failed on ChangeType " +
    268                  syncer::SyncChange::ChangeTypeToString(i->change_type()));
    269     }
    270   }
    271 
    272   if (!SaveChangesToWebData(new_entries)) {
    273     return error_handler_->CreateAndUploadError(
    274         FROM_HERE,
    275         "Failed to update webdata.");
    276   }
    277 
    278   webdata_backend_->NotifyOfMultipleAutofillChanges();
    279 
    280   // This will schedule a deletion operation on the DB thread, which will
    281   // trigger a notification to propagate the deletion to Sync.
    282   webdata_backend_->RemoveExpiredFormElements();
    283 
    284   return list_processing_error;
    285 }
    286 
    287 void AutocompleteSyncableService::AutofillEntriesChanged(
    288     const AutofillChangeList& changes) {
    289   // Check if sync is on. If we receive this notification prior to sync being
    290   // started, we'll notify sync to start as soon as it can and later process
    291   // all entries when MergeData..() is called. If we receive this notification
    292   // sync has exited, it will be synced next time Chrome starts.
    293   if (sync_processor_) {
    294     ActOnChanges(changes);
    295   } else if (!flare_.is_null()) {
    296     flare_.Run(syncer::AUTOFILL);
    297     flare_.Reset();
    298   }
    299 }
    300 
    301 bool AutocompleteSyncableService::LoadAutofillData(
    302     std::vector<AutofillEntry>* entries) const {
    303   return AutofillTable::FromWebDatabase(
    304       webdata_backend_->GetDatabase())->GetAllAutofillEntries(entries);
    305 }
    306 
    307 bool AutocompleteSyncableService::SaveChangesToWebData(
    308     const std::vector<AutofillEntry>& new_entries) {
    309   DCHECK(CalledOnValidThread());
    310 
    311   if (!new_entries.empty() &&
    312       !AutofillTable::FromWebDatabase(
    313           webdata_backend_->GetDatabase())->UpdateAutofillEntries(
    314               new_entries)) {
    315     return false;
    316   }
    317   return true;
    318 }
    319 
    320 // Creates or updates an autocomplete entry based on |data|.
    321 void AutocompleteSyncableService::CreateOrUpdateEntry(
    322     const syncer::SyncData& data,
    323     AutocompleteEntryMap* loaded_data,
    324     std::vector<AutofillEntry>* new_entries) {
    325   const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
    326   const sync_pb::AutofillSpecifics& autofill_specifics(specifics.autofill());
    327 
    328   if (!autofill_specifics.has_value()) {
    329     VLOG(1) << "Add/Update for old-style autofill profile being dropped!";
    330     return;
    331   }
    332 
    333   AutofillKey key(autofill_specifics.name().c_str(),
    334                   autofill_specifics.value().c_str());
    335   AutocompleteEntryMap::iterator it = loaded_data->find(key);
    336   const google::protobuf::RepeatedField<int64_t>& timestamps =
    337       autofill_specifics.usage_timestamp();
    338   if (it == loaded_data->end()) {
    339     // New entry.
    340     base::Time date_created, date_last_used;
    341     if (timestamps.size() > 0) {
    342       date_created = base::Time::FromInternalValue(*timestamps.begin());
    343       date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
    344     }
    345     new_entries->push_back(AutofillEntry(key, date_created, date_last_used));
    346   } else {
    347     // Entry already present - merge if necessary.
    348     base::Time date_created, date_last_used;
    349     bool different = MergeTimestamps(timestamps, *it->second.second,
    350                                      &date_created, &date_last_used);
    351     if (different) {
    352       AutofillEntry new_entry(
    353           it->second.second->key(), date_created, date_last_used);
    354       new_entries->push_back(new_entry);
    355       // Update the sync db since the timestamps have changed.
    356       *(it->second.second) = new_entry;
    357       it->second.first = syncer::SyncChange::ACTION_UPDATE;
    358     } else {
    359       loaded_data->erase(it);
    360     }
    361   }
    362 }
    363 
    364 // static
    365 void AutocompleteSyncableService::WriteAutofillEntry(
    366     const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
    367   sync_pb::AutofillSpecifics* autofill =
    368       autofill_specifics->mutable_autofill();
    369   autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
    370   autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
    371   autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
    372   if (entry.date_created() != entry.date_last_used())
    373     autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
    374 }
    375 
    376 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
    377     const sync_pb::AutofillSpecifics& autofill) {
    378   if (!AutofillTable::FromWebDatabase(
    379           webdata_backend_->GetDatabase())->RemoveFormElement(
    380               base::UTF8ToUTF16(autofill.name()),
    381               base::UTF8ToUTF16(autofill.value()))) {
    382     return error_handler_->CreateAndUploadError(
    383         FROM_HERE,
    384         "Could not remove autocomplete entry from WebDatabase.");
    385   }
    386   return syncer::SyncError();
    387 }
    388 
    389 void AutocompleteSyncableService::ActOnChanges(
    390      const AutofillChangeList& changes) {
    391   DCHECK(sync_processor_);
    392   syncer::SyncChangeList new_changes;
    393   for (AutofillChangeList::const_iterator change = changes.begin();
    394        change != changes.end(); ++change) {
    395     switch (change->type()) {
    396       case AutofillChange::ADD:
    397       case AutofillChange::UPDATE: {
    398         base::Time date_created, date_last_used;
    399         WebDatabase* db = webdata_backend_->GetDatabase();
    400         if (!AutofillTable::FromWebDatabase(db)->GetAutofillTimestamps(
    401                 change->key().name(), change->key().value(),
    402                 &date_created, &date_last_used)) {
    403           NOTREACHED();
    404           return;
    405         }
    406         AutofillEntry entry(change->key(), date_created, date_last_used);
    407         syncer::SyncChange::SyncChangeType change_type =
    408            (change->type() == AutofillChange::ADD) ?
    409             syncer::SyncChange::ACTION_ADD :
    410             syncer::SyncChange::ACTION_UPDATE;
    411         new_changes.push_back(syncer::SyncChange(FROM_HERE,
    412                                                  change_type,
    413                                                  CreateSyncData(entry)));
    414         break;
    415       }
    416 
    417       case AutofillChange::REMOVE: {
    418         AutofillEntry entry(change->key(), base::Time(), base::Time());
    419         new_changes.push_back(
    420             syncer::SyncChange(FROM_HERE,
    421                                syncer::SyncChange::ACTION_DELETE,
    422                                CreateSyncData(entry)));
    423         break;
    424       }
    425 
    426       default:
    427         NOTREACHED();
    428     }
    429   }
    430   syncer::SyncError error =
    431       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
    432   if (error.IsSet()) {
    433     VLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change.  Error: "
    434             << error.message();
    435   }
    436 }
    437 
    438 syncer::SyncData AutocompleteSyncableService::CreateSyncData(
    439     const AutofillEntry& entry) const {
    440   sync_pb::EntitySpecifics autofill_specifics;
    441   WriteAutofillEntry(entry, &autofill_specifics);
    442   std::string tag(KeyToTag(base::UTF16ToUTF8(entry.key().name()),
    443                            base::UTF16ToUTF8(entry.key().value())));
    444   return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
    445 }
    446 
    447 // static
    448 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
    449                                                   const std::string& value) {
    450   std::string prefix(kAutofillEntryNamespaceTag);
    451   return prefix + net::EscapePath(name) + "|" + net::EscapePath(value);
    452 }
    453