Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(523)

Side by Side Diff: chrome/browser/webdata/autocomplete_syncable_service.cc

Issue 9585020: Cull autofill entries older than 60 days. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressed comments - re-uploading as the first one failed Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 11 matching lines...) Expand all
22 22
23 using content::BrowserThread; 23 using content::BrowserThread;
24 24
25 namespace { 25 namespace {
26 26
27 const char kAutofillEntryNamespaceTag[] = "autofill_entry|"; 27 const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
28 28
29 // Merges timestamps from the |autofill| entry and |timestamps|. Returns 29 // Merges timestamps from the |autofill| entry and |timestamps|. Returns
30 // true if they were different, false if they were the same. 30 // true if they were different, false if they were the same.
31 // All of the timestamp vectors are assummed to be sorted, resulting vector is 31 // All of the timestamp vectors are assummed to be sorted, resulting vector is
32 // sorted as well. 32 // sorted as well. Only two timestamps - the earliest and the latest are stored.
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(),
38 timestamps.end());
39 37
38 new_timestamps->clear();
40 size_t timestamps_count = autofill.usage_timestamp_size(); 39 size_t timestamps_count = autofill.usage_timestamp_size();
41 40 if (timestamps_count == 0 && timestamps.empty()) {
42 bool different = timestamps.size() != timestamps_count; 41 return false;
43 for (size_t i = 0; i < timestamps_count; ++i) { 42 } else if (timestamps_count == 0) {
44 if (timestamp_union.insert(base::Time::FromInternalValue( 43 new_timestamps->insert(new_timestamps->begin(),
45 autofill.usage_timestamp(i))).second) { 44 timestamps.begin(),
45 timestamps.end());
46 return true;
47 } else if (timestamps.empty()) {
48 new_timestamps->reserve(2);
49 new_timestamps->push_back(base::Time::FromInternalValue(
50 autofill.usage_timestamp(0));
51 if (timestamps_count > 1) {
52 new_timestamps->push_back(base::Time::FromInternalValue(
53 autofill.usage_timestamp(timestamps_count - 1));
54 }
55 return true;
56 } else {
57 base::Time time_begin = base::Time::FromInternalValue(
58 autofill.usage_timestamp(0);
59 base::Time time_end = base::Time::FromInternalValue(
60 autofill.usage_timestamp(timestamps_count - 1);
61 bool different = (timestamps_count != timestamps.size());
62 if (timestamps.front() > time_begin) {
Nicolas Zea 2012/03/26 20:58:22 I don't think this is quite right. You're not sett
GeorgeY 2012/03/26 21:19:47 sure. works as well, though slightly modified (you
63 if (timestamps.size() == 1)
64 timestamps.push_back(timestamps[0]);
65 timestamps[0] = time_begin;
46 different = true; 66 different = true;
47 } 67 }
68 if (timestamps.back() < time_end) {
69 if (timestamps.size() == 1)
70 timestamps.push_back(time_end);
71 else
72 timestamps[1] = time_end;
73 different = true;
74 }
75 return different;
48 } 76 }
49
50 if (different) {
51 new_timestamps->insert(new_timestamps->begin(),
52 timestamp_union.begin(),
53 timestamp_union.end());
54 }
55 return different;
56 } 77 }
57 78
58 } // namespace 79 } // namespace
59 80
60 AutocompleteSyncableService::AutocompleteSyncableService( 81 AutocompleteSyncableService::AutocompleteSyncableService(
61 WebDataService* web_data_service) 82 WebDataService* web_data_service)
62 : web_data_service_(web_data_service) { 83 : web_data_service_(web_data_service) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
64 DCHECK(web_data_service_); 85 DCHECK(web_data_service_);
65 notification_registrar_.Add( 86 notification_registrar_.Add(
(...skipping 28 matching lines...) Expand all
94 115
95 AutocompleteEntryMap new_db_entries; 116 AutocompleteEntryMap new_db_entries;
96 for (std::vector<AutofillEntry>::iterator it = entries.begin(); 117 for (std::vector<AutofillEntry>::iterator it = entries.begin();
97 it != entries.end(); ++it) { 118 it != entries.end(); ++it) {
98 new_db_entries[it->key()] = std::make_pair(SyncChange::ACTION_ADD, it); 119 new_db_entries[it->key()] = std::make_pair(SyncChange::ACTION_ADD, it);
99 } 120 }
100 121
101 sync_processor_ = sync_processor.Pass(); 122 sync_processor_ = sync_processor.Pass();
102 123
103 std::vector<AutofillEntry> new_synced_entries; 124 std::vector<AutofillEntry> new_synced_entries;
125 std::vector<AutofillEntry> synced_expired_entries;
104 // Go through and check for all the entries that sync already knows about. 126 // Go through and check for all the entries that sync already knows about.
105 // CreateOrUpdateEntry() will remove entries that are same with the synced 127 // CreateOrUpdateEntry() will remove entries that are same with the synced
106 // ones from |new_db_entries|. 128 // ones from |new_db_entries|.
107 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); 129 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin();
108 sync_iter != initial_sync_data.end(); ++sync_iter) { 130 sync_iter != initial_sync_data.end(); ++sync_iter) {
109 CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries); 131 CreateOrUpdateEntry(*sync_iter, &new_db_entries,
132 &new_synced_entries, &synced_expired_entries);
110 } 133 }
111 134
135 // Check if newly received items need culling.
136 bool need_to_cull_data = !synced_expired_entries.empty();
137
112 if (!SaveChangesToWebData(new_synced_entries)) 138 if (!SaveChangesToWebData(new_synced_entries))
113 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); 139 return SyncError(FROM_HERE, "Failed to update webdata.", model_type());
114 140
115 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); 141 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_);
142 keys_to_ignore_.clear();
116 143
117 SyncChangeList new_changes; 144 SyncChangeList new_changes;
118 for (AutocompleteEntryMap::iterator i = new_db_entries.begin(); 145 for (AutocompleteEntryMap::iterator i = new_db_entries.begin();
119 i != new_db_entries.end(); ++i) { 146 i != new_db_entries.end(); ++i) {
120 new_changes.push_back( 147 // Sync back only the data that appeared after
121 SyncChange(i->second.first, CreateSyncData(*(i->second.second)))); 148 // |AutofillEntry::ExpirationTime()|.
149 if (!i->second.second->IsExpired()) {
150 new_changes.push_back(
151 SyncChange(i->second.first, CreateSyncData(*(i->second.second))));
152 } else {
153 need_to_cull_data = true;
154 // Key is not on the server and is too old, it will not ever be synced -
155 // delete it locally.
156 if (i->second.first == SyncChange::ACTION_ADD)
157 keys_to_ignore_.insert(i->first);
158 }
159 }
160
161 // Delete only the changes never synced to the db as they are too old.
162 for (size_t i = 0; i < synced_expired_entries.size(); ++i) {
163 // Key is on the server and not local and is too old, we need to notify
164 // sync that it has expired.
165 if (keys_to_ignore_.find(synced_expired_entries[i].key()) ==
166 keys_to_ignore_.end()) {
167 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE,
168 CreateSyncData(synced_expired_entries[i])));
169 }
170 }
171
172 if (need_to_cull_data) {
173 // This will schedule deletion operation later on DB thread and we will
174 // be notified on the results of the deletion and deletes will be synced to
175 // the sync.
176 web_data_service_->RemoveExpiredFormElements();
122 } 177 }
123 178
124 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); 179 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
125 180
126 return error; 181 return error;
127 } 182 }
128 183
129 void AutocompleteSyncableService::StopSyncing(syncable::ModelType type) { 184 void AutocompleteSyncableService::StopSyncing(syncable::ModelType type) {
130 DCHECK(CalledOnValidThread()); 185 DCHECK(CalledOnValidThread());
131 DCHECK_EQ(syncable::AUTOFILL, type); 186 DCHECK_EQ(syncable::AUTOFILL, type);
(...skipping 30 matching lines...) Expand all
162 if (!sync_processor_.get()) { 217 if (!sync_processor_.get()) {
163 SyncError error(FROM_HERE, "Models not yet associated.", 218 SyncError error(FROM_HERE, "Models not yet associated.",
164 syncable::AUTOFILL); 219 syncable::AUTOFILL);
165 return error; 220 return error;
166 } 221 }
167 222
168 // Data is loaded only if we get new ADD/UPDATE change. 223 // Data is loaded only if we get new ADD/UPDATE change.
169 std::vector<AutofillEntry> entries; 224 std::vector<AutofillEntry> entries;
170 scoped_ptr<AutocompleteEntryMap> db_entries; 225 scoped_ptr<AutocompleteEntryMap> db_entries;
171 std::vector<AutofillEntry> new_entries; 226 std::vector<AutofillEntry> new_entries;
227 std::vector<AutofillEntry> ignored_entries;
172 228
173 SyncError list_processing_error; 229 SyncError list_processing_error;
174 230
175 for (SyncChangeList::const_iterator i = change_list.begin(); 231 for (SyncChangeList::const_iterator i = change_list.begin();
176 i != change_list.end() && !list_processing_error.IsSet(); ++i) { 232 i != change_list.end() && !list_processing_error.IsSet(); ++i) {
177 DCHECK(i->IsValid()); 233 DCHECK(i->IsValid());
178 switch (i->change_type()) { 234 switch (i->change_type()) {
179 case SyncChange::ACTION_ADD: 235 case SyncChange::ACTION_ADD:
180 case SyncChange::ACTION_UPDATE: 236 case SyncChange::ACTION_UPDATE:
181 if (!db_entries.get()) { 237 if (!db_entries.get()) {
182 if (!LoadAutofillData(&entries)) { 238 if (!LoadAutofillData(&entries)) {
183 return SyncError( 239 return SyncError(
184 FROM_HERE, 240 FROM_HERE,
185 "Could not get the autocomplete data from WebDatabase.", 241 "Could not get the autocomplete data from WebDatabase.",
186 model_type()); 242 model_type());
187 } 243 }
188 db_entries.reset(new AutocompleteEntryMap); 244 db_entries.reset(new AutocompleteEntryMap);
189 for (std::vector<AutofillEntry>::iterator it = entries.begin(); 245 for (std::vector<AutofillEntry>::iterator it = entries.begin();
190 it != entries.end(); ++it) { 246 it != entries.end(); ++it) {
191 (*db_entries)[it->key()] = 247 (*db_entries)[it->key()] =
192 std::make_pair(SyncChange::ACTION_ADD, it); 248 std::make_pair(SyncChange::ACTION_ADD, it);
193 } 249 }
194 } 250 }
195 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries); 251 CreateOrUpdateEntry(i->sync_data(), db_entries.get(),
252 &new_entries, &ignored_entries);
196 break; 253 break;
197 case SyncChange::ACTION_DELETE: { 254 case SyncChange::ACTION_DELETE: {
198 DCHECK(i->sync_data().GetSpecifics().has_autofill()) 255 DCHECK(i->sync_data().GetSpecifics().has_autofill())
199 << "Autofill specifics data not present on delete!"; 256 << "Autofill specifics data not present on delete!";
200 const sync_pb::AutofillSpecifics& autofill = 257 const sync_pb::AutofillSpecifics& autofill =
201 i->sync_data().GetSpecifics().autofill(); 258 i->sync_data().GetSpecifics().autofill();
202 if (autofill.has_value()) { 259 if (autofill.has_value()) {
203 list_processing_error = AutofillEntryDelete(autofill); 260 list_processing_error = AutofillEntryDelete(autofill);
204 } else { 261 } else {
205 DLOG(WARNING) 262 DLOG(WARNING)
206 << "Delete for old-style autofill profile being dropped!"; 263 << "Delete for old-style autofill profile being dropped!";
207 } 264 }
208 } break; 265 } break;
209 default: 266 default:
210 NOTREACHED() << "Unexpected sync change state."; 267 NOTREACHED() << "Unexpected sync change state.";
211 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + 268 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " +
212 SyncChange::ChangeTypeToString(i->change_type()), 269 SyncChange::ChangeTypeToString(i->change_type()),
213 syncable::AUTOFILL); 270 syncable::AUTOFILL);
214 } 271 }
215 } 272 }
216 273
217 if (!SaveChangesToWebData(new_entries)) 274 if (!SaveChangesToWebData(new_entries))
218 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); 275 return SyncError(FROM_HERE, "Failed to update webdata.", model_type());
219 276
277 // Remove already expired data.
278 for (size_t i = 0; i < ignored_entries.size(); ++i) {
279 if (db_entries.get() &&
280 db_entries->find(ignored_entries[i].key()) != db_entries->end()) {
281 bool success = web_data_service_->GetDatabase()->GetAutofillTable()->
282 RemoveFormElement(ignored_entries[i].key().name(),
283 ignored_entries[i].key().value());
284 DCHECK(success);
285 }
286 }
287
220 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); 288 WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_);
221 289
222 return list_processing_error; 290 return list_processing_error;
223 } 291 }
224 292
225 void AutocompleteSyncableService::Observe(int type, 293 void AutocompleteSyncableService::Observe(int type,
226 const content::NotificationSource& source, 294 const content::NotificationSource& source,
227 const content::NotificationDetails& details) { 295 const content::NotificationDetails& details) {
228 DCHECK_EQ(chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED, type); 296 DCHECK_EQ(chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED, type);
229 297
(...skipping 27 matching lines...) Expand all
257 GetAutofillTable()->UpdateAutofillEntries(new_entries)) { 325 GetAutofillTable()->UpdateAutofillEntries(new_entries)) {
258 return false; 326 return false;
259 } 327 }
260 return true; 328 return true;
261 } 329 }
262 330
263 // Creates or updates an autocomplete entry based on |data|. 331 // Creates or updates an autocomplete entry based on |data|.
264 void AutocompleteSyncableService::CreateOrUpdateEntry( 332 void AutocompleteSyncableService::CreateOrUpdateEntry(
265 const SyncData& data, 333 const SyncData& data,
266 AutocompleteEntryMap* loaded_data, 334 AutocompleteEntryMap* loaded_data,
267 std::vector<AutofillEntry>* new_entries) { 335 std::vector<AutofillEntry>* new_entries,
336 std::vector<AutofillEntry>* ignored_entries) {
268 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); 337 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
269 const sync_pb::AutofillSpecifics& autofill_specifics( 338 const sync_pb::AutofillSpecifics& autofill_specifics(
270 specifics.autofill()); 339 specifics.autofill());
271 340
272 if (!autofill_specifics.has_value()) { 341 if (!autofill_specifics.has_value()) {
273 DLOG(WARNING) 342 DLOG(WARNING)
274 << "Add/Update for old-style autofill profile being dropped!"; 343 << "Add/Update for old-style autofill profile being dropped!";
275 return; 344 return;
276 } 345 }
277 346
278 AutofillKey key(autofill_specifics.name().c_str(), 347 AutofillKey key(autofill_specifics.name().c_str(),
279 autofill_specifics.value().c_str()); 348 autofill_specifics.value().c_str());
280 AutocompleteEntryMap::iterator it = loaded_data->find(key); 349 AutocompleteEntryMap::iterator it = loaded_data->find(key);
281 if (it == loaded_data->end()) { 350 if (it == loaded_data->end()) {
282 // New entry. 351 // New entry.
283 std::vector<base::Time> timestamps; 352 std::vector<base::Time> timestamps;
284 size_t timestamps_count = autofill_specifics.usage_timestamp_size(); 353 size_t timestamps_count = autofill_specifics.usage_timestamp_size();
285 timestamps.resize(timestamps_count); 354 timestamps.reserve(2);
286 for (size_t ts = 0; ts < timestamps_count; ++ts) { 355 if (timestamps_count) {
287 timestamps[ts] = base::Time::FromInternalValue( 356 timestamps.push_back(base::Time::FromInternalValue(
288 autofill_specifics.usage_timestamp(ts)); 357 autofill_specifics.usage_timestamp(0)))
289 } 358 }
290 new_entries->push_back(AutofillEntry(key, timestamps)); 359 if (timestamps_count > 1) {
360 timestamps.push_back(base::Time::FromInternalValue(
361 autofill_specifics.usage_timestamp(timestamps_count - 1)))
362 }
363 AutofillEntry new_entry(key, timestamps);
364 if (new_entry.IsExpired())
365 ignored_entries->push_back(new_entry);
366 else
367 new_entries->push_back(new_entry);
291 } else { 368 } else {
292 // Entry already present - merge if necessary. 369 // Entry already present - merge if necessary.
293 std::vector<base::Time> timestamps; 370 std::vector<base::Time> timestamps;
294 bool different = MergeTimestamps( 371 bool different = MergeTimestamps(
295 autofill_specifics, it->second.second->timestamps(), &timestamps); 372 autofill_specifics, it->second.second->timestamps(), &timestamps);
296 if (different) { 373 if (different) {
297 AutofillEntry new_entry(it->second.second->key(), timestamps); 374 AutofillEntry new_entry(it->second.second->key(), timestamps);
298 new_entries->push_back(new_entry); 375 if (new_entry.IsExpired())
376 ignored_entries->push_back(new_entry);
377 else
378 new_entries->push_back(new_entry);
299 379
300 // Update the sync db if the list of timestamps have changed. 380 // Update the sync db if the list of timestamps have changed.
301 *(it->second.second) = new_entry; 381 *(it->second.second) = new_entry;
Nicolas Zea 2012/03/26 20:58:22 This code has no effect if the entry is expired ri
GeorgeY 2012/03/26 21:19:47 Sure. It will get deleted in any case.
302 it->second.first = SyncChange::ACTION_UPDATE; 382 it->second.first = SyncChange::ACTION_UPDATE;
303 } else { 383 } else {
304 loaded_data->erase(it); 384 loaded_data->erase(it);
305 } 385 }
306 } 386 }
307 } 387 }
308 388
309 // static 389 // static
310 void AutocompleteSyncableService::WriteAutofillEntry( 390 void AutocompleteSyncableService::WriteAutofillEntry(
311 const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) { 391 const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 } 431 }
352 AutofillEntry entry(change->key(), timestamps); 432 AutofillEntry entry(change->key(), timestamps);
353 SyncChange::SyncChangeType change_type = 433 SyncChange::SyncChangeType change_type =
354 (change->type() == AutofillChange::ADD) ? 434 (change->type() == AutofillChange::ADD) ?
355 SyncChange::ACTION_ADD : SyncChange::ACTION_UPDATE; 435 SyncChange::ACTION_ADD : SyncChange::ACTION_UPDATE;
356 new_changes.push_back(SyncChange(change_type, 436 new_changes.push_back(SyncChange(change_type,
357 CreateSyncData(entry))); 437 CreateSyncData(entry)));
358 break; 438 break;
359 } 439 }
360 case AutofillChange::REMOVE: { 440 case AutofillChange::REMOVE: {
361 std::vector<base::Time> timestamps; 441 if (keys_to_ignore_.find(change->key()) == keys_to_ignore_.end()) {
362 AutofillEntry entry(change->key(), timestamps); 442 std::vector<base::Time> timestamps;
363 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, 443 AutofillEntry entry(change->key(), timestamps);
364 CreateSyncData(entry))); 444 new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE,
445 CreateSyncData(entry)));
446 }
365 break; 447 break;
366 } 448 }
367 default: 449 default:
368 NOTREACHED(); 450 NOTREACHED();
369 break; 451 break;
370 } 452 }
371 } 453 }
372 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); 454 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
373 if (error.IsSet()) { 455 if (error.IsSet()) {
374 DLOG(WARNING) << "[AUTOCOMPLETE SYNC]" 456 DLOG(WARNING) << "[AUTOCOMPLETE SYNC]"
375 << " Failed processing change:" 457 << " Failed processing change:"
376 << " Error:" << error.message(); 458 << " Error:" << error.message();
377 } 459 }
460 // |keys_to_ignore_| are only needed for the very first notification.
461 keys_to_ignore_.clear();
378 } 462 }
379 463
380 SyncData AutocompleteSyncableService::CreateSyncData( 464 SyncData AutocompleteSyncableService::CreateSyncData(
381 const AutofillEntry& entry) const { 465 const AutofillEntry& entry) const {
382 sync_pb::EntitySpecifics autofill_specifics; 466 sync_pb::EntitySpecifics autofill_specifics;
383 WriteAutofillEntry(entry, &autofill_specifics); 467 WriteAutofillEntry(entry, &autofill_specifics);
384 std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()), 468 std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()),
385 UTF16ToUTF8(entry.key().value()))); 469 UTF16ToUTF8(entry.key().value())));
386 return SyncData::CreateLocalData(tag, tag, autofill_specifics); 470 return SyncData::CreateLocalData(tag, tag, autofill_specifics);
387 } 471 }
388 472
389 // static 473 // static
390 std::string AutocompleteSyncableService::KeyToTag(const std::string& name, 474 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
391 const std::string& value) { 475 const std::string& value) {
392 std::string ns(kAutofillEntryNamespaceTag); 476 std::string ns(kAutofillEntryNamespaceTag);
393 return ns + net::EscapePath(name) + "|" + net::EscapePath(value); 477 return ns + net::EscapePath(name) + "|" + net::EscapePath(value);
394 } 478 }
OLDNEW
« no previous file with comments | « chrome/browser/webdata/autocomplete_syncable_service.h ('k') | chrome/browser/webdata/autofill_entry.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698