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 |autofill| entry and |timestamps|. Returns
     34 // true if they were different, false if they were the same.
     35 // All of the timestamp vectors are assummed to be sorted, resulting vector is
     36 // sorted as well. Only two timestamps - the earliest and the latest are stored.
     37 bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill,
     38                      const std::vector<base::Time>& timestamps,
     39                      std::vector<base::Time>* new_timestamps) {
     40   DCHECK(new_timestamps);
     41 
     42   new_timestamps->clear();
     43   size_t timestamps_count = autofill.usage_timestamp_size();
     44   if (timestamps_count == 0 && timestamps.empty()) {
     45     return false;
     46   } else if (timestamps_count == 0) {
     47     new_timestamps->insert(new_timestamps->begin(),
     48                            timestamps.begin(),
     49                            timestamps.end());
     50     return true;
     51   } else if (timestamps.empty()) {
     52     new_timestamps->reserve(2);
     53     new_timestamps->push_back(base::Time::FromInternalValue(
     54         autofill.usage_timestamp(0)));
     55     if (timestamps_count > 1) {
     56       new_timestamps->push_back(base::Time::FromInternalValue(
     57           autofill.usage_timestamp(timestamps_count - 1)));
     58     }
     59     return true;
     60   } else {
     61     base::Time sync_time_begin = base::Time::FromInternalValue(
     62         autofill.usage_timestamp(0));
     63     base::Time sync_time_end = base::Time::FromInternalValue(
     64         autofill.usage_timestamp(timestamps_count - 1));
     65     if (timestamps.front() != sync_time_begin ||
     66         timestamps.back() != sync_time_end) {
     67       new_timestamps->push_back(
     68           timestamps.front() < sync_time_begin ? timestamps.front() :
     69                                                  sync_time_begin);
     70       if (new_timestamps->back() != timestamps.back() ||
     71           new_timestamps->back() != sync_time_end) {
     72         new_timestamps->push_back(
     73             timestamps.back() > sync_time_end ? timestamps.back() :
     74                                                 sync_time_end);
     75       }
     76       return true;
     77     } else {
     78       new_timestamps->insert(new_timestamps->begin(),
     79                              timestamps.begin(),
     80                              timestamps.end());
     81       return false;
     82     }
     83   }
     84 }
     85 
     86 void* UserDataKey() {
     87   // Use the address of a static that COMDAT folding won't ever fold
     88   // with something else.
     89   static int user_data_key = 0;
     90   return reinterpret_cast<void*>(&user_data_key);
     91 }
     92 
     93 }  // namespace
     94 
     95 AutocompleteSyncableService::AutocompleteSyncableService(
     96     AutofillWebDataBackend* webdata_backend)
     97     : webdata_backend_(webdata_backend),
     98       scoped_observer_(this),
     99       cull_expired_entries_(false) {
    100   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    101   DCHECK(webdata_backend_);
    102 
    103   scoped_observer_.Add(webdata_backend_);
    104 }
    105 
    106 AutocompleteSyncableService::~AutocompleteSyncableService() {
    107   DCHECK(CalledOnValidThread());
    108 }
    109 
    110 // static
    111 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
    112     AutofillWebDataService* web_data_service,
    113     AutofillWebDataBackend* webdata_backend) {
    114   web_data_service->GetDBUserData()->SetUserData(
    115       UserDataKey(), new AutocompleteSyncableService(webdata_backend));
    116 }
    117 
    118 // static
    119 AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
    120     AutofillWebDataService* web_data_service) {
    121   return static_cast<AutocompleteSyncableService*>(
    122       web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
    123 }
    124 
    125 AutocompleteSyncableService::AutocompleteSyncableService()
    126     : webdata_backend_(NULL),
    127       scoped_observer_(this),
    128       cull_expired_entries_(false) {
    129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    130 }
    131 
    132 void AutocompleteSyncableService::InjectStartSyncFlare(
    133     const syncer::SyncableService::StartSyncFlare& flare) {
    134   flare_ = flare;
    135 }
    136 
    137 syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
    138     syncer::ModelType type,
    139     const syncer::SyncDataList& initial_sync_data,
    140     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
    141     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
    142   DCHECK(CalledOnValidThread());
    143   DCHECK(!sync_processor_.get());
    144   DCHECK(sync_processor.get());
    145   DCHECK(error_handler.get());
    146   VLOG(1) << "Associating Autocomplete: MergeDataAndStartSyncing";
    147 
    148   syncer::SyncMergeResult merge_result(type);
    149   error_handler_ = error_handler.Pass();
    150   std::vector<AutofillEntry> entries;
    151   if (!LoadAutofillData(&entries)) {
    152     merge_result.set_error(error_handler_->CreateAndUploadError(
    153         FROM_HERE,
    154         "Could not get the autocomplete data from WebDatabase."));
    155     return merge_result;
    156   }
    157 
    158   AutocompleteEntryMap new_db_entries;
    159   for (std::vector<AutofillEntry>::iterator it = entries.begin();
    160        it != entries.end(); ++it) {
    161     new_db_entries[it->key()] =
    162         std::make_pair(syncer::SyncChange::ACTION_ADD, it);
    163   }
    164 
    165   sync_processor_ = sync_processor.Pass();
    166 
    167   std::vector<AutofillEntry> new_synced_entries;
    168   // Go through and check for all the entries that sync already knows about.
    169   // CreateOrUpdateEntry() will remove entries that are same with the synced
    170   // ones from |new_db_entries|.
    171   for (syncer::SyncDataList::const_iterator sync_iter =
    172            initial_sync_data.begin();
    173        sync_iter != initial_sync_data.end(); ++sync_iter) {
    174     CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries);
    175   }
    176 
    177   if (!SaveChangesToWebData(new_synced_entries)) {
    178     merge_result.set_error(error_handler_->CreateAndUploadError(
    179         FROM_HERE,
    180         "Failed to update webdata."));
    181     return merge_result;
    182   }
    183 
    184   webdata_backend_->NotifyOfMultipleAutofillChanges();
    185 
    186   syncer::SyncChangeList new_changes;
    187   for (AutocompleteEntryMap::iterator i = new_db_entries.begin();
    188        i != new_db_entries.end(); ++i) {
    189     new_changes.push_back(
    190         syncer::SyncChange(FROM_HERE,
    191                            i->second.first,
    192                            CreateSyncData(*(i->second.second))));
    193   }
    194 
    195   if (cull_expired_entries_) {
    196     // This will schedule a deletion operation on the DB thread, which will
    197     // trigger a notification to propagate the deletion to Sync.
    198     webdata_backend_->RemoveExpiredFormElements();
    199   }
    200 
    201   merge_result.set_error(
    202       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
    203   return merge_result;
    204 }
    205 
    206 void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
    207   DCHECK(CalledOnValidThread());
    208   DCHECK_EQ(syncer::AUTOFILL, type);
    209 
    210   sync_processor_.reset(NULL);
    211   error_handler_.reset();
    212 }
    213 
    214 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
    215     syncer::ModelType type) const {
    216   DCHECK(CalledOnValidThread());
    217   DCHECK(sync_processor_.get());
    218   DCHECK_EQ(type, syncer::AUTOFILL);
    219 
    220   syncer::SyncDataList current_data;
    221 
    222   std::vector<AutofillEntry> entries;
    223   if (!LoadAutofillData(&entries))
    224     return current_data;
    225 
    226   for (std::vector<AutofillEntry>::iterator it = entries.begin();
    227        it != entries.end(); ++it) {
    228     current_data.push_back(CreateSyncData(*it));
    229   }
    230 
    231   return current_data;
    232 }
    233 
    234 syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
    235     const tracked_objects::Location& from_here,
    236     const syncer::SyncChangeList& change_list) {
    237   DCHECK(CalledOnValidThread());
    238   DCHECK(sync_processor_.get());
    239 
    240   if (!sync_processor_.get()) {
    241     syncer::SyncError error(FROM_HERE,
    242                             syncer::SyncError::DATATYPE_ERROR,
    243                             "Models not yet associated.",
    244                             syncer::AUTOFILL);
    245     return error;
    246   }
    247 
    248   // Data is loaded only if we get new ADD/UPDATE change.
    249   std::vector<AutofillEntry> entries;
    250   scoped_ptr<AutocompleteEntryMap> db_entries;
    251   std::vector<AutofillEntry> new_entries;
    252 
    253   syncer::SyncError list_processing_error;
    254 
    255   for (syncer::SyncChangeList::const_iterator i = change_list.begin();
    256        i != change_list.end() && !list_processing_error.IsSet(); ++i) {
    257     DCHECK(i->IsValid());
    258     switch (i->change_type()) {
    259       case syncer::SyncChange::ACTION_ADD:
    260       case syncer::SyncChange::ACTION_UPDATE:
    261         if (!db_entries.get()) {
    262           if (!LoadAutofillData(&entries)) {
    263             return error_handler_->CreateAndUploadError(
    264                 FROM_HERE,
    265                 "Could not get the autocomplete data from WebDatabase.");
    266           }
    267           db_entries.reset(new AutocompleteEntryMap);
    268           for (std::vector<AutofillEntry>::iterator it = entries.begin();
    269                it != entries.end(); ++it) {
    270             (*db_entries)[it->key()] =
    271                 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
    272           }
    273         }
    274         CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
    275         break;
    276       case syncer::SyncChange::ACTION_DELETE: {
    277         DCHECK(i->sync_data().GetSpecifics().has_autofill())
    278             << "Autofill specifics data not present on delete!";
    279         const sync_pb::AutofillSpecifics& autofill =
    280             i->sync_data().GetSpecifics().autofill();
    281         if (autofill.has_value()) {
    282           list_processing_error = AutofillEntryDelete(autofill);
    283         } else {
    284           DLOG(WARNING)
    285               << "Delete for old-style autofill profile being dropped!";
    286         }
    287       } break;
    288       default:
    289         NOTREACHED() << "Unexpected sync change state.";
    290         return error_handler_->CreateAndUploadError(
    291             FROM_HERE,
    292             "ProcessSyncChanges failed on ChangeType " +
    293                  syncer::SyncChange::ChangeTypeToString(i->change_type()));
    294     }
    295   }
    296 
    297   if (!SaveChangesToWebData(new_entries)) {
    298     return error_handler_->CreateAndUploadError(
    299         FROM_HERE,
    300         "Failed to update webdata.");
    301   }
    302 
    303   webdata_backend_->NotifyOfMultipleAutofillChanges();
    304 
    305   if (cull_expired_entries_) {
    306     // This will schedule a deletion operation on the DB thread, which will
    307     // trigger a notification to propagate the deletion to Sync.
    308     webdata_backend_->RemoveExpiredFormElements();
    309   }
    310 
    311   return list_processing_error;
    312 }
    313 
    314 void AutocompleteSyncableService::AutofillEntriesChanged(
    315     const AutofillChangeList& changes) {
    316   // Check if sync is on. If we recieve this notification prior to sync being
    317   // started, we'll notify sync to start as soon as it can and later process
    318   // all entries when MergeData..() is called. If we receive this notification
    319   // sync has exited, it will be synced next time Chrome starts.
    320   if (sync_processor_.get()) {
    321     ActOnChanges(changes);
    322   } else if (!flare_.is_null()) {
    323     flare_.Run(syncer::AUTOFILL);
    324     flare_.Reset();
    325   }
    326 }
    327 
    328 bool AutocompleteSyncableService::LoadAutofillData(
    329     std::vector<AutofillEntry>* entries) const {
    330   return AutofillTable::FromWebDatabase(
    331       webdata_backend_->GetDatabase())->GetAllAutofillEntries(entries);
    332 }
    333 
    334 bool AutocompleteSyncableService::SaveChangesToWebData(
    335     const std::vector<AutofillEntry>& new_entries) {
    336   DCHECK(CalledOnValidThread());
    337 
    338   if (!new_entries.empty() &&
    339       !AutofillTable::FromWebDatabase(
    340           webdata_backend_->GetDatabase())->UpdateAutofillEntries(
    341               new_entries)) {
    342     return false;
    343   }
    344   return true;
    345 }
    346 
    347 // Creates or updates an autocomplete entry based on |data|.
    348 void AutocompleteSyncableService::CreateOrUpdateEntry(
    349     const syncer::SyncData& data,
    350     AutocompleteEntryMap* loaded_data,
    351     std::vector<AutofillEntry>* new_entries) {
    352   const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
    353   const sync_pb::AutofillSpecifics& autofill_specifics(
    354       specifics.autofill());
    355 
    356   if (!autofill_specifics.has_value()) {
    357     DLOG(WARNING)
    358         << "Add/Update for old-style autofill profile being dropped!";
    359     return;
    360   }
    361 
    362   AutofillKey key(autofill_specifics.name().c_str(),
    363                   autofill_specifics.value().c_str());
    364   AutocompleteEntryMap::iterator it = loaded_data->find(key);
    365   if (it == loaded_data->end()) {
    366     // New entry.
    367     std::vector<base::Time> timestamps;
    368     size_t timestamps_count = autofill_specifics.usage_timestamp_size();
    369     timestamps.reserve(2);
    370     if (timestamps_count) {
    371       timestamps.push_back(base::Time::FromInternalValue(
    372           autofill_specifics.usage_timestamp(0)));
    373     }
    374     if (timestamps_count > 1) {
    375       timestamps.push_back(base::Time::FromInternalValue(
    376           autofill_specifics.usage_timestamp(timestamps_count - 1)));
    377     }
    378     AutofillEntry new_entry(key, timestamps);
    379     new_entries->push_back(new_entry);
    380   } else {
    381     // Entry already present - merge if necessary.
    382     std::vector<base::Time> timestamps;
    383     bool different = MergeTimestamps(
    384         autofill_specifics, it->second.second->timestamps(), &timestamps);
    385     if (different) {
    386       AutofillEntry new_entry(it->second.second->key(), timestamps);
    387       new_entries->push_back(new_entry);
    388       // Update the sync db if the list of timestamps have changed.
    389       *(it->second.second) = new_entry;
    390       it->second.first = syncer::SyncChange::ACTION_UPDATE;
    391     } else {
    392       loaded_data->erase(it);
    393     }
    394   }
    395 }
    396 
    397 // static
    398 void AutocompleteSyncableService::WriteAutofillEntry(
    399     const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
    400   sync_pb::AutofillSpecifics* autofill =
    401       autofill_specifics->mutable_autofill();
    402   autofill->set_name(UTF16ToUTF8(entry.key().name()));
    403   autofill->set_value(UTF16ToUTF8(entry.key().value()));
    404   const std::vector<base::Time>& ts(entry.timestamps());
    405   for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
    406        timestamp != ts.end(); ++timestamp) {
    407     autofill->add_usage_timestamp(timestamp->ToInternalValue());
    408   }
    409 }
    410 
    411 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
    412     const sync_pb::AutofillSpecifics& autofill) {
    413   if (!AutofillTable::FromWebDatabase(
    414           webdata_backend_->GetDatabase())->RemoveFormElement(
    415               UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
    416     return error_handler_->CreateAndUploadError(
    417         FROM_HERE,
    418         "Could not remove autocomplete entry from WebDatabase.");
    419   }
    420   return syncer::SyncError();
    421 }
    422 
    423 void AutocompleteSyncableService::ActOnChanges(
    424      const AutofillChangeList& changes) {
    425   DCHECK(sync_processor_.get());
    426   syncer::SyncChangeList new_changes;
    427   for (AutofillChangeList::const_iterator change = changes.begin();
    428        change != changes.end(); ++change) {
    429     switch (change->type()) {
    430       case AutofillChange::ADD:
    431       case AutofillChange::UPDATE: {
    432         std::vector<base::Time> timestamps;
    433         WebDatabase* db = webdata_backend_->GetDatabase();
    434         if (!AutofillTable::FromWebDatabase(db)->GetAutofillTimestamps(
    435                 change->key().name(),
    436                 change->key().value(),
    437                 &timestamps)) {
    438           NOTREACHED();
    439           return;
    440         }
    441         AutofillEntry entry(change->key(), timestamps);
    442         syncer::SyncChange::SyncChangeType change_type =
    443            (change->type() == AutofillChange::ADD) ?
    444             syncer::SyncChange::ACTION_ADD :
    445             syncer::SyncChange::ACTION_UPDATE;
    446         new_changes.push_back(syncer::SyncChange(FROM_HERE,
    447                                                  change_type,
    448                                                  CreateSyncData(entry)));
    449         break;
    450       }
    451       case AutofillChange::REMOVE: {
    452         std::vector<base::Time> timestamps;
    453         AutofillEntry entry(change->key(), timestamps);
    454         new_changes.push_back(
    455             syncer::SyncChange(FROM_HERE,
    456                                syncer::SyncChange::ACTION_DELETE,
    457                                CreateSyncData(entry)));
    458         break;
    459       }
    460       default:
    461         NOTREACHED();
    462         break;
    463     }
    464   }
    465   syncer::SyncError error =
    466       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
    467   if (error.IsSet()) {
    468     DLOG(WARNING) << "[AUTOCOMPLETE SYNC]"
    469                   << " Failed processing change:"
    470                   << " Error:" << error.message();
    471   }
    472 }
    473 
    474 void AutocompleteSyncableService::UpdateCullSetting(
    475     bool cull_expired_entries) {
    476   DCHECK(CalledOnValidThread());
    477   cull_expired_entries_ = cull_expired_entries;
    478 }
    479 
    480 syncer::SyncData AutocompleteSyncableService::CreateSyncData(
    481     const AutofillEntry& entry) const {
    482   sync_pb::EntitySpecifics autofill_specifics;
    483   WriteAutofillEntry(entry, &autofill_specifics);
    484   std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()),
    485                            UTF16ToUTF8(entry.key().value())));
    486   return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
    487 }
    488 
    489 // static
    490 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
    491                                                   const std::string& value) {
    492   std::string ns(kAutofillEntryNamespaceTag);
    493   return ns + net::EscapePath(name) + "|" + net::EscapePath(value);
    494 }
    495