Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / webdata / autocomplete_syncable_service.cc
index a57994a..b2b1953 100644 (file)
@@ -30,57 +30,33 @@ namespace {
 
 const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
 
-// Merges timestamps from the |autofill| entry and |timestamps|. Returns
-// true if they were different, false if they were the same.
-// All of the timestamp vectors are assummed to be sorted, resulting vector is
-// sorted as well. Only two timestamps - the earliest and the latest are stored.
-bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill,
-                     const std::vector<base::Time>& timestamps,
-                     std::vector<base::Time>* new_timestamps) {
-  DCHECK(new_timestamps);
-
-  new_timestamps->clear();
-  size_t timestamps_count = autofill.usage_timestamp_size();
-  if (timestamps_count == 0 && timestamps.empty()) {
-    return false;
-  } else if (timestamps_count == 0) {
-    new_timestamps->insert(new_timestamps->begin(),
-                           timestamps.begin(),
-                           timestamps.end());
-    return true;
-  } else if (timestamps.empty()) {
-    new_timestamps->reserve(2);
-    new_timestamps->push_back(base::Time::FromInternalValue(
-        autofill.usage_timestamp(0)));
-    if (timestamps_count > 1) {
-      new_timestamps->push_back(base::Time::FromInternalValue(
-          autofill.usage_timestamp(timestamps_count - 1)));
-    }
+// Merges timestamps from the |sync_timestamps| and the |local_entry|.
+// Returns true if they were different, false if they were the same.  If the
+// timestamps were different, fills |date_created| and |date_last_used| with the
+// merged timestamps.  The |sync_timestamps| vector is assumed to be sorted.
+bool MergeTimestamps(
+    const google::protobuf::RepeatedField<int64_t>& sync_timestamps,
+    const AutofillEntry& local_entry,
+    base::Time* date_created,
+    base::Time* date_last_used) {
+  if (sync_timestamps.size() == 0) {
+    *date_created = local_entry.date_created();
+    *date_last_used = local_entry.date_last_used();
     return true;
-  } else {
-    base::Time sync_time_begin = base::Time::FromInternalValue(
-        autofill.usage_timestamp(0));
-    base::Time sync_time_end = base::Time::FromInternalValue(
-        autofill.usage_timestamp(timestamps_count - 1));
-    if (timestamps.front() != sync_time_begin ||
-        timestamps.back() != sync_time_end) {
-      new_timestamps->push_back(
-          timestamps.front() < sync_time_begin ? timestamps.front() :
-                                                 sync_time_begin);
-      if (new_timestamps->back() != timestamps.back() ||
-          new_timestamps->back() != sync_time_end) {
-        new_timestamps->push_back(
-            timestamps.back() > sync_time_end ? timestamps.back() :
-                                                sync_time_end);
-      }
-      return true;
-    } else {
-      new_timestamps->insert(new_timestamps->begin(),
-                             timestamps.begin(),
-                             timestamps.end());
-      return false;
-    }
   }
+
+  base::Time sync_date_created =
+      base::Time::FromInternalValue(*sync_timestamps.begin());
+  base::Time sync_date_last_used =
+      base::Time::FromInternalValue(*sync_timestamps.rbegin());
+
+  if (sync_date_created == local_entry.date_created() &&
+      sync_date_last_used == local_entry.date_last_used())
+    return false;
+
+  *date_created = std::min(local_entry.date_created(), sync_date_created);
+  *date_last_used = std::max(local_entry.date_last_used(), sync_date_last_used);
+  return true;
 }
 
 void* UserDataKey() {
@@ -93,14 +69,13 @@ void* UserDataKey() {
 }  // namespace
 
 AutocompleteSyncableService::AutocompleteSyncableService(
-    AutofillWebDataBackend* webdata_backend)
-    : webdata_backend_(webdata_backend),
-      scoped_observer_(this),
-      cull_expired_entries_(false) {
+    AutofillWebDataBackend* web_data_backend)
+    : web_data_backend_(web_data_backend),
+      scoped_observer_(this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
-  DCHECK(webdata_backend_);
+  DCHECK(web_data_backend_);
 
-  scoped_observer_.Add(webdata_backend_);
+  scoped_observer_.Add(web_data_backend_);
 }
 
 AutocompleteSyncableService::~AutocompleteSyncableService() {
@@ -110,9 +85,9 @@ AutocompleteSyncableService::~AutocompleteSyncableService() {
 // static
 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
     AutofillWebDataService* web_data_service,
-    AutofillWebDataBackend* webdata_backend) {
+    AutofillWebDataBackend* web_data_backend) {
   web_data_service->GetDBUserData()->SetUserData(
-      UserDataKey(), new AutocompleteSyncableService(webdata_backend));
+      UserDataKey(), new AutocompleteSyncableService(web_data_backend));
 }
 
 // static
@@ -123,9 +98,8 @@ AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
 }
 
 AutocompleteSyncableService::AutocompleteSyncableService()
-    : webdata_backend_(NULL),
-      scoped_observer_(this),
-      cull_expired_entries_(false) {
+    : web_data_backend_(NULL),
+      scoped_observer_(this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
 }
 
@@ -140,10 +114,9 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
   DCHECK(CalledOnValidThread());
-  DCHECK(!sync_processor_.get());
-  DCHECK(sync_processor.get());
-  DCHECK(error_handler.get());
-  VLOG(1) << "Associating Autocomplete: MergeDataAndStartSyncing";
+  DCHECK(!sync_processor_);
+  DCHECK(sync_processor);
+  DCHECK(error_handler);
 
   syncer::SyncMergeResult merge_result(type);
   error_handler_ = error_handler.Pass();
@@ -151,7 +124,7 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
   if (!LoadAutofillData(&entries)) {
     merge_result.set_error(error_handler_->CreateAndUploadError(
         FROM_HERE,
-        "Could not get the autocomplete data from WebDatabase."));
+        "Could not load autocomplete data from the WebDatabase."));
     return merge_result;
   }
 
@@ -168,10 +141,9 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
   // Go through and check for all the entries that sync already knows about.
   // CreateOrUpdateEntry() will remove entries that are same with the synced
   // ones from |new_db_entries|.
-  for (syncer::SyncDataList::const_iterator sync_iter =
-           initial_sync_data.begin();
-       sync_iter != initial_sync_data.end(); ++sync_iter) {
-    CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries);
+  for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
+       it != initial_sync_data.end(); ++it) {
+    CreateOrUpdateEntry(*it, &new_db_entries, &new_synced_entries);
   }
 
   if (!SaveChangesToWebData(new_synced_entries)) {
@@ -181,25 +153,25 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
     return merge_result;
   }
 
-  webdata_backend_->NotifyOfMultipleAutofillChanges();
-
   syncer::SyncChangeList new_changes;
-  for (AutocompleteEntryMap::iterator i = new_db_entries.begin();
-       i != new_db_entries.end(); ++i) {
+  for (AutocompleteEntryMap::iterator it = new_db_entries.begin();
+       it != new_db_entries.end(); ++it) {
     new_changes.push_back(
         syncer::SyncChange(FROM_HERE,
-                           i->second.first,
-                           CreateSyncData(*(i->second.second))));
-  }
-
-  if (cull_expired_entries_) {
-    // This will schedule a deletion operation on the DB thread, which will
-    // trigger a notification to propagate the deletion to Sync.
-    webdata_backend_->RemoveExpiredFormElements();
+                           it->second.first,
+                           CreateSyncData(*(it->second.second))));
   }
 
   merge_result.set_error(
       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
+
+  // This will schedule a deletion operation on the DB thread, which will
+  // trigger a notification to propagate the deletion to Sync.
+  // NOTE: This must be called *after* the ProcessSyncChanges call above.
+  // Otherwise, an item that Sync is not yet aware of might expire, causing a
+  // Sync error when that item's deletion is propagated to Sync.
+  web_data_backend_->RemoveExpiredFormElements();
+
   return merge_result;
 }
 
@@ -207,14 +179,14 @@ void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
   DCHECK(CalledOnValidThread());
   DCHECK_EQ(syncer::AUTOFILL, type);
 
-  sync_processor_.reset(NULL);
+  sync_processor_.reset();
   error_handler_.reset();
 }
 
 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
     syncer::ModelType type) const {
   DCHECK(CalledOnValidThread());
-  DCHECK(sync_processor_.get());
+  DCHECK(sync_processor_);
   DCHECK_EQ(type, syncer::AUTOFILL);
 
   syncer::SyncDataList current_data;
@@ -235,9 +207,9 @@ syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
     const tracked_objects::Location& from_here,
     const syncer::SyncChangeList& change_list) {
   DCHECK(CalledOnValidThread());
-  DCHECK(sync_processor_.get());
+  DCHECK(sync_processor_);
 
-  if (!sync_processor_.get()) {
+  if (!sync_processor_) {
     syncer::SyncError error(FROM_HERE,
                             syncer::SyncError::DATATYPE_ERROR,
                             "Models not yet associated.",
@@ -258,7 +230,7 @@ syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
     switch (i->change_type()) {
       case syncer::SyncChange::ACTION_ADD:
       case syncer::SyncChange::ACTION_UPDATE:
-        if (!db_entries.get()) {
+        if (!db_entries) {
           if (!LoadAutofillData(&entries)) {
             return error_handler_->CreateAndUploadError(
                 FROM_HERE,
@@ -273,20 +245,21 @@ syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
         }
         CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
         break;
+
       case syncer::SyncChange::ACTION_DELETE: {
         DCHECK(i->sync_data().GetSpecifics().has_autofill())
             << "Autofill specifics data not present on delete!";
         const sync_pb::AutofillSpecifics& autofill =
             i->sync_data().GetSpecifics().autofill();
-        if (autofill.has_value()) {
+        if (autofill.has_value())
           list_processing_error = AutofillEntryDelete(autofill);
-        } else {
-          DLOG(WARNING)
-              << "Delete for old-style autofill profile being dropped!";
-        }
-      } break;
-      default:
-        NOTREACHED() << "Unexpected sync change state.";
+        else
+          DVLOG(1) << "Delete for old-style autofill profile being dropped!";
+        break;
+      }
+
+      case syncer::SyncChange::ACTION_INVALID:
+        NOTREACHED();
         return error_handler_->CreateAndUploadError(
             FROM_HERE,
             "ProcessSyncChanges failed on ChangeType " +
@@ -300,24 +273,21 @@ syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
         "Failed to update webdata.");
   }
 
-  webdata_backend_->NotifyOfMultipleAutofillChanges();
-
-  if (cull_expired_entries_) {
-    // This will schedule a deletion operation on the DB thread, which will
-    // trigger a notification to propagate the deletion to Sync.
-    webdata_backend_->RemoveExpiredFormElements();
-  }
+  // This will schedule a deletion operation on the DB thread, which will
+  // trigger a notification to propagate the deletion to Sync.
+  web_data_backend_->RemoveExpiredFormElements();
 
   return list_processing_error;
 }
 
 void AutocompleteSyncableService::AutofillEntriesChanged(
     const AutofillChangeList& changes) {
-  // Check if sync is on. If we recieve this notification prior to sync being
-  // started, we'll notify sync to start as soon as it can and later process
-  // all entries when MergeData..() is called. If we receive this notification
-  // sync has exited, it will be synced next time Chrome starts.
-  if (sync_processor_.get()) {
+  // Check if sync is on. If we receive this notification prior to sync being
+  // started, we'll notify sync to start as soon as it can and later process all
+  // entries when MergeDataAndStartSyncing() is called. If we receive this
+  // notification after sync has exited, it will be synced the next time Chrome
+  // starts.
+  if (sync_processor_) {
     ActOnChanges(changes);
   } else if (!flare_.is_null()) {
     flare_.Run(syncer::AUTOFILL);
@@ -327,20 +297,16 @@ void AutocompleteSyncableService::AutofillEntriesChanged(
 
 bool AutocompleteSyncableService::LoadAutofillData(
     std::vector<AutofillEntry>* entries) const {
-  return AutofillTable::FromWebDatabase(
-      webdata_backend_->GetDatabase())->GetAllAutofillEntries(entries);
+  return GetAutofillTable()->GetAllAutofillEntries(entries);
 }
 
 bool AutocompleteSyncableService::SaveChangesToWebData(
     const std::vector<AutofillEntry>& new_entries) {
   DCHECK(CalledOnValidThread());
-
-  if (!new_entries.empty() &&
-      !AutofillTable::FromWebDatabase(
-          webdata_backend_->GetDatabase())->UpdateAutofillEntries(
-              new_entries)) {
+  if (!GetAutofillTable()->UpdateAutofillEntries(new_entries))
     return false;
-  }
+
+  web_data_backend_->NotifyOfMultipleAutofillChanges();
   return true;
 }
 
@@ -350,42 +316,36 @@ void AutocompleteSyncableService::CreateOrUpdateEntry(
     AutocompleteEntryMap* loaded_data,
     std::vector<AutofillEntry>* new_entries) {
   const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
-  const sync_pb::AutofillSpecifics& autofill_specifics(
-      specifics.autofill());
+  const sync_pb::AutofillSpecifics& autofill_specifics(specifics.autofill());
 
   if (!autofill_specifics.has_value()) {
-    DLOG(WARNING)
-        << "Add/Update for old-style autofill profile being dropped!";
+    DVLOG(1) << "Add/Update for old-style autofill profile being dropped!";
     return;
   }
 
   AutofillKey key(autofill_specifics.name().c_str(),
                   autofill_specifics.value().c_str());
   AutocompleteEntryMap::iterator it = loaded_data->find(key);
+  const google::protobuf::RepeatedField<int64_t>& timestamps =
+      autofill_specifics.usage_timestamp();
   if (it == loaded_data->end()) {
     // New entry.
-    std::vector<base::Time> timestamps;
-    size_t timestamps_count = autofill_specifics.usage_timestamp_size();
-    timestamps.reserve(2);
-    if (timestamps_count) {
-      timestamps.push_back(base::Time::FromInternalValue(
-          autofill_specifics.usage_timestamp(0)));
+    base::Time date_created, date_last_used;
+    if (timestamps.size() > 0) {
+      date_created = base::Time::FromInternalValue(*timestamps.begin());
+      date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
     }
-    if (timestamps_count > 1) {
-      timestamps.push_back(base::Time::FromInternalValue(
-          autofill_specifics.usage_timestamp(timestamps_count - 1)));
-    }
-    AutofillEntry new_entry(key, timestamps);
-    new_entries->push_back(new_entry);
+    new_entries->push_back(AutofillEntry(key, date_created, date_last_used));
   } else {
     // Entry already present - merge if necessary.
-    std::vector<base::Time> timestamps;
-    bool different = MergeTimestamps(
-        autofill_specifics, it->second.second->timestamps(), &timestamps);
+    base::Time date_created, date_last_used;
+    bool different = MergeTimestamps(timestamps, *it->second.second,
+                                     &date_created, &date_last_used);
     if (different) {
-      AutofillEntry new_entry(it->second.second->key(), timestamps);
+      AutofillEntry new_entry(
+          it->second.second->key(), date_created, date_last_used);
       new_entries->push_back(new_entry);
-      // Update the sync db if the list of timestamps have changed.
+      // Update the sync db since the timestamps have changed.
       *(it->second.second) = new_entry;
       it->second.first = syncer::SyncChange::ACTION_UPDATE;
     } else {
@@ -399,20 +359,18 @@ void AutocompleteSyncableService::WriteAutofillEntry(
     const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
   sync_pb::AutofillSpecifics* autofill =
       autofill_specifics->mutable_autofill();
-  autofill->set_name(UTF16ToUTF8(entry.key().name()));
-  autofill->set_value(UTF16ToUTF8(entry.key().value()));
-  const std::vector<base::Time>& ts(entry.timestamps());
-  for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
-       timestamp != ts.end(); ++timestamp) {
-    autofill->add_usage_timestamp(timestamp->ToInternalValue());
-  }
+  autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
+  autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
+  autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
+  if (entry.date_created() != entry.date_last_used())
+    autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
 }
 
 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
     const sync_pb::AutofillSpecifics& autofill) {
-  if (!AutofillTable::FromWebDatabase(
-          webdata_backend_->GetDatabase())->RemoveFormElement(
-              UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
+  if (!GetAutofillTable()->RemoveFormElement(
+          base::UTF8ToUTF16(autofill.name()),
+          base::UTF8ToUTF16(autofill.value()))) {
     return error_handler_->CreateAndUploadError(
         FROM_HERE,
         "Could not remove autocomplete entry from WebDatabase.");
@@ -422,23 +380,19 @@ syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
 
 void AutocompleteSyncableService::ActOnChanges(
      const AutofillChangeList& changes) {
-  DCHECK(sync_processor_.get());
+  DCHECK(sync_processor_);
   syncer::SyncChangeList new_changes;
   for (AutofillChangeList::const_iterator change = changes.begin();
        change != changes.end(); ++change) {
     switch (change->type()) {
       case AutofillChange::ADD:
       case AutofillChange::UPDATE: {
-        std::vector<base::Time> timestamps;
-        WebDatabase* db = webdata_backend_->GetDatabase();
-        if (!AutofillTable::FromWebDatabase(db)->GetAutofillTimestamps(
-                change->key().name(),
-                change->key().value(),
-                &timestamps)) {
-          NOTREACHED();
-          return;
-        }
-        AutofillEntry entry(change->key(), timestamps);
+        base::Time date_created, date_last_used;
+        bool success = GetAutofillTable()->GetAutofillTimestamps(
+            change->key().name(), change->key().value(),
+            &date_created, &date_last_used);
+        DCHECK(success);
+        AutofillEntry entry(change->key(), date_created, date_last_used);
         syncer::SyncChange::SyncChangeType change_type =
            (change->type() == AutofillChange::ADD) ?
             syncer::SyncChange::ACTION_ADD :
@@ -448,47 +402,41 @@ void AutocompleteSyncableService::ActOnChanges(
                                                  CreateSyncData(entry)));
         break;
       }
+
       case AutofillChange::REMOVE: {
-        std::vector<base::Time> timestamps;
-        AutofillEntry entry(change->key(), timestamps);
+        AutofillEntry entry(change->key(), base::Time(), base::Time());
         new_changes.push_back(
             syncer::SyncChange(FROM_HERE,
                                syncer::SyncChange::ACTION_DELETE,
                                CreateSyncData(entry)));
         break;
       }
-      default:
-        NOTREACHED();
-        break;
     }
   }
   syncer::SyncError error =
       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
   if (error.IsSet()) {
-    DLOG(WARNING) << "[AUTOCOMPLETE SYNC]"
-                  << " Failed processing change:"
-                  << " Error:" << error.message();
+    DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change.  Error: "
+             << error.message();
   }
 }
 
-void AutocompleteSyncableService::UpdateCullSetting(
-    bool cull_expired_entries) {
-  DCHECK(CalledOnValidThread());
-  cull_expired_entries_ = cull_expired_entries;
-}
-
 syncer::SyncData AutocompleteSyncableService::CreateSyncData(
     const AutofillEntry& entry) const {
   sync_pb::EntitySpecifics autofill_specifics;
   WriteAutofillEntry(entry, &autofill_specifics);
-  std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()),
-                           UTF16ToUTF8(entry.key().value())));
+  std::string tag(KeyToTag(base::UTF16ToUTF8(entry.key().name()),
+                           base::UTF16ToUTF8(entry.key().value())));
   return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
 }
 
+AutofillTable* AutocompleteSyncableService::GetAutofillTable() const {
+  return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
+}
+
 // static
 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
                                                   const std::string& value) {
-  std::string ns(kAutofillEntryNamespaceTag);
-  return ns + net::EscapePath(name) + "|" + net::EscapePath(value);
+  std::string prefix(kAutofillEntryNamespaceTag);
+  return prefix + net::EscapePath(name) + "|" + net::EscapePath(value);
 }