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 3 // found in the LICENSE file. 4 5 #include "base/json/json_reader.h" 6 #include "base/logging.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "base/values.h" 10 #include "components/autofill/content/browser/wallet/gaia_account.h" 11 #include "components/autofill/content/browser/wallet/required_action.h" 12 #include "components/autofill/content/browser/wallet/wallet_items.h" 13 #include "components/autofill/content/browser/wallet/wallet_test_util.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "url/gurl.h" 16 17 using base::ASCIIToUTF16; 18 19 namespace { 20 21 const char kMaskedInstrument[] = 22 "{" 23 " \"descriptive_name\":\"descriptive_name\"," 24 " \"type\":\"VISA\"," 25 " \"last_four_digits\":\"last_four_digits\"," 26 " \"expiration_month\":12," 27 " \"expiration_year\":2012," 28 " \"billing_address\":" 29 " {" 30 " \"name\":\"name\"," 31 " \"address1\":\"address1\"," 32 " \"address2\":\"address2\"," 33 " \"city\":\"city\"," 34 " \"dependent_locality_name\":\"burough\"," 35 " \"state\":\"state\"," 36 " \"postal_code\":\"postal_code\"," 37 " \"sorting_code\":\"sorting_code\"," 38 " \"phone_number\":\"phone_number\"," 39 " \"country_code\":\"US\"," 40 " \"type\":\"FULL\"," 41 " \"language_code\":\"language_code\"" 42 " }," 43 " \"status\":\"VALID\"," 44 " \"object_id\":\"object_id\"" 45 "}"; 46 47 const char kMaskedInstrumentMissingStatus[] = 48 "{" 49 " \"descriptive_name\":\"descriptive_name\"," 50 " \"type\":\"VISA\"," 51 " \"last_four_digits\":\"last_four_digits\"," 52 " \"expiration_month\":12," 53 " \"expiration_year\":2012," 54 " \"billing_address\":" 55 " {" 56 " \"name\":\"name\"," 57 " \"address1\":\"address1\"," 58 " \"address2\":\"address2\"," 59 " \"city\":\"city\"," 60 " \"dependent_locality_name\":\"burough\"," 61 " \"state\":\"state\"," 62 " \"postal_code\":\"postal_code\"," 63 " \"sorting_code\":\"sorting_code\"," 64 " \"phone_number\":\"phone_number\"," 65 " \"country_code\":\"US\"," 66 " \"language_code\":\"language_code\"" 67 " }," 68 " \"object_id\":\"object_id\"," 69 " \"amex_disallowed\":true" 70 "}"; 71 72 const char kMaskedInstrumentMissingType[] = 73 "{" 74 " \"descriptive_name\":\"descriptive_name\"," 75 " \"last_four_digits\":\"last_four_digits\"," 76 " \"expiration_month\":12," 77 " \"expiration_year\":2012," 78 " \"billing_address\":" 79 " {" 80 " \"name\":\"name\"," 81 " \"address1\":\"address1\"," 82 " \"address2\":\"address2\"," 83 " \"city\":\"city\"," 84 " \"dependent_locality_name\":\"burough\"," 85 " \"state\":\"state\"," 86 " \"postal_code\":\"postal_code\"," 87 " \"sorting_code\":\"sorting_code\"," 88 " \"phone_number\":\"phone_number\"," 89 " \"country_code\":\"US\"," 90 " \"language_code\":\"language_code\"" 91 " }," 92 " \"status\":\"VALID\"," 93 " \"object_id\":\"object_id\"" 94 "}"; 95 96 const char kMaskedInstrumentMissingLastFourDigits[] = 97 "{" 98 " \"descriptive_name\":\"descriptive_name\"," 99 " \"type\":\"VISA\"," 100 " \"expiration_month\":12," 101 " \"expiration_year\":2012," 102 " \"billing_address\":" 103 " {" 104 " \"name\":\"name\"," 105 " \"address1\":\"address1\"," 106 " \"address2\":\"address2\"," 107 " \"city\":\"city\"," 108 " \"dependent_locality_name\":\"burough\"," 109 " \"state\":\"state\"," 110 " \"postal_code\":\"postal_code\"," 111 " \"sorting_code\":\"sorting_code\"," 112 " \"phone_number\":\"phone_number\"," 113 " \"country_code\":\"US\"," 114 " \"language_code\":\"language_code\"" 115 " }," 116 " \"status\":\"VALID\"," 117 " \"object_id\":\"object_id\"" 118 "}"; 119 120 const char kMaskedInstrumentMissingAddress[] = 121 "{" 122 " \"descriptive_name\":\"descriptive_name\"," 123 " \"type\":\"VISA\"," 124 " \"last_four_digits\":\"last_four_digits\"," 125 " \"expiration_month\":12," 126 " \"expiration_year\":2012," 127 " \"status\":\"VALID\"," 128 " \"object_id\":\"object_id\"" 129 "}"; 130 131 const char kMaskedInstrumentMalformedAddress[] = 132 "{" 133 " \"descriptive_name\":\"descriptive_name\"," 134 " \"type\":\"VISA\"," 135 " \"last_four_digits\":\"last_four_digits\"," 136 " \"expiration_month\":12," 137 " \"expiration_year\":2012," 138 " \"billing_address\":" 139 " {" 140 " \"address1\":\"address1\"," 141 " \"address2\":\"address2\"," 142 " \"city\":\"city\"," 143 " \"dependent_locality_name\":\"burough\"," 144 " \"state\":\"state\"," 145 " \"phone_number\":\"phone_number\"," 146 " \"country_code\":\"US\"" 147 " }," 148 " \"status\":\"VALID\"," 149 " \"object_id\":\"object_id\"" 150 "}"; 151 152 const char kMaskedInstrumentMissingObjectId[] = 153 "{" 154 " \"descriptive_name\":\"descriptive_name\"," 155 " \"type\":\"VISA\"," 156 " \"last_four_digits\":\"last_four_digits\"," 157 " \"expiration_month\":12," 158 " \"expiration_year\":2012," 159 " \"billing_address\":" 160 " {" 161 " \"name\":\"name\"," 162 " \"address1\":\"address1\"," 163 " \"address2\":\"address2\"," 164 " \"city\":\"city\"," 165 " \"dependent_locality_name\":\"burough\"," 166 " \"state\":\"state\"," 167 " \"postal_code\":\"postal_code\"," 168 " \"phone_number\":\"phone_number\"," 169 " \"country_code\":\"US\"," 170 " \"language_code\":\"language_code\"" 171 " }," 172 " \"status\":\"VALID\"" 173 "}"; 174 175 const char kLegalDocument[] = 176 "{" 177 " \"legal_document_id\":\"doc_id\"," 178 " \"display_name\":\"display_name\"" 179 "}"; 180 181 const char kLegalDocumentMissingDocumentId[] = 182 "{" 183 " \"display_name\":\"display_name\"" 184 "}"; 185 186 const char kLegalDocumentMissingDisplayName[] = 187 "{" 188 " \"legal_document_id\":\"doc_id\"" 189 "}"; 190 191 const char kWalletItemsWithRequiredActions[] = 192 "{" 193 " \"required_action\":" 194 " [" 195 " \" setup_wallet\"," 196 " \" CHOOse_ANother_INSTRUMENT_OR_ADDRESS\"," 197 " \"AcCePt_ToS \"," 198 " \" \\tGAIA_auth \\n\\r\"," 199 " \"UPDATE_expiration_date\"," 200 " \"UPGRADE_min_ADDRESS \"," 201 " \" pAsSiVe_GAIA_auth \"," 202 " \" REQUIRE_PHONE_NUMBER\\t \"" 203 " ]" 204 "}"; 205 206 const char kWalletItemsWithInvalidRequiredActions[] = 207 "{" 208 " \"required_action\":" 209 " [" 210 " \"verify_CVV\"," 211 " \"invalid_FORM_FIELD\"," 212 " \" \"" 213 " ]" 214 "}"; 215 216 const char kWalletItemsMissingGoogleTransactionId[] = 217 "{" 218 " \"required_action\":" 219 " [" 220 " ]," 221 " \"instrument\":" 222 " [" 223 " {" 224 " \"descriptive_name\":\"descriptive_name\"," 225 " \"type\":\"VISA\"," 226 " \"last_four_digits\":\"last_four_digits\"," 227 " \"expiration_month\":12," 228 " \"expiration_year\":2012," 229 " \"billing_address\":" 230 " {" 231 " \"name\":\"name\"," 232 " \"address1\":\"address1\"," 233 " \"address2\":\"address2\"," 234 " \"city\":\"city\"," 235 " \"state\":\"state\"," 236 " \"postal_code\":\"postal_code\"," 237 " \"phone_number\":\"phone_number\"," 238 " \"country_code\":\"US\"," 239 " \"language_code\":\"language_code\"" 240 " }," 241 " \"status\":\"VALID\"," 242 " \"object_id\":\"object_id\"" 243 " }" 244 " ]," 245 " \"default_instrument_id\":\"default_instrument_id\"," 246 " \"address\":" 247 " [" 248 " {" 249 " \"id\":\"id\"," 250 " \"phone_number\":\"phone_number\"," 251 " \"postal_address\":" 252 " {" 253 " \"recipient_name\":\"recipient_name\"," 254 " \"address_line\":" 255 " [" 256 " \"address_line_1\"," 257 " \"address_line_2\"" 258 " ]," 259 " \"locality_name\":\"locality_name\"," 260 " \"administrative_area_name\":\"administrative_area_name\"," 261 " \"postal_code_number\":\"postal_code_number\"," 262 " \"country_name_code\":\"US\"," 263 " \"language_code\":\"language_code\"" 264 " }" 265 " }" 266 " ]," 267 " \"default_address_id\":\"default_address_id\"," 268 " \"amex_disallowed\":true," 269 " \"required_legal_document\":" 270 " [" 271 " {" 272 " \"legal_document_id\":\"doc_id\"," 273 " \"display_name\":\"display_name\"" 274 " }" 275 " ]" 276 "}"; 277 278 const char kWalletItems[] = 279 "{" 280 " \"required_action\":" 281 " [" 282 " ]," 283 " \"google_transaction_id\":\"google_transaction_id\"," 284 " \"instrument\":" 285 " [" 286 " {" 287 " \"descriptive_name\":\"descriptive_name\"," 288 " \"type\":\"VISA\"," 289 " \"last_four_digits\":\"last_four_digits\"," 290 " \"expiration_month\":12," 291 " \"expiration_year\":2012," 292 " \"billing_address\":" 293 " {" 294 " \"name\":\"name\"," 295 " \"address1\":\"address1\"," 296 " \"address2\":\"address2\"," 297 " \"city\":\"city\"," 298 " \"dependent_locality_name\":\"burough\"," 299 " \"state\":\"state\"," 300 " \"postal_code\":\"postal_code\"," 301 " \"sorting_code\":\"sorting_code\"," 302 " \"phone_number\":\"phone_number\"," 303 " \"country_code\":\"US\"," 304 " \"type\":\"FULL\"," 305 " \"language_code\":\"language_code\"" 306 " }," 307 " \"status\":\"VALID\"," 308 " \"object_id\":\"object_id\"" 309 " }" 310 " ]," 311 " \"default_instrument_id\":\"default_instrument_id\"," 312 " \"address\":" 313 " [" 314 " {" 315 " \"id\":\"id\"," 316 " \"phone_number\":\"phone_number\"," 317 " \"postal_address\":" 318 " {" 319 " \"recipient_name\":\"recipient_name\"," 320 " \"address_line\":" 321 " [" 322 " \"address_line_1\"," 323 " \"address_line_2\"" 324 " ]," 325 " \"locality_name\":\"locality_name\"," 326 " \"dependent_locality_name\":\"dependent_locality_name\"," 327 " \"administrative_area_name\":\"administrative_area_name\"," 328 " \"postal_code_number\":\"postal_code_number\"," 329 " \"sorting_code\":\"sorting_code\"," 330 " \"country_name_code\":\"US\"," 331 " \"language_code\":\"language_code\"" 332 " }" 333 " }" 334 " ]," 335 " \"default_address_id\":\"default_address_id\"," 336 " \"obfuscated_gaia_id\":\"ignore_this_value\"," 337 " \"amex_disallowed\":true," 338 " \"gaia_profile\":" 339 " [" 340 " {" 341 " \"buyer_email\":\"user (at) chromium.org\"," 342 " \"gaia_index\":0," 343 " \"gaia_id\":\"123456789\"," 344 " \"buyer_name\":\"Joe Usecase\"," 345 " \"is_active\":true," 346 " \"avatar_url_27x27\":\"https://lh3.googleusercontent.com/27.jpg\"," 347 " \"avatar_url_54x54\":\"https://lh3.googleusercontent.com/54.jpg\"," 348 " \"avatar_url_48x48\":\"https://lh3.googleusercontent.com/48.jpg\"," 349 " \"avatar_url_96x96\":\"https://lh3.googleusercontent.com/96.jpg\"" 350 " }," 351 " {" 352 " \"buyer_email\":\"user2 (at) chromium.org\"," 353 " \"gaia_index\":1," 354 " \"gaia_id\":\"obfuscated_gaia_id\"," 355 " \"buyer_name\":\"Jill Usecase\"," 356 " \"is_active\":false," 357 " \"avatar_url_27x27\":\"https://lh3.googleusercontent.com/27.jpg\"," 358 " \"avatar_url_54x54\":\"https://lh3.googleusercontent.com/54.jpg\"," 359 " \"avatar_url_48x48\":\"https://lh3.googleusercontent.com/48.jpg\"," 360 " \"avatar_url_96x96\":\"https://lh3.googleusercontent.com/96.jpg\"" 361 " }" 362 " ]"; 363 364 const char kRequiredLegalDocument[] = 365 " ," 366 " \"required_legal_document\":" 367 " [" 368 " {" 369 " \"legal_document_id\":\"doc_id\"," 370 " \"display_name\":\"display_name\"" 371 " }" 372 " ]"; 373 374 const char kCloseJson[] = "}"; 375 376 } // anonymous namespace 377 378 namespace autofill { 379 namespace wallet { 380 381 class WalletItemsTest : public testing::Test { 382 public: 383 WalletItemsTest() {} 384 protected: 385 void SetUpDictionary(const std::string& json) { 386 scoped_ptr<base::Value> value(base::JSONReader::Read(json)); 387 ASSERT_TRUE(value.get()); 388 ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); 389 dict.reset(static_cast<base::DictionaryValue*>(value.release())); 390 } 391 scoped_ptr<base::DictionaryValue> dict; 392 }; 393 394 TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingStatus) { 395 SetUpDictionary(kMaskedInstrumentMissingStatus); 396 EXPECT_EQ(NULL, 397 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 398 } 399 400 TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingType) { 401 SetUpDictionary(kMaskedInstrumentMissingType); 402 EXPECT_EQ(NULL, 403 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 404 } 405 406 TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingLastFourDigits) { 407 SetUpDictionary(kMaskedInstrumentMissingLastFourDigits); 408 EXPECT_EQ(NULL, 409 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 410 } 411 412 TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingAddress) { 413 SetUpDictionary(kMaskedInstrumentMissingAddress); 414 EXPECT_EQ(NULL, 415 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 416 } 417 418 TEST_F(WalletItemsTest, CreateMaskedInstrumentMalformedAddress) { 419 SetUpDictionary(kMaskedInstrumentMalformedAddress); 420 EXPECT_EQ(NULL, 421 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 422 } 423 424 TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingObjectId) { 425 SetUpDictionary(kMaskedInstrumentMissingObjectId); 426 EXPECT_EQ(NULL, 427 WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get()); 428 } 429 430 TEST_F(WalletItemsTest, CreateMaskedInstrument) { 431 SetUpDictionary(kMaskedInstrument); 432 scoped_ptr<Address> address( 433 new Address("US", 434 ASCIIToUTF16("name"), 435 StreetAddress("address1", "address2"), 436 ASCIIToUTF16("city"), 437 ASCIIToUTF16("burough"), 438 ASCIIToUTF16("state"), 439 ASCIIToUTF16("postal_code"), 440 ASCIIToUTF16("sorting_code"), 441 ASCIIToUTF16("phone_number"), 442 std::string(), 443 "language_code")); 444 WalletItems::MaskedInstrument masked_instrument( 445 ASCIIToUTF16("descriptive_name"), 446 WalletItems::MaskedInstrument::VISA, 447 ASCIIToUTF16("last_four_digits"), 448 12, 449 2012, 450 address.Pass(), 451 WalletItems::MaskedInstrument::VALID, 452 "object_id"); 453 EXPECT_EQ(masked_instrument, 454 *WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict)); 455 } 456 457 TEST_F(WalletItemsTest, CreateLegalDocumentMissingDocId) { 458 SetUpDictionary(kLegalDocumentMissingDocumentId); 459 EXPECT_EQ(NULL, WalletItems::LegalDocument::CreateLegalDocument(*dict).get()); 460 } 461 462 TEST_F(WalletItemsTest, CreateLegalDocumentMissingDisplayName) { 463 SetUpDictionary(kLegalDocumentMissingDisplayName); 464 EXPECT_EQ(NULL, WalletItems::LegalDocument::CreateLegalDocument(*dict).get()); 465 } 466 467 TEST_F(WalletItemsTest, CreateLegalDocument) { 468 SetUpDictionary(kLegalDocument); 469 WalletItems::LegalDocument expected("doc_id", ASCIIToUTF16("display_name")); 470 EXPECT_EQ(expected, 471 *WalletItems::LegalDocument::CreateLegalDocument(*dict)); 472 } 473 474 TEST_F(WalletItemsTest, LegalDocumentUrl) { 475 WalletItems::LegalDocument legal_doc("doc_id", ASCIIToUTF16("display_name")); 476 EXPECT_EQ("https://wallet.google.com/legaldocument?docId=doc_id", 477 legal_doc.url().spec()); 478 } 479 480 TEST_F(WalletItemsTest, LegalDocumentEmptyId) { 481 WalletItems::LegalDocument legal_doc(GURL("http://example.com"), 482 ASCIIToUTF16("display_name")); 483 EXPECT_TRUE(legal_doc.id().empty()); 484 } 485 486 TEST_F(WalletItemsTest, CreateWalletItemsWithRequiredActions) { 487 SetUpDictionary(kWalletItemsWithRequiredActions); 488 489 std::vector<RequiredAction> required_actions; 490 required_actions.push_back(SETUP_WALLET); 491 required_actions.push_back(CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS); 492 required_actions.push_back(ACCEPT_TOS); 493 required_actions.push_back(GAIA_AUTH); 494 required_actions.push_back(UPDATE_EXPIRATION_DATE); 495 required_actions.push_back(UPGRADE_MIN_ADDRESS); 496 required_actions.push_back(PASSIVE_GAIA_AUTH); 497 required_actions.push_back(REQUIRE_PHONE_NUMBER); 498 499 WalletItems expected(required_actions, 500 std::string(), 501 std::string(), 502 std::string(), 503 AMEX_DISALLOWED); 504 EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict)); 505 506 ASSERT_FALSE(required_actions.empty()); 507 required_actions.pop_back(); 508 WalletItems different_required_actions(required_actions, 509 std::string(), 510 std::string(), 511 std::string(), 512 AMEX_DISALLOWED); 513 EXPECT_NE(expected, different_required_actions); 514 } 515 516 TEST_F(WalletItemsTest, CreateWalletItemsWithInvalidRequiredActions) { 517 SetUpDictionary(kWalletItemsWithInvalidRequiredActions); 518 EXPECT_EQ(NULL, WalletItems::CreateWalletItems(*dict).get()); 519 } 520 521 TEST_F(WalletItemsTest, CreateWalletItemsMissingGoogleTransactionId) { 522 SetUpDictionary(kWalletItemsMissingGoogleTransactionId); 523 EXPECT_EQ(NULL, WalletItems::CreateWalletItems(*dict).get()); 524 } 525 526 TEST_F(WalletItemsTest, CreateWalletItemsMissingAmexDisallowed) { 527 SetUpDictionary(std::string(kWalletItems) + std::string(kCloseJson)); 528 EXPECT_TRUE(dict->Remove("amex_disallowed", NULL)); 529 base::string16 amex_number = ASCIIToUTF16("378282246310005"); 530 base::string16 message; 531 EXPECT_FALSE(WalletItems::CreateWalletItems(*dict)->SupportsCard(amex_number, 532 &message)); 533 EXPECT_FALSE(message.empty()); 534 } 535 536 TEST_F(WalletItemsTest, CreateWalletItems) { 537 SetUpDictionary(std::string(kWalletItems) + std::string(kCloseJson)); 538 std::vector<RequiredAction> required_actions; 539 WalletItems expected(required_actions, 540 "google_transaction_id", 541 "default_instrument_id", 542 "default_address_id", 543 AMEX_DISALLOWED); 544 545 scoped_ptr<GaiaAccount> user1(GaiaAccount::CreateForTesting( 546 "user (at) chromium.org", 547 "123456789", 548 0, 549 true)); 550 expected.AddAccount(user1.Pass()); 551 scoped_ptr<GaiaAccount> user2(GaiaAccount::CreateForTesting( 552 "user2 (at) chromium.org", 553 "obfuscated_gaia_id", 554 1, 555 false)); 556 expected.AddAccount(user2.Pass()); 557 EXPECT_EQ("123456789", expected.ObfuscatedGaiaId()); 558 559 scoped_ptr<Address> billing_address( 560 new Address("US", 561 ASCIIToUTF16("name"), 562 StreetAddress("address1", "address2"), 563 ASCIIToUTF16("city"), 564 ASCIIToUTF16("burough"), 565 ASCIIToUTF16("state"), 566 ASCIIToUTF16("postal_code"), 567 ASCIIToUTF16("sorting_code"), 568 ASCIIToUTF16("phone_number"), 569 std::string(), 570 "language_code")); 571 scoped_ptr<WalletItems::MaskedInstrument> masked_instrument( 572 new WalletItems::MaskedInstrument(ASCIIToUTF16("descriptive_name"), 573 WalletItems::MaskedInstrument::VISA, 574 ASCIIToUTF16("last_four_digits"), 575 12, 576 2012, 577 billing_address.Pass(), 578 WalletItems::MaskedInstrument::VALID, 579 "object_id")); 580 expected.AddInstrument(masked_instrument.Pass()); 581 582 scoped_ptr<Address> shipping_address( 583 new Address("US", 584 ASCIIToUTF16("recipient_name"), 585 StreetAddress("address_line_1", "address_line_2"), 586 ASCIIToUTF16("locality_name"), 587 ASCIIToUTF16("dependent_locality_name"), 588 ASCIIToUTF16("administrative_area_name"), 589 ASCIIToUTF16("postal_code_number"), 590 ASCIIToUTF16("sorting_code"), 591 ASCIIToUTF16("phone_number"), 592 "id", 593 "language_code")); 594 expected.AddAddress(shipping_address.Pass()); 595 EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict)); 596 597 // Now try with a legal document as well. 598 SetUpDictionary(std::string(kWalletItems) + 599 std::string(kRequiredLegalDocument) + 600 std::string(kCloseJson)); 601 scoped_ptr<WalletItems::LegalDocument> legal_document( 602 new WalletItems::LegalDocument("doc_id", 603 ASCIIToUTF16("display_name"))); 604 expected.AddLegalDocument(legal_document.Pass()); 605 expected.AddLegalDocument( 606 WalletItems::LegalDocument::CreatePrivacyPolicyDocument()); 607 608 EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict)); 609 } 610 611 } // namespace wallet 612 } // namespace autofill 613