OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "components/autofill/core/browser/webdata/autofill_table.h" | 5 #include "components/autofill/core/browser/webdata/autofill_table.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
10 #include <map> | 10 #include <map> |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
465 return MigrateToVersion60AddServerCards(); | 465 return MigrateToVersion60AddServerCards(); |
466 case 61: | 466 case 61: |
467 *update_compatible_version = false; | 467 *update_compatible_version = false; |
468 return MigrateToVersion61AddUsageStats(); | 468 return MigrateToVersion61AddUsageStats(); |
469 case 62: | 469 case 62: |
470 *update_compatible_version = false; | 470 *update_compatible_version = false; |
471 return MigrateToVersion62AddUsageStatsForUnmaskedCards(); | 471 return MigrateToVersion62AddUsageStatsForUnmaskedCards(); |
472 case 63: | 472 case 63: |
473 *update_compatible_version = false; | 473 *update_compatible_version = false; |
474 return MigrateToVersion63AddServerRecipientName(); | 474 return MigrateToVersion63AddServerRecipientName(); |
475 case 64: | |
476 *update_compatible_version = false; | |
477 return MigrateToVersion64AddUnmaskDate(); | |
475 } | 478 } |
476 return true; | 479 return true; |
477 } | 480 } |
478 | 481 |
479 bool AutofillTable::AddFormFieldValues( | 482 bool AutofillTable::AddFormFieldValues( |
480 const std::vector<FormFieldData>& elements, | 483 const std::vector<FormFieldData>& elements, |
481 std::vector<AutofillChange>* changes) { | 484 std::vector<AutofillChange>* changes) { |
482 return AddFormFieldValuesTime(elements, changes, Time::Now()); | 485 return AddFormFieldValuesTime(elements, changes, Time::Now()); |
483 } | 486 } |
484 | 487 |
(...skipping 702 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1187 card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++)); | 1190 card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++)); |
1188 card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++)); | 1191 card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++)); |
1189 credit_cards->push_back(card); | 1192 credit_cards->push_back(card); |
1190 } | 1193 } |
1191 | 1194 |
1192 return s.Succeeded(); | 1195 return s.Succeeded(); |
1193 } | 1196 } |
1194 | 1197 |
1195 void AutofillTable::SetServerCreditCards( | 1198 void AutofillTable::SetServerCreditCards( |
1196 const std::vector<CreditCard>& credit_cards) { | 1199 const std::vector<CreditCard>& credit_cards) { |
1200 sql::Transaction transaction(db_); | |
1201 if (!transaction.Begin()) | |
1202 return; | |
1203 | |
1197 // Delete all old values. | 1204 // Delete all old values. |
1198 sql::Statement masked_delete(db_->GetUniqueStatement( | 1205 sql::Statement masked_delete(db_->GetUniqueStatement( |
1199 "DELETE FROM masked_credit_cards")); | 1206 "DELETE FROM masked_credit_cards")); |
1200 masked_delete.Run(); | 1207 masked_delete.Run(); |
1201 | 1208 |
1202 // Delete all items in the unmasked table that aren't in the new set. | 1209 // Delete all items in the unmasked table that aren't in the new set. |
1203 sql::Statement get_unmasked(db_->GetUniqueStatement( | 1210 sql::Statement get_unmasked(db_->GetUniqueStatement( |
1204 "SELECT id FROM unmasked_credit_cards")); | 1211 "SELECT id FROM unmasked_credit_cards")); |
1212 std::set<std::string> kept_unmasked_entries; | |
1205 while (get_unmasked.Step()) { | 1213 while (get_unmasked.Step()) { |
1206 // We expect relatively few cards, just do brute-force. | 1214 // We expect relatively few cards, just do brute-force. |
1207 std::string server_id = get_unmasked.ColumnString(0); | 1215 std::string server_id = get_unmasked.ColumnString(0); |
1208 bool found_card = false; | 1216 bool found_card = false; |
1209 for (const CreditCard& cur_card : credit_cards) { | 1217 for (const CreditCard& cur_card : credit_cards) { |
1210 if (cur_card.server_id() == server_id) { | 1218 if (cur_card.server_id() == server_id) { |
1211 found_card = true; | 1219 found_card = true; |
1212 break; | 1220 break; |
1213 } | 1221 } |
1214 } | 1222 } |
1215 if (!found_card) { | 1223 if (found_card) { |
1224 kept_unmasked_entries.insert(server_id); | |
1225 } else { | |
1216 // This unmasked card in the DB isn't present in the input. The statement | 1226 // This unmasked card in the DB isn't present in the input. The statement |
1217 // is compiled every time because it's much more likely that this is never | 1227 // is compiled every time because it's much more likely that this is never |
1218 // executed than it runs more than once. | 1228 // executed than it runs more than once. |
1219 sql::Statement unmasked_delete(db_->GetUniqueStatement( | 1229 sql::Statement unmasked_delete(db_->GetUniqueStatement( |
1220 "DELETE FROM unmasked_credit_cards WHERE id = ?")); | 1230 "DELETE FROM unmasked_credit_cards WHERE id = ?")); |
1221 unmasked_delete.BindString(0, server_id); | 1231 unmasked_delete.BindString(0, server_id); |
1222 unmasked_delete.Run(); | 1232 unmasked_delete.Run(); |
1223 DCHECK_EQ(1, db_->GetLastChangeCount()); | 1233 DCHECK_EQ(1, db_->GetLastChangeCount()); |
1224 } | 1234 } |
1225 } | 1235 } |
1226 | 1236 |
1227 sql::Statement masked_insert(db_->GetUniqueStatement( | 1237 sql::Statement masked_insert(db_->GetUniqueStatement( |
1228 "INSERT INTO masked_credit_cards(" | 1238 "INSERT INTO masked_credit_cards(" |
1229 "id," // 0 | 1239 "id," // 0 |
1230 "type," // 1 | 1240 "type," // 1 |
1231 "status," // 2 | 1241 "status," // 2 |
1232 "name_on_card," // 3 | 1242 "name_on_card," // 3 |
1233 "last_four," // 4 | 1243 "last_four," // 4 |
1234 "exp_month," // 4 | 1244 "exp_month," // 4 |
1235 "exp_year) " // 5 | 1245 "exp_year) " // 5 |
1236 "VALUES (?,?,?,?,?,?,?)")); | 1246 "VALUES (?,?,?,?,?,?,?)")); |
1237 sql::Statement unmasked_insert(db_->GetUniqueStatement( | 1247 sql::Statement unmasked_insert(db_->GetUniqueStatement( |
1238 "INSERT INTO unmasked_credit_cards(" | 1248 "INSERT INTO unmasked_credit_cards(" |
1239 "id," // 0 | 1249 "id," // 0 |
1240 "card_number_encrypted)" // 1 | 1250 "card_number_encrypted, " // 1 |
1241 "VALUES (?,?)")); | 1251 "unmask_date)" // 2 |
1252 "VALUES (?,?,?)")); | |
1242 for (const CreditCard& card : credit_cards) { | 1253 for (const CreditCard& card : credit_cards) { |
1243 DCHECK(card.record_type() != CreditCard::LOCAL_CARD); | 1254 DCHECK(card.record_type() != CreditCard::LOCAL_CARD); |
1244 | 1255 |
1245 masked_insert.BindString(0, card.server_id()); | 1256 masked_insert.BindString(0, card.server_id()); |
1246 masked_insert.BindString(1, card.type()); | 1257 masked_insert.BindString(1, card.type()); |
1247 masked_insert.BindString(2, | 1258 masked_insert.BindString(2, |
1248 ServerStatusEnumToString(card.GetServerStatus())); | 1259 ServerStatusEnumToString(card.GetServerStatus())); |
1249 masked_insert.BindString16(3, card.GetRawInfo(CREDIT_CARD_NAME)); | 1260 masked_insert.BindString16(3, card.GetRawInfo(CREDIT_CARD_NAME)); |
1250 masked_insert.BindString16(4, card.LastFourDigits()); | 1261 masked_insert.BindString16(4, card.LastFourDigits()); |
1251 masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH)); | 1262 masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH)); |
1252 masked_insert.BindString16(6, | 1263 masked_insert.BindString16(6, |
1253 card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); | 1264 card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); |
1254 | 1265 |
1255 masked_insert.Run(); | 1266 masked_insert.Run(); |
1256 masked_insert.Reset(true); | 1267 masked_insert.Reset(true); |
1257 | 1268 |
1258 if (card.record_type() == CreditCard::FULL_SERVER_CARD) { | 1269 if (kept_unmasked_entries.find(card.server_id()) == |
1259 // Unmasked cards also get an entry in the unmasked table. Note that the | 1270 kept_unmasked_entries.end() && |
1260 // input card could be MASKED but if we have an UNMASKED entry for that | 1271 card.record_type() == CreditCard::FULL_SERVER_CARD) { |
1261 // card already, it will be preserved. | 1272 // New unmasked cards also get an entry in the unmasked table. In |
1273 // practice this will only happen for tests since the server cards will | |
1274 // all be marked masked (if a server card is manually unmasked, we will | |
1275 // have kept the existing entry in the table and won't get here). | |
1262 unmasked_insert.BindString(0, card.server_id()); | 1276 unmasked_insert.BindString(0, card.server_id()); |
1263 BindEncryptedCardToColumn(&unmasked_insert, 1, | 1277 BindEncryptedCardToColumn(&unmasked_insert, 1, |
1264 card.GetRawInfo(CREDIT_CARD_NUMBER)); | 1278 card.GetRawInfo(CREDIT_CARD_NUMBER)); |
1279 // Unmask time for this card is now. | |
1280 unmasked_insert.BindInt64(2, Time::Now().ToInternalValue()); | |
1265 unmasked_insert.Run(); | 1281 unmasked_insert.Run(); |
1266 unmasked_insert.Reset(true); | 1282 unmasked_insert.Reset(true); |
1267 } | 1283 } |
1268 } | 1284 } |
1285 | |
1286 if (!transaction.Commit()) | |
1287 return; | |
Evan Stade
2015/03/06 00:20:51
nit: unnecessary return
| |
1269 } | 1288 } |
1270 | 1289 |
1271 bool AutofillTable::UnmaskServerCreditCard(const std::string& id, | 1290 bool AutofillTable::UnmaskServerCreditCard(const std::string& id, |
1272 const base::string16& full_number) { | 1291 const base::string16& full_number) { |
1273 // Make sure there aren't duplicates for this card. | 1292 // Make sure there aren't duplicates for this card. |
1274 MaskServerCreditCard(id); | 1293 MaskServerCreditCard(id); |
1275 sql::Statement s(db_->GetUniqueStatement( | 1294 sql::Statement s(db_->GetUniqueStatement( |
1276 "INSERT INTO unmasked_credit_cards(id, card_number_encrypted," | 1295 "INSERT INTO unmasked_credit_cards(" |
1277 " use_count, use_date) " | 1296 "id," |
1278 "VALUES (?,?,?,?)")); | 1297 "card_number_encrypted," |
1298 "use_count," | |
1299 "use_date," | |
1300 "unmask_date)" | |
1301 "VALUES (?,?,?,?,?)")); | |
1279 s.BindString(0, id); | 1302 s.BindString(0, id); |
1280 | 1303 |
1281 std::string encrypted_data; | 1304 std::string encrypted_data; |
1282 OSCrypt::EncryptString16(full_number, &encrypted_data); | 1305 OSCrypt::EncryptString16(full_number, &encrypted_data); |
1283 s.BindBlob(1, encrypted_data.data(), | 1306 s.BindBlob(1, encrypted_data.data(), |
1284 static_cast<int>(encrypted_data.length())); | 1307 static_cast<int>(encrypted_data.length())); |
1285 | 1308 |
1286 // Unmasking counts as a usage, so set the stats accordingly. | 1309 // Unmasking counts as a usage, so set the stats accordingly. |
1287 s.BindInt64(2, 1); | 1310 base::Time now = base::Time::Now(); |
1288 s.BindInt64(3, base::Time::Now().ToInternalValue()); | 1311 s.BindInt64(2, 1); // use_count |
1312 s.BindInt64(3, now.ToInternalValue()); // use_date | |
1313 | |
1314 s.BindInt64(4, now.ToInternalValue()); // unmask_date | |
1289 | 1315 |
1290 s.Run(); | 1316 s.Run(); |
1291 return db_->GetLastChangeCount() > 0; | 1317 return db_->GetLastChangeCount() > 0; |
1292 } | 1318 } |
1293 | 1319 |
1294 bool AutofillTable::MaskServerCreditCard(const std::string& id) { | 1320 bool AutofillTable::MaskServerCreditCard(const std::string& id) { |
1295 sql::Statement s(db_->GetUniqueStatement( | 1321 sql::Statement s(db_->GetUniqueStatement( |
1296 "DELETE FROM unmasked_credit_cards WHERE id = ?")); | 1322 "DELETE FROM unmasked_credit_cards WHERE id = ?")); |
1297 s.BindString(0, id); | 1323 s.BindString(0, id); |
1298 s.Run(); | 1324 s.Run(); |
(...skipping 21 matching lines...) Expand all Loading... | |
1320 CreditCard* tmp_credit_card = NULL; | 1346 CreditCard* tmp_credit_card = NULL; |
1321 if (!GetCreditCard(credit_card.guid(), &tmp_credit_card)) | 1347 if (!GetCreditCard(credit_card.guid(), &tmp_credit_card)) |
1322 return false; | 1348 return false; |
1323 | 1349 |
1324 scoped_ptr<CreditCard> old_credit_card(tmp_credit_card); | 1350 scoped_ptr<CreditCard> old_credit_card(tmp_credit_card); |
1325 bool update_modification_date = *old_credit_card != credit_card; | 1351 bool update_modification_date = *old_credit_card != credit_card; |
1326 | 1352 |
1327 sql::Statement s(db_->GetUniqueStatement( | 1353 sql::Statement s(db_->GetUniqueStatement( |
1328 "UPDATE credit_cards " | 1354 "UPDATE credit_cards " |
1329 "SET guid=?, name_on_card=?, expiration_month=?," | 1355 "SET guid=?, name_on_card=?, expiration_month=?," |
1330 " expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?," | 1356 "expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?," |
1331 " date_modified=?, origin=?" | 1357 "date_modified=?, origin=?" |
1332 "WHERE guid=?")); | 1358 "WHERE guid=?")); |
1333 BindCreditCardToStatement( | 1359 BindCreditCardToStatement( |
1334 credit_card, | 1360 credit_card, |
1335 update_modification_date ? base::Time::Now() : | 1361 update_modification_date ? base::Time::Now() : |
1336 old_credit_card->modification_date(), | 1362 old_credit_card->modification_date(), |
1337 &s); | 1363 &s); |
1338 s.BindString(9, credit_card.guid()); | 1364 s.BindString(9, credit_card.guid()); |
1339 | 1365 |
1340 bool result = s.Run(); | 1366 bool result = s.Run(); |
1341 DCHECK_GT(db_->GetLastChangeCount(), 0); | 1367 DCHECK_GT(db_->GetLastChangeCount(), 0); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1400 } | 1426 } |
1401 if (!s_credit_cards_get.Succeeded()) | 1427 if (!s_credit_cards_get.Succeeded()) |
1402 return false; | 1428 return false; |
1403 | 1429 |
1404 // Remove Autofill credit cards in the time range. | 1430 // Remove Autofill credit cards in the time range. |
1405 sql::Statement s_credit_cards(db_->GetUniqueStatement( | 1431 sql::Statement s_credit_cards(db_->GetUniqueStatement( |
1406 "DELETE FROM credit_cards " | 1432 "DELETE FROM credit_cards " |
1407 "WHERE date_modified >= ? AND date_modified < ?")); | 1433 "WHERE date_modified >= ? AND date_modified < ?")); |
1408 s_credit_cards.BindInt64(0, delete_begin_t); | 1434 s_credit_cards.BindInt64(0, delete_begin_t); |
1409 s_credit_cards.BindInt64(1, delete_end_t); | 1435 s_credit_cards.BindInt64(1, delete_end_t); |
1436 if (!s_credit_cards.Run()) | |
1437 return false; | |
1410 | 1438 |
1411 return s_credit_cards.Run(); | 1439 // Remove unmasked credit cards in the time range. |
1440 sql::Statement s_unmasked_cards(db_->GetUniqueStatement( | |
1441 "DELETE FROM unmasked_credit_cards " | |
1442 "WHERE unmask_date >= ? AND unmask_date < ?")); | |
1443 s_unmasked_cards.BindInt64(0, delete_begin.ToInternalValue()); | |
1444 s_unmasked_cards.BindInt64(1, delete_end.ToInternalValue()); | |
1445 if (!s_unmasked_cards.Run()) | |
Evan Stade
2015/03/06 00:20:51
nit: return s_unmasked_cards.Run()
| |
1446 return false; | |
1447 | |
1448 return true; | |
1412 } | 1449 } |
1413 | 1450 |
1414 bool AutofillTable::RemoveOriginURLsModifiedBetween( | 1451 bool AutofillTable::RemoveOriginURLsModifiedBetween( |
1415 const Time& delete_begin, | 1452 const Time& delete_begin, |
1416 const Time& delete_end, | 1453 const Time& delete_end, |
1417 ScopedVector<AutofillProfile>* profiles) { | 1454 ScopedVector<AutofillProfile>* profiles) { |
1418 DCHECK(delete_end.is_null() || delete_begin < delete_end); | 1455 DCHECK(delete_end.is_null() || delete_begin < delete_end); |
1419 | 1456 |
1420 time_t delete_begin_t = delete_begin.ToTimeT(); | 1457 time_t delete_begin_t = delete_begin.ToTimeT(); |
1421 time_t delete_end_t = GetEndTime(delete_end); | 1458 time_t delete_end_t = GetEndTime(delete_end); |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1665 } | 1702 } |
1666 return true; | 1703 return true; |
1667 } | 1704 } |
1668 | 1705 |
1669 bool AutofillTable::InitUnmaskedCreditCardsTable() { | 1706 bool AutofillTable::InitUnmaskedCreditCardsTable() { |
1670 if (!db_->DoesTableExist("unmasked_credit_cards")) { | 1707 if (!db_->DoesTableExist("unmasked_credit_cards")) { |
1671 if (!db_->Execute("CREATE TABLE unmasked_credit_cards (" | 1708 if (!db_->Execute("CREATE TABLE unmasked_credit_cards (" |
1672 "id VARCHAR," | 1709 "id VARCHAR," |
1673 "card_number_encrypted VARCHAR, " | 1710 "card_number_encrypted VARCHAR, " |
1674 "use_count INTEGER NOT NULL DEFAULT 0, " | 1711 "use_count INTEGER NOT NULL DEFAULT 0, " |
1675 "use_date INTEGER NOT NULL DEFAULT 0)")) { | 1712 "use_date INTEGER NOT NULL DEFAULT 0, " |
1713 "unmask_date INTEGER NOT NULL DEFAULT 0)")) { | |
1676 NOTREACHED(); | 1714 NOTREACHED(); |
1677 return false; | 1715 return false; |
1678 } | 1716 } |
1679 } | 1717 } |
1680 return true; | 1718 return true; |
1681 } | 1719 } |
1682 | 1720 |
1683 bool AutofillTable::InitServerAddressesTable() { | 1721 bool AutofillTable::InitServerAddressesTable() { |
1684 if (!db_->DoesTableExist("server_addresses")) { | 1722 if (!db_->DoesTableExist("server_addresses")) { |
1685 // The space after language_code is necessary to match what sqlite does | 1723 // The space after language_code is necessary to match what sqlite does |
1686 // when it appends the column in migration. | 1724 // when it appends the column in migration. |
1687 if (!db_->Execute("CREATE TABLE server_addresses (" | 1725 if (!db_->Execute("CREATE TABLE server_addresses (" |
1688 "id VARCHAR," | 1726 "id VARCHAR," |
1689 "company_name VARCHAR," | 1727 "company_name VARCHAR," |
1690 "street_address VARCHAR," | 1728 "street_address VARCHAR," |
1691 "address_1 VARCHAR," | 1729 "address_1 VARCHAR," |
1692 "address_2 VARCHAR," | 1730 "address_2 VARCHAR," |
1693 "address_3 VARCHAR," | 1731 "address_3 VARCHAR," |
1694 "address_4 VARCHAR," | 1732 "address_4 VARCHAR," |
1695 "postal_code VARCHAR," | 1733 "postal_code VARCHAR," |
1696 "sorting_code VARCHAR," | 1734 "sorting_code VARCHAR," |
1697 "country_code VARCHAR," | 1735 "country_code VARCHAR," |
1698 "language_code VARCHAR, " // Space required. | 1736 "language_code VARCHAR, " // Space required. |
1699 "recipient_name VARCHAR)")) { | 1737 "recipient_name VARCHAR, " // Ditto. |
1738 "phone_number VARCHAR)")) { | |
1700 NOTREACHED(); | 1739 NOTREACHED(); |
1701 return false; | 1740 return false; |
1702 } | 1741 } |
1703 } | 1742 } |
1704 return true; | 1743 return true; |
1705 } | 1744 } |
1706 | 1745 |
1707 bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() { | 1746 bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() { |
1708 sql::Transaction transaction(db_); | 1747 sql::Transaction transaction(db_); |
1709 if (!transaction.Begin()) | 1748 if (!transaction.Begin()) |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1982 | 2021 |
1983 bool AutofillTable::MigrateToVersion63AddServerRecipientName() { | 2022 bool AutofillTable::MigrateToVersion63AddServerRecipientName() { |
1984 if (!db_->DoesColumnExist("server_addresses", "recipient_name") && | 2023 if (!db_->DoesColumnExist("server_addresses", "recipient_name") && |
1985 !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " | 2024 !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " |
1986 "recipient_name VARCHAR")) { | 2025 "recipient_name VARCHAR")) { |
1987 return false; | 2026 return false; |
1988 } | 2027 } |
1989 return true; | 2028 return true; |
1990 } | 2029 } |
1991 | 2030 |
2031 bool AutofillTable::MigrateToVersion64AddUnmaskDate() { | |
Evan Stade
2015/03/06 00:20:51
transaction
| |
2032 if (!db_->DoesColumnExist("unmasked_credit_cards", "unmask_date") && | |
2033 !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " | |
2034 "unmask_date INTEGER NOT NULL DEFAULT 0")) { | |
2035 return false; | |
2036 } | |
2037 if (!db_->DoesColumnExist("server_addresses", "phone_number") && | |
2038 !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " | |
2039 "phone_number VARCHAR")) { | |
2040 return false; | |
2041 } | |
2042 return true; | |
2043 } | |
2044 | |
1992 } // namespace autofill | 2045 } // namespace autofill |
OLD | NEW |