Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/webdata/autocomplete_syncable_service.h" | 5 #include "chrome/browser/webdata/autocomplete_syncable_service.h" |
| 6 | 6 |
| 7 #include "base/location.h" | 7 #include "base/location.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
| 10 #include "chrome/browser/profiles/profile.h" | 10 #include "chrome/browser/profiles/profile.h" |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill, | 33 bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill, |
| 34 const std::vector<base::Time>& timestamps, | 34 const std::vector<base::Time>& timestamps, |
| 35 std::vector<base::Time>* new_timestamps) { | 35 std::vector<base::Time>* new_timestamps) { |
| 36 DCHECK(new_timestamps); | 36 DCHECK(new_timestamps); |
| 37 std::set<base::Time> timestamp_union(timestamps.begin(), | 37 std::set<base::Time> timestamp_union(timestamps.begin(), |
| 38 timestamps.end()); | 38 timestamps.end()); |
| 39 | 39 |
| 40 size_t timestamps_count = autofill.usage_timestamp_size(); | 40 size_t timestamps_count = autofill.usage_timestamp_size(); |
| 41 | 41 |
| 42 bool different = timestamps.size() != timestamps_count; | 42 bool different = timestamps.size() != timestamps_count; |
| 43 for (size_t i = 0; i < timestamps_count; ++i) { | 43 for (size_t i = 0; i < timestamps_count; ++i) { |
|
Nicolas Zea
2012/03/26 18:23:55
This shouldn't union, but instead just check first
GeorgeY
2012/03/26 19:59:10
Done.
| |
| 44 if (timestamp_union.insert(base::Time::FromInternalValue( | 44 if (timestamp_union.insert(base::Time::FromInternalValue( |
| 45 autofill.usage_timestamp(i))).second) { | 45 autofill.usage_timestamp(i))).second) { |
| 46 different = true; | 46 different = true; |
| 47 } | 47 } |
| 48 } | 48 } |
| 49 | 49 |
| 50 if (different) { | 50 if (different) { |
| 51 new_timestamps->insert(new_timestamps->begin(), | 51 new_timestamps->insert(new_timestamps->begin(), |
| 52 timestamp_union.begin(), | 52 timestamp_union.begin(), |
| 53 timestamp_union.end()); | 53 timestamp_union.end()); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 | 93 |
| 94 AutocompleteEntryMap new_db_entries; | 94 AutocompleteEntryMap new_db_entries; |
| 95 for (std::vector<AutofillEntry>::iterator it = entries.begin(); | 95 for (std::vector<AutofillEntry>::iterator it = entries.begin(); |
| 96 it != entries.end(); ++it) { | 96 it != entries.end(); ++it) { |
| 97 new_db_entries[it->key()] = std::make_pair(SyncChange::ACTION_ADD, it); | 97 new_db_entries[it->key()] = std::make_pair(SyncChange::ACTION_ADD, it); |
| 98 } | 98 } |
| 99 | 99 |
| 100 sync_processor_.reset(sync_processor); | 100 sync_processor_.reset(sync_processor); |
| 101 | 101 |
| 102 std::vector<AutofillEntry> new_synced_entries; | 102 std::vector<AutofillEntry> new_synced_entries; |
| 103 std::vector<AutofillEntry> synced_expired_entries; | |
| 103 // Go through and check for all the entries that sync already knows about. | 104 // Go through and check for all the entries that sync already knows about. |
| 104 // CreateOrUpdateEntry() will remove entries that are same with the synced | 105 // CreateOrUpdateEntry() will remove entries that are same with the synced |
| 105 // ones from |new_db_entries|. | 106 // ones from |new_db_entries|. |
| 106 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); | 107 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); |
| 107 sync_iter != initial_sync_data.end(); ++sync_iter) { | 108 sync_iter != initial_sync_data.end(); ++sync_iter) { |
| 108 CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries); | 109 CreateOrUpdateEntry(*sync_iter, &new_db_entries, |
| 110 &new_synced_entries, &synced_expired_entries); | |
| 109 } | 111 } |
| 110 | 112 |
| 113 // Check if newly received items need culling. | |
| 114 bool need_to_cull_data = !synced_expired_entries.empty(); | |
| 115 | |
| 111 if (!SaveChangesToWebData(new_synced_entries)) | 116 if (!SaveChangesToWebData(new_synced_entries)) |
| 112 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); | 117 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); |
| 113 | 118 |
| 114 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); | 119 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); |
| 120 keys_to_ignore_.clear(); | |
| 115 | 121 |
| 116 SyncChangeList new_changes; | 122 SyncChangeList new_changes; |
| 117 for (AutocompleteEntryMap::iterator i = new_db_entries.begin(); | 123 for (AutocompleteEntryMap::iterator i = new_db_entries.begin(); |
| 118 i != new_db_entries.end(); ++i) { | 124 i != new_db_entries.end(); ++i) { |
| 119 new_changes.push_back( | 125 // Sync back only the data that appeared after |
| 120 SyncChange(i->second.first, CreateSyncData(*(i->second.second)))); | 126 // |AutofillEntry::ExpirationTime()|. |
| 127 if (!i->second.second->IsExpired()) { | |
| 128 new_changes.push_back( | |
| 129 SyncChange(i->second.first, CreateSyncData(*(i->second.second)))); | |
| 130 } else { | |
| 131 need_to_cull_data = true; | |
| 132 // Key is not on the server and is too old, it will not ever be synced - | |
| 133 // delete it locally. | |
| 134 if (i->second.first == SyncChange::ACTION_ADD) | |
| 135 keys_to_ignore_.insert(i->first); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 // Delete only the changes never synced to the db as they are too old. | |
|
Ilya Sherman
2012/03/23 22:44:45
nit: "synced to" -> "synced to" (extra space)
GeorgeY
2012/03/26 19:59:10
Done.
| |
| 140 for (size_t i = 0; i < synced_expired_entries.size(); ++i) { | |
| 141 // Key is on the server and not local and is too old, we need to notify | |
| 142 // sync that it has expired. | |
| 143 if (keys_to_ignore_.find(synced_expired_entries[i].key()) == | |
| 144 keys_to_ignore_.end()) { | |
| 145 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, | |
| 146 CreateSyncData(synced_expired_entries[i]))); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 if (need_to_cull_data) { | |
| 151 // This will schedule deletion operation later on DB thread and we will | |
| 152 // be notified on the results of the deletion and deletes will be synced to | |
| 153 // the sync. | |
| 154 web_data_service_->RemoveExpiredFormElements(); | |
| 121 } | 155 } |
| 122 | 156 |
| 123 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); | 157 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| 124 | 158 |
| 125 return error; | 159 return error; |
| 126 } | 160 } |
| 127 | 161 |
| 128 void AutocompleteSyncableService::StopSyncing(syncable::ModelType type) { | 162 void AutocompleteSyncableService::StopSyncing(syncable::ModelType type) { |
| 129 DCHECK(CalledOnValidThread()); | 163 DCHECK(CalledOnValidThread()); |
| 130 DCHECK_EQ(syncable::AUTOFILL, type); | 164 DCHECK_EQ(syncable::AUTOFILL, type); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 161 if (!sync_processor_.get()) { | 195 if (!sync_processor_.get()) { |
| 162 SyncError error(FROM_HERE, "Models not yet associated.", | 196 SyncError error(FROM_HERE, "Models not yet associated.", |
| 163 syncable::AUTOFILL); | 197 syncable::AUTOFILL); |
| 164 return error; | 198 return error; |
| 165 } | 199 } |
| 166 | 200 |
| 167 // Data is loaded only if we get new ADD/UPDATE change. | 201 // Data is loaded only if we get new ADD/UPDATE change. |
| 168 std::vector<AutofillEntry> entries; | 202 std::vector<AutofillEntry> entries; |
| 169 scoped_ptr<AutocompleteEntryMap> db_entries; | 203 scoped_ptr<AutocompleteEntryMap> db_entries; |
| 170 std::vector<AutofillEntry> new_entries; | 204 std::vector<AutofillEntry> new_entries; |
| 205 std::vector<AutofillEntry> ignored_entries; | |
| 171 | 206 |
| 172 SyncError list_processing_error; | 207 SyncError list_processing_error; |
| 173 | 208 |
| 174 for (SyncChangeList::const_iterator i = change_list.begin(); | 209 for (SyncChangeList::const_iterator i = change_list.begin(); |
| 175 i != change_list.end() && !list_processing_error.IsSet(); ++i) { | 210 i != change_list.end() && !list_processing_error.IsSet(); ++i) { |
| 176 DCHECK(i->IsValid()); | 211 DCHECK(i->IsValid()); |
| 177 switch (i->change_type()) { | 212 switch (i->change_type()) { |
| 178 case SyncChange::ACTION_ADD: | 213 case SyncChange::ACTION_ADD: |
| 179 case SyncChange::ACTION_UPDATE: | 214 case SyncChange::ACTION_UPDATE: |
| 180 if (!db_entries.get()) { | 215 if (!db_entries.get()) { |
| 181 if (!LoadAutofillData(&entries)) { | 216 if (!LoadAutofillData(&entries)) { |
| 182 return SyncError( | 217 return SyncError( |
| 183 FROM_HERE, | 218 FROM_HERE, |
| 184 "Could not get the autocomplete data from WebDatabase.", | 219 "Could not get the autocomplete data from WebDatabase.", |
| 185 model_type()); | 220 model_type()); |
| 186 } | 221 } |
| 187 db_entries.reset(new AutocompleteEntryMap); | 222 db_entries.reset(new AutocompleteEntryMap); |
| 188 for (std::vector<AutofillEntry>::iterator it = entries.begin(); | 223 for (std::vector<AutofillEntry>::iterator it = entries.begin(); |
| 189 it != entries.end(); ++it) { | 224 it != entries.end(); ++it) { |
| 190 (*db_entries)[it->key()] = | 225 (*db_entries)[it->key()] = |
| 191 std::make_pair(SyncChange::ACTION_ADD, it); | 226 std::make_pair(SyncChange::ACTION_ADD, it); |
| 192 } | 227 } |
| 193 } | 228 } |
| 194 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries); | 229 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), |
| 230 &new_entries, &ignored_entries); | |
| 195 break; | 231 break; |
| 196 case SyncChange::ACTION_DELETE: { | 232 case SyncChange::ACTION_DELETE: { |
| 197 DCHECK(i->sync_data().GetSpecifics().has_autofill()) | 233 DCHECK(i->sync_data().GetSpecifics().has_autofill()) |
| 198 << "Autofill specifics data not present on delete!"; | 234 << "Autofill specifics data not present on delete!"; |
| 199 const sync_pb::AutofillSpecifics& autofill = | 235 const sync_pb::AutofillSpecifics& autofill = |
| 200 i->sync_data().GetSpecifics().autofill(); | 236 i->sync_data().GetSpecifics().autofill(); |
| 201 if (autofill.has_value()) { | 237 if (autofill.has_value()) { |
| 202 list_processing_error = AutofillEntryDelete(autofill); | 238 list_processing_error = AutofillEntryDelete(autofill); |
| 203 } else { | 239 } else { |
| 204 DLOG(WARNING) | 240 DLOG(WARNING) |
| 205 << "Delete for old-style autofill profile being dropped!"; | 241 << "Delete for old-style autofill profile being dropped!"; |
| 206 } | 242 } |
| 207 } break; | 243 } break; |
| 208 default: | 244 default: |
| 209 NOTREACHED() << "Unexpected sync change state."; | 245 NOTREACHED() << "Unexpected sync change state."; |
| 210 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + | 246 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + |
| 211 SyncChange::ChangeTypeToString(i->change_type()), | 247 SyncChange::ChangeTypeToString(i->change_type()), |
| 212 syncable::AUTOFILL); | 248 syncable::AUTOFILL); |
| 213 } | 249 } |
| 214 } | 250 } |
| 215 | 251 |
| 216 if (!SaveChangesToWebData(new_entries)) | 252 if (!SaveChangesToWebData(new_entries)) |
| 217 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); | 253 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); |
| 218 | 254 |
| 255 // Remove already expired data. | |
| 256 for (size_t i = 0; i < ignored_entries.size(); ++i) { | |
| 257 if (db_entries.get() && | |
| 258 db_entries->find(ignored_entries[i].key()) != db_entries->end()) { | |
| 259 if (!web_data_service_->GetDatabase()->GetAutofillTable()-> | |
| 260 RemoveFormElement(ignored_entries[i].key().name(), | |
| 261 ignored_entries[i].key().value())) { | |
| 262 NOTREACHED(); | |
| 263 } | |
|
Ilya Sherman
2012/03/23 22:44:45
nit: Up to you, but I think this would be a little
GeorgeY
2012/03/26 19:59:10
Done.
| |
| 264 } | |
| 265 } | |
| 266 | |
| 219 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); | 267 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); |
| 220 | 268 |
| 221 return list_processing_error; | 269 return list_processing_error; |
| 222 } | 270 } |
| 223 | 271 |
| 224 void AutocompleteSyncableService::Observe(int type, | 272 void AutocompleteSyncableService::Observe(int type, |
| 225 const content::NotificationSource& source, | 273 const content::NotificationSource& source, |
| 226 const content::NotificationDetails& details) { | 274 const content::NotificationDetails& details) { |
| 227 DCHECK_EQ(chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED, type); | 275 DCHECK_EQ(chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED, type); |
| 228 | 276 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 256 GetAutofillTable()->UpdateAutofillEntries(new_entries)) { | 304 GetAutofillTable()->UpdateAutofillEntries(new_entries)) { |
| 257 return false; | 305 return false; |
| 258 } | 306 } |
| 259 return true; | 307 return true; |
| 260 } | 308 } |
| 261 | 309 |
| 262 // Creates or updates an autocomplete entry based on |data|. | 310 // Creates or updates an autocomplete entry based on |data|. |
| 263 void AutocompleteSyncableService::CreateOrUpdateEntry( | 311 void AutocompleteSyncableService::CreateOrUpdateEntry( |
| 264 const SyncData& data, | 312 const SyncData& data, |
| 265 AutocompleteEntryMap* loaded_data, | 313 AutocompleteEntryMap* loaded_data, |
| 266 std::vector<AutofillEntry>* new_entries) { | 314 std::vector<AutofillEntry>* new_entries, |
| 315 std::vector<AutofillEntry>* ignored_entries) { | |
| 267 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); | 316 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); |
| 268 const sync_pb::AutofillSpecifics& autofill_specifics( | 317 const sync_pb::AutofillSpecifics& autofill_specifics( |
| 269 specifics.autofill()); | 318 specifics.autofill()); |
| 270 | 319 |
| 271 if (!autofill_specifics.has_value()) { | 320 if (!autofill_specifics.has_value()) { |
| 272 DLOG(WARNING) | 321 DLOG(WARNING) |
| 273 << "Add/Update for old-style autofill profile being dropped!"; | 322 << "Add/Update for old-style autofill profile being dropped!"; |
| 274 return; | 323 return; |
| 275 } | 324 } |
| 276 | 325 |
| 277 AutofillKey key(autofill_specifics.name().c_str(), | 326 AutofillKey key(autofill_specifics.name().c_str(), |
| 278 autofill_specifics.value().c_str()); | 327 autofill_specifics.value().c_str()); |
| 279 AutocompleteEntryMap::iterator it = loaded_data->find(key); | 328 AutocompleteEntryMap::iterator it = loaded_data->find(key); |
| 280 if (it == loaded_data->end()) { | 329 if (it == loaded_data->end()) { |
| 281 // New entry. | 330 // New entry. |
| 282 std::vector<base::Time> timestamps; | 331 std::vector<base::Time> timestamps; |
| 283 size_t timestamps_count = autofill_specifics.usage_timestamp_size(); | 332 size_t timestamps_count = autofill_specifics.usage_timestamp_size(); |
| 284 timestamps.resize(timestamps_count); | 333 timestamps.resize(timestamps_count); |
| 285 for (size_t ts = 0; ts < timestamps_count; ++ts) { | 334 for (size_t ts = 0; ts < timestamps_count; ++ts) { |
| 286 timestamps[ts] = base::Time::FromInternalValue( | 335 timestamps[ts] = base::Time::FromInternalValue( |
| 287 autofill_specifics.usage_timestamp(ts)); | 336 autofill_specifics.usage_timestamp(ts)); |
|
Nicolas Zea
2012/03/26 18:23:55
this should probably only look at the first and la
GeorgeY
2012/03/26 19:59:10
Done.
| |
| 288 } | 337 } |
| 289 new_entries->push_back(AutofillEntry(key, timestamps)); | 338 AutofillEntry new_entry(key, timestamps); |
| 339 if (new_entry.IsExpired()) | |
| 340 ignored_entries->push_back(new_entry); | |
| 341 else | |
| 342 new_entries->push_back(new_entry); | |
| 290 } else { | 343 } else { |
| 291 // Entry already present - merge if necessary. | 344 // Entry already present - merge if necessary. |
| 292 std::vector<base::Time> timestamps; | 345 std::vector<base::Time> timestamps; |
| 293 bool different = MergeTimestamps( | 346 bool different = MergeTimestamps( |
| 294 autofill_specifics, it->second.second->timestamps(), ×tamps); | 347 autofill_specifics, it->second.second->timestamps(), ×tamps); |
| 295 if (different) { | 348 if (different) { |
| 296 AutofillEntry new_entry(it->second.second->key(), timestamps); | 349 AutofillEntry new_entry(it->second.second->key(), timestamps); |
| 297 new_entries->push_back(new_entry); | 350 if (new_entry.IsExpired()) |
| 351 ignored_entries->push_back(new_entry); | |
| 352 else | |
| 353 new_entries->push_back(new_entry); | |
| 298 | 354 |
| 299 // Update the sync db if the list of timestamps have changed. | 355 // Update the sync db if the list of timestamps have changed. |
| 300 *(it->second.second) = new_entry; | 356 *(it->second.second) = new_entry; |
| 301 it->second.first = SyncChange::ACTION_UPDATE; | 357 it->second.first = SyncChange::ACTION_UPDATE; |
| 302 } else { | 358 } else { |
| 303 loaded_data->erase(it); | 359 loaded_data->erase(it); |
| 304 } | 360 } |
| 305 } | 361 } |
| 306 } | 362 } |
| 307 | 363 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 } | 406 } |
| 351 AutofillEntry entry(change->key(), timestamps); | 407 AutofillEntry entry(change->key(), timestamps); |
| 352 SyncChange::SyncChangeType change_type = | 408 SyncChange::SyncChangeType change_type = |
| 353 (change->type() == AutofillChange::ADD) ? | 409 (change->type() == AutofillChange::ADD) ? |
| 354 SyncChange::ACTION_ADD : SyncChange::ACTION_UPDATE; | 410 SyncChange::ACTION_ADD : SyncChange::ACTION_UPDATE; |
| 355 new_changes.push_back(SyncChange(change_type, | 411 new_changes.push_back(SyncChange(change_type, |
| 356 CreateSyncData(entry))); | 412 CreateSyncData(entry))); |
| 357 break; | 413 break; |
| 358 } | 414 } |
| 359 case AutofillChange::REMOVE: { | 415 case AutofillChange::REMOVE: { |
| 360 std::vector<base::Time> timestamps; | 416 if (keys_to_ignore_.find(change->key()) == keys_to_ignore_.end()) { |
| 361 AutofillEntry entry(change->key(), timestamps); | 417 std::vector<base::Time> timestamps; |
| 362 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, | 418 AutofillEntry entry(change->key(), timestamps); |
| 363 CreateSyncData(entry))); | 419 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, |
| 420 CreateSyncData(entry))); | |
| 421 } | |
| 364 break; | 422 break; |
| 365 } | 423 } |
| 366 default: | 424 default: |
| 367 NOTREACHED(); | 425 NOTREACHED(); |
| 368 break; | 426 break; |
| 369 } | 427 } |
| 370 } | 428 } |
| 371 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); | 429 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); |
| 372 if (error.IsSet()) { | 430 if (error.IsSet()) { |
| 373 DLOG(WARNING) << "[AUTOCOMPLETE SYNC]" | 431 DLOG(WARNING) << "[AUTOCOMPLETE SYNC]" |
| 374 << " Failed processing change:" | 432 << " Failed processing change:" |
| 375 << " Error:" << error.message(); | 433 << " Error:" << error.message(); |
| 376 } | 434 } |
| 435 // |keys_to_ignore_| are only needed for the very first notification. | |
| 436 keys_to_ignore_.clear(); | |
| 377 } | 437 } |
| 378 | 438 |
| 379 SyncData AutocompleteSyncableService::CreateSyncData( | 439 SyncData AutocompleteSyncableService::CreateSyncData( |
| 380 const AutofillEntry& entry) const { | 440 const AutofillEntry& entry) const { |
| 381 sync_pb::EntitySpecifics autofill_specifics; | 441 sync_pb::EntitySpecifics autofill_specifics; |
| 382 WriteAutofillEntry(entry, &autofill_specifics); | 442 WriteAutofillEntry(entry, &autofill_specifics); |
| 383 std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()), | 443 std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()), |
| 384 UTF16ToUTF8(entry.key().value()))); | 444 UTF16ToUTF8(entry.key().value()))); |
| 385 return SyncData::CreateLocalData(tag, tag, autofill_specifics); | 445 return SyncData::CreateLocalData(tag, tag, autofill_specifics); |
| 386 } | 446 } |
| 387 | 447 |
| 388 // static | 448 // static |
| 389 std::string AutocompleteSyncableService::KeyToTag(const std::string& name, | 449 std::string AutocompleteSyncableService::KeyToTag(const std::string& name, |
| 390 const std::string& value) { | 450 const std::string& value) { |
| 391 std::string ns(kAutofillEntryNamespaceTag); | 451 std::string ns(kAutofillEntryNamespaceTag); |
| 392 return ns + net::EscapePath(name) + "|" + net::EscapePath(value); | 452 return ns + net::EscapePath(name) + "|" + net::EscapePath(value); |
| 393 } | 453 } |
| OLD | NEW |