diff options
author | jdonnelly <jdonnelly@chromium.org> | 2015-11-10 13:45:38 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-10 21:46:45 +0000 |
commit | 171e51e7a69886d993b1257d89a969524b30efff (patch) | |
tree | 7f369711656bb816e3ec20daadae5032a0ce5199 | |
parent | cf4914733abd70c90a3396c394ff4fad7ac55bb2 (diff) | |
download | chromium_src-171e51e7a69886d993b1257d89a969524b30efff.zip chromium_src-171e51e7a69886d993b1257d89a969524b30efff.tar.gz chromium_src-171e51e7a69886d993b1257d89a969524b30efff.tar.bz2 |
Add credit card upstreaming.
In support, also:
- Rename RealPanWalletClient to PaymentsClient and add the new upstreaming RPCs. Redesign the logic here to support multiple types of requests.
- Move risk data generation from CardUnmaskPromptControllerImpl to AutofillManager. Here it can be used for both unmasking and uploading.
BUG=535784
Review URL: https://codereview.chromium.org/1412073004
Cr-Commit-Position: refs/heads/master@{#358913}
35 files changed, 1338 insertions, 651 deletions
diff --git a/android_webview/native/aw_autofill_client.cc b/android_webview/native/aw_autofill_client.cc index 2ac1b40..2b2c2667 100644 --- a/android_webview/native/aw_autofill_client.cc +++ b/android_webview/native/aw_autofill_client.cc @@ -217,12 +217,23 @@ void AwAutofillClient::ShowUnmaskPrompt( NOTIMPLEMENTED(); } -void AwAutofillClient::OnUnmaskVerificationResult(GetRealPanResult result) { +void AwAutofillClient::OnUnmaskVerificationResult(PaymentsRpcResult result) { NOTIMPLEMENTED(); } -void AwAutofillClient::ConfirmSaveCreditCard( - const base::Closure& save_card_callback) { +void AwAutofillClient::ConfirmSaveCreditCardLocally( + const base::Closure& callback) { + NOTIMPLEMENTED(); +} + +void AwAutofillClient::ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) { + NOTIMPLEMENTED(); +} + +void AwAutofillClient::LoadRiskData( + const base::Callback<void(const std::string&)>& callback) { NOTIMPLEMENTED(); } diff --git a/android_webview/native/aw_autofill_client.h b/android_webview/native/aw_autofill_client.h index a884949..c869ddb 100644 --- a/android_webview/native/aw_autofill_client.h +++ b/android_webview/native/aw_autofill_client.h @@ -66,8 +66,13 @@ class AwAutofillClient : public autofill::AutofillClient, void ShowUnmaskPrompt( const autofill::CreditCard& card, base::WeakPtr<autofill::CardUnmaskDelegate> delegate) override; - void OnUnmaskVerificationResult(GetRealPanResult result) override; - void ConfirmSaveCreditCard(const base::Closure& save_card_callback) override; + void OnUnmaskVerificationResult(PaymentsRpcResult result) override; + void ConfirmSaveCreditCardLocally(const base::Closure& callback) override; + void ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) override; + void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(const CreditCardScanCallback& callback) override; void ShowRequestAutocompleteDialog(const autofill::FormData& form, diff --git a/chrome/browser/ui/autofill/card_unmask_prompt_view_browsertest.cc b/chrome/browser/ui/autofill/card_unmask_prompt_view_browsertest.cc index e9e4ce2..cd4c294 100644 --- a/chrome/browser/ui/autofill/card_unmask_prompt_view_browsertest.cc +++ b/chrome/browser/ui/autofill/card_unmask_prompt_view_browsertest.cc @@ -6,7 +6,6 @@ #include "base/guid.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/autofill/risk_util.h" #include "chrome/browser/ui/autofill/card_unmask_prompt_view_tester.h" #include "chrome/browser/ui/autofill/create_card_unmask_prompt_view.h" #include "chrome/browser/ui/browser.h" @@ -55,8 +54,9 @@ class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl { TestCardUnmaskPromptController( content::WebContents* contents, scoped_refptr<content::MessageLoopRunner> runner) - : CardUnmaskPromptControllerImpl(base::Bind(&LoadRiskData, 0, contents), - user_prefs::UserPrefs::Get(contents->GetBrowserContext()), false), + : CardUnmaskPromptControllerImpl( + user_prefs::UserPrefs::Get(contents->GetBrowserContext()), + false), runner_(runner), weak_factory_(this) {} @@ -65,10 +65,6 @@ class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl { return base::TimeDelta::FromMilliseconds(10); } - void LoadRiskFingerprint() override { - OnDidLoadRiskFingerprint("risk_data"); - } - base::WeakPtr<TestCardUnmaskPromptController> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc index 3288d5a..26a8c50 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.cc +++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc @@ -54,7 +54,6 @@ namespace autofill { ChromeAutofillClient::ChromeAutofillClient(content::WebContents* web_contents) : content::WebContentsObserver(web_contents), unmask_controller_( - base::Bind(&LoadRiskData, 0, web_contents), user_prefs::UserPrefs::Get(web_contents->GetBrowserContext()), Profile::FromBrowserContext(web_contents->GetBrowserContext()) ->IsOffTheRecord()), @@ -148,27 +147,39 @@ void ChromeAutofillClient::ShowUnmaskPrompt( card, delegate); } -void ChromeAutofillClient::OnUnmaskVerificationResult(GetRealPanResult result) { +void ChromeAutofillClient::OnUnmaskVerificationResult( + PaymentsRpcResult result) { unmask_controller_.OnVerificationResult(result); } -void ChromeAutofillClient::ConfirmSaveCreditCard( - const base::Closure& save_card_callback) { +void ChromeAutofillClient::ConfirmSaveCreditCardLocally( + const base::Closure& callback) { // TODO(bondd): Implement save card bubble for OS_MACOSX. #if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX) // Do lazy initialization of SaveCardBubbleControllerImpl. autofill::SaveCardBubbleControllerImpl::CreateForWebContents(web_contents()); autofill::SaveCardBubbleControllerImpl* controller = autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents()); - controller->SetCallback(save_card_callback); + controller->SetCallback(callback); controller->ShowBubble(); #else AutofillCCInfoBarDelegate::Create( - InfoBarService::FromWebContents(web_contents()), this, - save_card_callback); + InfoBarService::FromWebContents(web_contents()), this, callback); #endif } +void ChromeAutofillClient::ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) { + // TODO(bondd): Implement upload UI. + ConfirmSaveCreditCardLocally(callback); +} + +void ChromeAutofillClient::LoadRiskData( + const base::Callback<void(const std::string&)>& callback) { + ::autofill::LoadRiskData(0, web_contents(), callback); +} + bool ChromeAutofillClient::HasCreditCardScanFeature() { return CreditCardScannerController::HasCreditCardScanFeature(); } diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h index 306259a..271678e 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.h +++ b/chrome/browser/ui/autofill/chrome_autofill_client.h @@ -53,8 +53,13 @@ class ChromeAutofillClient void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, base::WeakPtr<CardUnmaskDelegate> delegate) override; - void OnUnmaskVerificationResult(GetRealPanResult result) override; - void ConfirmSaveCreditCard(const base::Closure& save_card_callback) override; + void OnUnmaskVerificationResult(PaymentsRpcResult result) override; + void ConfirmSaveCreditCardLocally(const base::Closure& callback) override; + void ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) override; + void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(const CreditCardScanCallback& callback) override; void ShowRequestAutocompleteDialog( @@ -112,7 +117,7 @@ class ChromeAutofillClient // The last render frame that called requestAutocomplete. content::RenderFrameHost* last_rfh_to_rac_; - // The identity provider, used for Wallet integration. + // The identity provider, used for Payments integration. scoped_ptr<IdentityProvider> identity_provider_; DISALLOW_COPY_AND_ASSIGN(ChromeAutofillClient); diff --git a/components/autofill.gypi b/components/autofill.gypi index f30eb9d..19ff103 100644 --- a/components/autofill.gypi +++ b/components/autofill.gypi @@ -175,6 +175,9 @@ 'autofill/core/browser/options_util.h', 'autofill/core/browser/password_generator.cc', 'autofill/core/browser/password_generator.h', + 'autofill/core/browser/payments/payments_client.cc', + 'autofill/core/browser/payments/payments_client.h', + 'autofill/core/browser/payments/payments_request.h', 'autofill/core/browser/personal_data_manager.cc', 'autofill/core/browser/personal_data_manager.h', 'autofill/core/browser/personal_data_manager_observer.h', @@ -197,8 +200,6 @@ 'autofill/core/browser/ui/card_unmask_prompt_view.h', 'autofill/core/browser/validation.cc', 'autofill/core/browser/validation.h', - 'autofill/core/browser/wallet/real_pan_wallet_client.cc', - 'autofill/core/browser/wallet/real_pan_wallet_client.h', 'autofill/core/browser/webdata/autocomplete_syncable_service.cc', 'autofill/core/browser/webdata/autocomplete_syncable_service.h', 'autofill/core/browser/webdata/autofill_change.cc', diff --git a/components/autofill/content/browser/BUILD.gn b/components/autofill/content/browser/BUILD.gn index 02871f7..36f9760 100644 --- a/components/autofill/content/browser/BUILD.gn +++ b/components/autofill/content/browser/BUILD.gn @@ -70,7 +70,7 @@ source_set("unit_tests") { "content_autofill_driver_unittest.cc", "request_autocomplete_manager_unittest.cc", "wallet/full_wallet_unittest.cc", - "wallet/real_pan_wallet_client_unittest.cc", + "wallet/payments_client_unittest.cc", "wallet/wallet_address_unittest.cc", "wallet/wallet_service_url_unittest.cc", ] diff --git a/components/autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc b/components/autofill/content/browser/wallet/payments_client_unittest.cc index 4bbc78c..f761e99 100644 --- a/components/autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc +++ b/components/autofill/content/browser/wallet/payments_client_unittest.cc @@ -4,8 +4,9 @@ #include "base/command_line.h" #include "base/thread_task_runner_handle.h" +#include "base/values.h" #include "components/autofill/core/browser/autofill_test_utils.h" -#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h" +#include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/common/autofill_switches.h" #include "content/public/test/test_browser_thread_bundle.h" #include "google_apis/gaia/fake_identity_provider.h" @@ -15,54 +16,87 @@ #include "testing/gtest/include/gtest/gtest.h" namespace autofill { -namespace wallet { +namespace payments { -class RealPanWalletClientTest : public testing::Test, - public RealPanWalletClient::Delegate { +class PaymentsClientTest : public testing::Test, public PaymentsClientDelegate { public: - RealPanWalletClientTest() : result_(AutofillClient::SUCCESS) {} - ~RealPanWalletClientTest() override {} + PaymentsClientTest() : result_(AutofillClient::NONE) {} + ~PaymentsClientTest() override {} void SetUp() override { - // Silence the warning for mismatching sync and wallet servers. + // Silence the warning for mismatching sync and Payments servers. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kWalletServiceUseSandbox, "0"); + result_ = AutofillClient::NONE; + real_pan_.clear(); + legal_message_.reset(); + request_context_ = new net::TestURLRequestContextGetter( base::ThreadTaskRunnerHandle::Get()); token_service_.reset(new FakeOAuth2TokenService()); identity_provider_.reset(new FakeIdentityProvider(token_service_.get())); - client_.reset(new RealPanWalletClient(request_context_.get(), this)); + client_.reset(new PaymentsClient(request_context_.get(), this)); } void TearDown() override { client_.reset(); } - // RealPanWalletClient::Delegate + // PaymentsClientDelegate IdentityProvider* GetIdentityProvider() override { return identity_provider_.get(); } - void OnDidGetRealPan(AutofillClient::GetRealPanResult result, + void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, const std::string& real_pan) override { result_ = result; real_pan_ = real_pan; } + void OnDidGetUploadDetails( + AutofillClient::PaymentsRpcResult result, + const base::string16& context_token, + scoped_ptr<base::DictionaryValue> legal_message) override { + result_ = result; + legal_message_ = legal_message.Pass(); + } + + void OnDidUploadCard(AutofillClient::PaymentsRpcResult result) override { + result_ = result; + } + protected: void StartUnmasking() { token_service_->AddAccount("example@gmail.com"); identity_provider_->LogIn("example@gmail.com"); - CreditCard card = test::GetMaskedServerCard(); - CardUnmaskDelegate::UnmaskResponse response; - response.cvc = base::ASCIIToUTF16("123"); - client_->UnmaskCard(card, response); + PaymentsClient::UnmaskRequestDetails request_details; + request_details.card = test::GetMaskedServerCard(); + request_details.user_response.cvc = base::ASCIIToUTF16("123"); + request_details.risk_data = "some risk data"; + client_->UnmaskCard(request_details); + } + + void StartGettingUploadDetails() { + token_service_->AddAccount("example@gmail.com"); + identity_provider_->LogIn("example@gmail.com"); + client_->GetUploadDetails("language-LOCALE"); + } + + void StartUploading() { + token_service_->AddAccount("example@gmail.com"); + identity_provider_->LogIn("example@gmail.com"); + PaymentsClient::UploadRequestDetails request_details; + request_details.card = test::GetCreditCard(); + request_details.cvc = base::ASCIIToUTF16("123"); + request_details.context_token = base::ASCIIToUTF16("context token"); + request_details.risk_data = "some risk data"; + request_details.app_locale = "language-LOCALE"; + client_->UploadCard(request_details); } void IssueOAuthToken() { token_service_->IssueAllTokensForAccount( - "example@gmail.com", - "totally_real_token", + "example@gmail.com", "totally_real_token", base::Time::Now() + base::TimeDelta::FromDays(10)); // Verify the auth header. @@ -71,8 +105,8 @@ class RealPanWalletClientTest : public testing::Test, fetcher->GetExtraRequestHeaders(&request_headers); std::string auth_header_value; EXPECT_TRUE(request_headers.GetHeader( - net::HttpRequestHeaders::kAuthorization, - &auth_header_value)) << request_headers.ToString(); + net::HttpRequestHeaders::kAuthorization, &auth_header_value)) + << request_headers.ToString(); EXPECT_EQ("Bearer totally_real_token", auth_header_value); } @@ -85,21 +119,22 @@ class RealPanWalletClientTest : public testing::Test, fetcher->delegate()->OnURLFetchComplete(fetcher); } - AutofillClient::GetRealPanResult result_; + AutofillClient::PaymentsRpcResult result_; std::string real_pan_; + scoped_ptr<base::DictionaryValue> legal_message_; content::TestBrowserThreadBundle thread_bundle_; net::TestURLFetcherFactory factory_; scoped_refptr<net::TestURLRequestContextGetter> request_context_; scoped_ptr<FakeOAuth2TokenService> token_service_; scoped_ptr<FakeIdentityProvider> identity_provider_; - scoped_ptr<RealPanWalletClient> client_; + scoped_ptr<PaymentsClient> client_; private: - DISALLOW_COPY_AND_ASSIGN(RealPanWalletClientTest); + DISALLOW_COPY_AND_ASSIGN(PaymentsClientTest); }; -TEST_F(RealPanWalletClientTest, OAuthError) { +TEST_F(PaymentsClientTest, OAuthError) { StartUnmasking(); token_service_->IssueErrorForAllPendingRequestsForAccount( "example@gmail.com", @@ -108,7 +143,7 @@ TEST_F(RealPanWalletClientTest, OAuthError) { EXPECT_TRUE(real_pan_.empty()); } -TEST_F(RealPanWalletClientTest, Success) { +TEST_F(PaymentsClientTest, UnmaskSuccess) { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }"); @@ -116,7 +151,57 @@ TEST_F(RealPanWalletClientTest, Success) { EXPECT_EQ("1234", real_pan_); } -TEST_F(RealPanWalletClientTest, RetryFailure) { +TEST_F(PaymentsClientTest, GetDetailsSuccess) { + StartGettingUploadDetails(); + ReturnResponse( + net::HTTP_OK, + "{ \"context_token\": \"some_token\", \"legal_message\": {} }"); + EXPECT_EQ(AutofillClient::SUCCESS, result_); + EXPECT_NE(nullptr, legal_message_.get()); +} + +TEST_F(PaymentsClientTest, UploadSuccess) { + StartUploading(); + IssueOAuthToken(); + ReturnResponse(net::HTTP_OK, "{}"); + EXPECT_EQ(AutofillClient::SUCCESS, result_); +} + +TEST_F(PaymentsClientTest, GetDetailsFollowedByUploadSuccess) { + StartGettingUploadDetails(); + ReturnResponse( + net::HTTP_OK, + "{ \"context_token\": \"some_token\", \"legal_message\": {} }"); + EXPECT_EQ(AutofillClient::SUCCESS, result_); + + result_ = AutofillClient::NONE; + + StartUploading(); + IssueOAuthToken(); + ReturnResponse(net::HTTP_OK, "{}"); + EXPECT_EQ(AutofillClient::SUCCESS, result_); +} + +TEST_F(PaymentsClientTest, UnmaskMissingPan) { + StartUnmasking(); + ReturnResponse(net::HTTP_OK, "{}"); + EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_); +} + +TEST_F(PaymentsClientTest, GetDetailsMissingContextToken) { + StartGettingUploadDetails(); + ReturnResponse(net::HTTP_OK, "{ \"legal_message\": {} }"); + EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_); +} + +TEST_F(PaymentsClientTest, GetDetailsMissingLegalMessage) { + StartGettingUploadDetails(); + ReturnResponse(net::HTTP_OK, "{ \"context_token\": \"some_token\" }"); + EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_); + EXPECT_EQ(nullptr, legal_message_.get()); +} + +TEST_F(PaymentsClientTest, RetryFailure) { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_OK, "{ \"error\": { \"code\": \"INTERNAL\" } }"); @@ -124,31 +209,30 @@ TEST_F(RealPanWalletClientTest, RetryFailure) { EXPECT_EQ("", real_pan_); } -TEST_F(RealPanWalletClientTest, PermanentFailure) { +TEST_F(PaymentsClientTest, PermanentFailure) { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_OK, - "{ \"error\": { \"code\": \"ANYTHING_ELSE\" } }"); + "{ \"error\": { \"code\": \"ANYTHING_ELSE\" } }"); EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_); EXPECT_EQ("", real_pan_); } -TEST_F(RealPanWalletClientTest, MalformedResponse) { +TEST_F(PaymentsClientTest, MalformedResponse) { StartUnmasking(); IssueOAuthToken(); - ReturnResponse(net::HTTP_OK, - "{ \"error_code\": \"WRONG_JSON_FORMAT\" }"); + ReturnResponse(net::HTTP_OK, "{ \"error_code\": \"WRONG_JSON_FORMAT\" }"); EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_); EXPECT_EQ("", real_pan_); } -TEST_F(RealPanWalletClientTest, ReauthNeeded) { +TEST_F(PaymentsClientTest, ReauthNeeded) { { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_UNAUTHORIZED, ""); // No response yet. - EXPECT_EQ(AutofillClient::SUCCESS, result_); + EXPECT_EQ(AutofillClient::NONE, result_); EXPECT_EQ("", real_pan_); // Second HTTP_UNAUTHORIZED causes permanent failure. @@ -158,7 +242,7 @@ TEST_F(RealPanWalletClientTest, ReauthNeeded) { EXPECT_EQ("", real_pan_); } - result_ = AutofillClient::SUCCESS; + result_ = AutofillClient::NONE; real_pan_.clear(); { @@ -166,7 +250,7 @@ TEST_F(RealPanWalletClientTest, ReauthNeeded) { IssueOAuthToken(); ReturnResponse(net::HTTP_UNAUTHORIZED, ""); // No response yet. - EXPECT_EQ(AutofillClient::SUCCESS, result_); + EXPECT_EQ(AutofillClient::NONE, result_); EXPECT_EQ("", real_pan_); // HTTP_OK after first HTTP_UNAUTHORIZED results in success. @@ -177,7 +261,7 @@ TEST_F(RealPanWalletClientTest, ReauthNeeded) { } } -TEST_F(RealPanWalletClientTest, NetworkError) { +TEST_F(PaymentsClientTest, NetworkError) { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_REQUEST_TIMEOUT, std::string()); @@ -185,7 +269,7 @@ TEST_F(RealPanWalletClientTest, NetworkError) { EXPECT_EQ("", real_pan_); } -TEST_F(RealPanWalletClientTest, OtherError) { +TEST_F(PaymentsClientTest, OtherError) { StartUnmasking(); IssueOAuthToken(); ReturnResponse(net::HTTP_FORBIDDEN, std::string()); @@ -194,4 +278,4 @@ TEST_F(RealPanWalletClientTest, OtherError) { } } // namespace autofill -} // namespace wallet +} // namespace payments diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 1ecaf93..28ad3c5 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn @@ -90,6 +90,9 @@ source_set("browser") { "options_util.h", "password_generator.cc", "password_generator.h", + "payments/payments_client.cc", + "payments/payments_client.h", + "payments/payments_request.h", "personal_data_manager.cc", "personal_data_manager.h", "personal_data_manager_observer.h", @@ -112,8 +115,6 @@ source_set("browser") { "ui/card_unmask_prompt_view.h", "validation.cc", "validation.h", - "wallet/real_pan_wallet_client.cc", - "wallet/real_pan_wallet_client.h", "webdata/autocomplete_syncable_service.cc", "webdata/autocomplete_syncable_service.h", "webdata/autofill_change.cc", diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h index b89acc9..a1faf80 100644 --- a/components/autofill/core/browser/autofill_client.h +++ b/components/autofill/core/browser/autofill_client.h @@ -11,6 +11,7 @@ #include "base/i18n/rtl.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" +#include "base/values.h" #include "ui/base/window_open_disposition.h" #include "url/gurl.h" @@ -60,7 +61,7 @@ class AutofillClient { AutocompleteResultErrorInvalid, }; - enum GetRealPanResult { + enum PaymentsRpcResult { // Empty result. Used for initializing variables and should generally // not be returned nor passed as arguments unless explicitly allowed by // the API. @@ -75,7 +76,7 @@ class AutofillClient { // Request failed; don't try again. PERMANENT_FAILURE, - // Unable to connect to Wallet servers. Prompt user to check internet + // Unable to connect to Payments servers. Prompt user to check internet // connection. NETWORK_ERROR, }; @@ -115,12 +116,21 @@ class AutofillClient { // information to proceed. virtual void ShowUnmaskPrompt(const CreditCard& card, base::WeakPtr<CardUnmaskDelegate> delegate) = 0; - virtual void OnUnmaskVerificationResult(GetRealPanResult result) = 0; + virtual void OnUnmaskVerificationResult(PaymentsRpcResult result) = 0; - // Run |save_card_callback| if the credit card should be imported as personal + // Runs |callback| if the credit card should be imported as personal // data. |metric_logger| can be used to log user actions. - virtual void ConfirmSaveCreditCard( - const base::Closure& save_card_callback) = 0; + virtual void ConfirmSaveCreditCardLocally(const base::Closure& callback) = 0; + + // Runs |callback| if the credit card should be uploaded to Payments. Displays + // the contents of |legal_message| to the user. + virtual void ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) = 0; + + // Gathers risk data and provides it to |callback|. + virtual void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) = 0; // Returns true if both the platform and the device support scanning credit // cards. Should be called before ScanCreditCard(). diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc index c43f5b4..23a6e25 100644 --- a/components/autofill/core/browser/autofill_experiments.cc +++ b/components/autofill/core/browser/autofill_experiments.cc @@ -7,8 +7,10 @@ #include "base/command_line.h" #include "base/metrics/field_trial.h" #include "base/prefs/pref_service.h" +#include "base/strings/string_util.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/autofill_switches.h" +#include "google_apis/gaia/gaia_auth_util.h" namespace autofill { @@ -47,4 +49,30 @@ bool OfferStoreUnmaskedCards() { #endif } +bool IsCreditCardUploadEnabled(const PrefService* pref_service, + const std::string& user_email) { + // Query the field trial before checking command line flags to ensure UMA + // reports the correct group. + std::string group_name = + base::FieldTrialList::FindFullName("OfferUploadCreditCards"); + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableOfferUploadCreditCards)) + return true; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableOfferUploadCreditCards)) + return false; + + if (group_name.empty() || group_name == "Disabled") + return false; + + if (!pref_service->GetBoolean(prefs::kAutofillWalletSyncExperimentEnabled) || + !pref_service->GetBoolean(prefs::kAutofillWalletImportEnabled)) + return false; + + std::string domain = gaia::ExtractDomainName(user_email); + return domain == "googlemail.com" || domain == "gmail.com" || + domain == "google.com"; +} + } // namespace autofill diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h index cd2764e..3a152cc 100644 --- a/components/autofill/core/browser/autofill_experiments.h +++ b/components/autofill/core/browser/autofill_experiments.h @@ -5,6 +5,8 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ +#include <string> + class PrefService; namespace autofill { @@ -24,6 +26,12 @@ bool IsInAutofillSuggestionsDisabledExperiment(); // response of the option. bool OfferStoreUnmaskedCards(); +// Returns true if uploading credit cards to Wallet servers is enabled. This +// requires the appropriate flags and user settings to be true and the user to +// be a member of a supported domain. +bool IsCreditCardUploadEnabled(const PrefService* pref_service, + const std::string& user_email); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index edc99de..8e05d1f 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc @@ -52,6 +52,7 @@ #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/password_form_fill_data.h" #include "components/pref_registry/pref_registry_syncable.h" +#include "google_apis/gaia/identity_provider.h" #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" @@ -135,7 +136,8 @@ AutofillManager::AutofillManager( AutofillDownloadManagerState enable_download_manager) : driver_(driver), client_(client), - real_pan_client_(driver->GetURLRequestContext(), this), + payments_client_( + new payments::PaymentsClient(driver->GetURLRequestContext(), this)), app_locale_(app_locale), personal_data_(client->GetPersonalDataManager()), autocomplete_history_manager_( @@ -150,6 +152,7 @@ AutofillManager::AutofillManager( user_did_type_(false), user_did_autofill_(false), user_did_edit_autofilled_field_(false), + user_did_accept_upload_prompt_(false), external_delegate_(NULL), test_delegate_(NULL), weak_ptr_factory_(this) { @@ -511,13 +514,15 @@ void AutofillManager::FillOrPreviewCreditCardForm( if (action == AutofillDriver::FORM_DATA_ACTION_FILL) { if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD && WillFillCreditCardNumber(form, field)) { - unmasking_card_ = credit_card; + unmask_request_.card = credit_card; unmasking_query_id_ = query_id; unmasking_form_ = form; unmasking_field_ = field; - real_pan_client_.Prepare(); - client()->ShowUnmaskPrompt(unmasking_card_, - weak_ptr_factory_.GetWeakPtr()); + payments_client_->Prepare(); + client_->ShowUnmaskPrompt(unmask_request_.card, + weak_ptr_factory_.GetWeakPtr()); + client_->LoadRiskData(base::Bind(&AutofillManager::OnDidGetUnmaskRiskData, + weak_ptr_factory_.GetWeakPtr())); credit_card_form_event_logger_->OnDidSelectMaskedServerCardSuggestion(); return; } @@ -714,7 +719,7 @@ void AutofillManager::RemoveAutocompleteEntry(const base::string16& name, } bool AutofillManager::IsShowingUnmaskPrompt() { - return unmasking_card_.Compare(CreditCard()) != 0; + return unmask_request_.card.Compare(CreditCard()) != 0; } const std::vector<FormStructure*>& AutofillManager::GetFormStructures() { @@ -750,48 +755,103 @@ void AutofillManager::OnLoadedServerPredictions( } void AutofillManager::OnUnmaskResponse(const UnmaskResponse& response) { - unmask_response_ = response; - real_pan_request_timestamp_ = base::Time::Now(); - real_pan_client_.UnmaskCard(unmasking_card_, response); + unmask_request_.user_response = response; + if (!unmask_request_.risk_data.empty()) { + real_pan_request_timestamp_ = base::Time::Now(); + payments_client_->UnmaskCard(unmask_request_); + } } void AutofillManager::OnUnmaskPromptClosed() { - real_pan_client_.CancelRequest(); + payments_client_->CancelRequest(); driver_->RendererShouldClearPreviewedForm(); - unmasking_card_ = CreditCard(); - unmask_response_ = UnmaskResponse(); + unmask_request_ = payments::PaymentsClient::UnmaskRequestDetails(); } IdentityProvider* AutofillManager::GetIdentityProvider() { - return client()->GetIdentityProvider(); + return client_->GetIdentityProvider(); } -void AutofillManager::OnDidGetRealPan(AutofillClient::GetRealPanResult result, +void AutofillManager::OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, const std::string& real_pan) { AutofillMetrics::LogRealPanDuration( base::Time::Now() - real_pan_request_timestamp_, result); if (!real_pan.empty()) { DCHECK_EQ(AutofillClient::SUCCESS, result); - credit_card_form_event_logger_->OnDidFillSuggestion(unmasking_card_); - recently_unmasked_cards_.push_back(unmasking_card_); - unmasking_card_.set_record_type(CreditCard::FULL_SERVER_CARD); - unmasking_card_.SetNumber(base::UTF8ToUTF16(real_pan)); - if (!unmask_response_.exp_month.empty()) { - unmasking_card_.SetRawInfo(CREDIT_CARD_EXP_MONTH, - unmask_response_.exp_month); + credit_card_form_event_logger_->OnDidFillSuggestion(unmask_request_.card); + recently_unmasked_cards_.push_back(unmask_request_.card); + unmask_request_.card.set_record_type(CreditCard::FULL_SERVER_CARD); + unmask_request_.card.SetNumber(base::UTF8ToUTF16(real_pan)); + if (!unmask_request_.user_response.exp_month.empty()) { + unmask_request_.card.SetRawInfo(CREDIT_CARD_EXP_MONTH, + unmask_request_.user_response.exp_month); } - if (!unmask_response_.exp_year.empty()) { - unmasking_card_.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, - unmask_response_.exp_year); + if (!unmask_request_.user_response.exp_year.empty()) { + unmask_request_.card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, + unmask_request_.user_response.exp_year); } - if (unmask_response_.should_store_pan) - personal_data_->UpdateServerCreditCard(unmasking_card_); + if (unmask_request_.user_response.should_store_pan) + personal_data_->UpdateServerCreditCard(unmask_request_.card); FillCreditCardForm(unmasking_query_id_, unmasking_form_, unmasking_field_, - unmasking_card_); + unmask_request_.card); + } + + client_->OnUnmaskVerificationResult(result); +} + +void AutofillManager::OnDidGetUploadDetails( + AutofillClient::PaymentsRpcResult result, + const base::string16& context_token, + scoped_ptr<base::DictionaryValue> legal_message) { + // TODO(jdonnelly): Log duration. + if (result == AutofillClient::SUCCESS) { + // Do *not* call payments_client_->Prepare() here. We shouldn't send + // credentials until the user has explicitly accepted a prompt to upload. + upload_request_.context_token = context_token; + user_did_accept_upload_prompt_ = false; + client_->ConfirmSaveCreditCardToCloud( + base::Bind(&AutofillManager::OnUserDidAcceptUpload, + weak_ptr_factory_.GetWeakPtr()), + legal_message.Pass()); + client_->LoadRiskData(base::Bind(&AutofillManager::OnDidGetUploadRiskData, + weak_ptr_factory_.GetWeakPtr())); + } else { + // Since the upload details request failed, fallback to a local save. + client_->ConfirmSaveCreditCardLocally(base::Bind( + base::IgnoreResult(&PersonalDataManager::SaveImportedCreditCard), + base::Unretained(personal_data_), upload_request_.card)); + } +} + +void AutofillManager::OnDidUploadCard( + AutofillClient::PaymentsRpcResult result) { + // We don't do anything user-visible if the upload attempt fails. + // TODO(jdonnelly): Log duration. +} + +void AutofillManager::OnDidGetUnmaskRiskData(const std::string& risk_data) { + unmask_request_.risk_data = risk_data; + if (!unmask_request_.user_response.cvc.empty()) { + real_pan_request_timestamp_ = base::Time::Now(); + payments_client_->UnmaskCard(unmask_request_); + } +} + +void AutofillManager::OnUserDidAcceptUpload() { + user_did_accept_upload_prompt_ = true; + if (!upload_request_.risk_data.empty()) { + upload_request_.app_locale = app_locale_; + payments_client_->UploadCard(upload_request_); } +} - client()->OnUnmaskVerificationResult(result); +void AutofillManager::OnDidGetUploadRiskData(const std::string& risk_data) { + upload_request_.risk_data = risk_data; + if (user_did_accept_upload_prompt_) { + upload_request_.app_locale = app_locale_; + payments_client_->UploadCard(upload_request_); + } } void AutofillManager::OnDidEndTextFieldEditing() { @@ -802,6 +862,11 @@ bool AutofillManager::IsAutofillEnabled() const { return ::autofill::IsAutofillEnabled(client_->GetPrefs()); } +bool AutofillManager::IsCreditCardUploadEnabled() { + return ::autofill::IsCreditCardUploadEnabled( + client_->GetPrefs(), GetIdentityProvider()->GetActiveUsername()); +} + bool AutofillManager::ShouldUploadForm(const FormStructure& form) { if (!IsAutofillEnabled()) return false; @@ -860,11 +925,28 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { imported_credit_card->TypeAndLastFourDigits()) return; } - client_->ConfirmSaveCreditCard( - base::Bind( - base::IgnoreResult(&PersonalDataManager::SaveImportedCreditCard), - base::Unretained(personal_data_), - *imported_credit_card)); + + // Check for a CVC in order to determine whether we can prompt the user to + // upload their card. + for (const auto& field : submitted_form) { + if (field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE) { + upload_request_.cvc = field->value; + break; + } + } + + if (!upload_request_.cvc.empty() && IsCreditCardUploadEnabled()) { + // Initiate the upload flow if a CVC was entered into the form and the + // feature is enabled. + upload_request_ = payments::PaymentsClient::UploadRequestDetails(); + upload_request_.card = *imported_credit_card; + payments_client_->GetUploadDetails(app_locale_); + } else { + // Otherwise, prompt the user for local save. + client_->ConfirmSaveCreditCardLocally(base::Bind( + base::IgnoreResult(&PersonalDataManager::SaveImportedCreditCard), + base::Unretained(personal_data_), *imported_credit_card)); + } } } @@ -908,6 +990,8 @@ void AutofillManager::UploadFormData(const FormStructure& submitted_form) { } void AutofillManager::Reset() { + // Note that upload_request_ is not reset here because the prompt to + // save a card is shown after page navigation. form_structures_.clear(); address_form_event_logger_.reset( new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)); @@ -919,7 +1003,7 @@ void AutofillManager::Reset() { user_did_type_ = false; user_did_autofill_ = false; user_did_edit_autofilled_field_ = false; - unmasking_card_ = CreditCard(); + unmask_request_ = payments::PaymentsClient::UnmaskRequestDetails(); unmasking_query_id_ = -1; unmasking_form_ = FormData(); unmasking_field_ = FormFieldData(); @@ -933,7 +1017,8 @@ AutofillManager::AutofillManager(AutofillDriver* driver, PersonalDataManager* personal_data) : driver_(driver), client_(client), - real_pan_client_(driver->GetURLRequestContext(), this), + payments_client_( + new payments::PaymentsClient(driver->GetURLRequestContext(), this)), app_locale_("en-US"), personal_data_(personal_data), autocomplete_history_manager_( @@ -1116,10 +1201,10 @@ void AutofillManager::FillOrPreviewDataModelForm( if (is_credit_card && cached_field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE) { - // If this is |unmasking_card_|, |unmask_response_.cvc| should be - // non-empty and vice versa. - value = unmask_response_.cvc; - DCHECK_EQ(&unmasking_card_ == &data_model, !value.empty()); + // If this is |unmask_request_.card|, |unmask_request_.user_response.cvc| + // should be non-empty and vice versa. + value = unmask_request_.user_response.cvc; + DCHECK_EQ(&unmask_request_.card == &data_model, !value.empty()); } // Must match ForEachMatchingFormField() in form_autofill_util.cc. diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index c7b3029..90c30e4 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h @@ -26,8 +26,8 @@ #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/card_unmask_delegate.h" #include "components/autofill/core/browser/form_structure.h" +#include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" -#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h" #include "components/autofill/core/common/form_data.h" // This define protects some debugging code (see DumpAutofillData). This @@ -71,7 +71,7 @@ struct FormFieldData; // forms. One per frame; owned by the AutofillDriver. class AutofillManager : public AutofillDownloadManager::Observer, public CardUnmaskDelegate, - public wallet::RealPanWalletClient::Delegate { + public payments::PaymentsClientDelegate { public: enum AutofillDownloadManagerState { ENABLE_AUTOFILL_DOWNLOAD_MANAGER, @@ -127,7 +127,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, void RemoveAutocompleteEntry(const base::string16& name, const base::string16& value); - // Returns true when the Wallet card unmask prompt is being displayed. + // Returns true when the Payments card unmask prompt is being displayed. bool IsShowingUnmaskPrompt(); // Returns the present form structures seen by Autofill manager. @@ -150,6 +150,8 @@ class AutofillManager : public AutofillDownloadManager::Observer, void OnFormsSeen(const std::vector<FormData>& forms, const base::TimeTicks& timestamp); + void set_app_locale(std::string app_locale) { app_locale_ = app_locale; } + // IMPORTANT: On iOS, this method is called when the form is submitted, // immediately before OnFormSubmitted() is called. Do not assume that // OnWillSubmitForm() will run before the form submits. @@ -184,6 +186,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Returns the value of the AutofillEnabled pref. virtual bool IsAutofillEnabled() const; + // Returns true if all the conditions for enabling the upload of credit card + // are satisfied. + virtual bool IsCreditCardUploadEnabled(); + // Shared code to determine if |form| should be uploaded to the Autofill // server. It verifies that uploading is allowed and |form| meets conditions // to be uploadable. @@ -222,6 +228,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, ScopedVector<FormStructure>* form_structures() { return &form_structures_; } + protected: // Exposed for testing. AutofillExternalDelegate* external_delegate() { return external_delegate_; @@ -232,6 +239,11 @@ class AutofillManager : public AutofillDownloadManager::Observer, download_manager_.reset(manager); } + // Exposed for testing. + void set_payments_client(payments::PaymentsClient* payments_client) { + payments_client_.reset(payments_client); + } + private: // AutofillDownloadManager::Observer: void OnLoadedServerPredictions(const std::string& response_xml) override; @@ -240,10 +252,27 @@ class AutofillManager : public AutofillDownloadManager::Observer, void OnUnmaskResponse(const UnmaskResponse& response) override; void OnUnmaskPromptClosed() override; - // wallet::RealPanWalletClient::Delegate: + // payments::PaymentsClientDelegate: IdentityProvider* GetIdentityProvider() override; - void OnDidGetRealPan(AutofillClient::GetRealPanResult result, + void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, const std::string& real_pan) override; + void OnDidGetUploadDetails( + AutofillClient::PaymentsRpcResult result, + const base::string16& context_token, + scoped_ptr<base::DictionaryValue> legal_message) override; + void OnDidUploadCard(AutofillClient::PaymentsRpcResult result) override; + + // Saves risk data in |unmasking_risk_data_| and calls UnmaskCard if the user + // has accepted the prompt. + void OnDidGetUnmaskRiskData(const std::string& risk_data); + + // Sets |user_did_accept_upload_prompt_| and calls UploadCard if the risk data + // is available. + void OnUserDidAcceptUpload(); + + // Saves risk data in |uploading_risk_data_| and calls UploadCard if the user + // has accepted the prompt. + void OnDidGetUploadRiskData(const std::string& risk_data); // Returns false if Autofill is disabled or if no Autofill data is available. bool RefreshDataModels(); @@ -361,8 +390,8 @@ class AutofillManager : public AutofillDownloadManager::Observer, AutofillClient* const client_; - // Handles real PAN requests. - wallet::RealPanWalletClient real_pan_client_; + // Handles Payments service requests. + scoped_ptr<payments::PaymentsClient> payments_client_; std::string app_locale_; @@ -407,17 +436,19 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Our copy of the form data. ScopedVector<FormStructure> form_structures_; - // A copy of the credit card that's currently being unmasked, and data about - // the form. - CreditCard unmasking_card_; - // A copy of the latest card unmasking response. - UnmaskResponse unmask_response_; + // Collected information about a pending unmask request, and data about the + // form. + payments::PaymentsClient::UnmaskRequestDetails unmask_request_; int unmasking_query_id_; FormData unmasking_form_; FormFieldData unmasking_field_; - // Time when we requested the last real pan + // Time when we requested the last real pan. base::Time real_pan_request_timestamp_; + // Collected information about a pending upload request. + payments::PaymentsClient::UploadRequestDetails upload_request_; + bool user_did_accept_upload_prompt_; + // Masked copies of recently unmasked cards, to help avoid double-asking to // save the card (in the prompt and in the infobar after submit). std::vector<CreditCard> recently_unmasked_cards_; @@ -494,7 +525,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, AutocompleteOffRespectedForAutocomplete); FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DontSaveCvcInAutocompleteHistory); - FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DontOfferToSaveWalletCard); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DontOfferToSavePaymentsCard); FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, FillInUpdatedExpirationDate); DISALLOW_COPY_AND_ASSIGN(AutofillManager); }; diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index f5cbdfa..7383a8e 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc @@ -63,13 +63,40 @@ class MockAutofillClient : public TestAutofillClient { ~MockAutofillClient() override {} - MOCK_METHOD1(ConfirmSaveCreditCard, - void(const base::Closure& save_card_callback)); + MOCK_METHOD1(ConfirmSaveCreditCardLocally, + void(const base::Closure& callback)); private: DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); }; +class TestPaymentsClient : public payments::PaymentsClient { + public: + TestPaymentsClient(net::URLRequestContextGetter* context_getter, + payments::PaymentsClientDelegate* delegate) + : PaymentsClient(context_getter, delegate), delegate_(delegate) {} + + ~TestPaymentsClient() override {} + + void GetUploadDetails(const std::string& app_locale) override { + delegate_->OnDidGetUploadDetails( + app_locale == "en-US" ? AutofillClient::SUCCESS + : AutofillClient::PERMANENT_FAILURE, + base::ASCIIToUTF16("this is a context token"), + scoped_ptr<base::DictionaryValue>(nullptr)); + } + + void UploadCard(const payments::PaymentsClient::UploadRequestDetails& + request_details) override { + delegate_->OnDidUploadCard(AutofillClient::SUCCESS); + } + + private: + payments::PaymentsClientDelegate* const delegate_; + + DISALLOW_COPY_AND_ASSIGN(TestPaymentsClient); +}; + class TestPersonalDataManager : public PersonalDataManager { public: TestPersonalDataManager() @@ -297,7 +324,7 @@ void ExpectFilledForm(int page_id, bool use_month_type) { // The number of fields in the address and credit card forms created above. const size_t kAddressFormSize = 11; - const size_t kCreditCardFormSize = use_month_type ? 3 : 4; + const size_t kCreditCardFormSize = use_month_type ? 4 : 5; EXPECT_EQ(expected_page_id, page_id); EXPECT_EQ(ASCIIToUTF16("MyForm"), filled_form.name); @@ -433,7 +460,12 @@ class TestAutofillManager : public AutofillManager { : AutofillManager(driver, client, personal_data), personal_data_(personal_data), autofill_enabled_(true), - expect_all_unknown_possible_types_(false) {} + credit_card_upload_enabled_(true), + credit_card_was_uploaded_(false), + expect_all_unknown_possible_types_(false) { + set_payments_client( + new TestPaymentsClient(driver->GetURLRequestContext(), this)); + } ~TestAutofillManager() override {} bool IsAutofillEnabled() const override { return autofill_enabled_; } @@ -442,6 +474,16 @@ class TestAutofillManager : public AutofillManager { autofill_enabled_ = autofill_enabled; } + bool IsCreditCardUploadEnabled() override { + return credit_card_upload_enabled_; + } + + void set_credit_card_upload_enabled(bool credit_card_upload_enabled) { + credit_card_upload_enabled_ = credit_card_upload_enabled; + } + + bool credit_card_was_uploaded() { return credit_card_was_uploaded_; } + void set_expected_submitted_field_types( const std::vector<ServerFieldTypeSet>& expected_types) { expected_submitted_field_types_ = expected_types; @@ -529,10 +571,16 @@ class TestAutofillManager : public AutofillManager { } private: + void OnDidUploadCard(AutofillClient::PaymentsRpcResult result) override { + credit_card_was_uploaded_ = true; + }; + // Weak reference. TestPersonalDataManager* personal_data_; bool autofill_enabled_; + bool credit_card_upload_enabled_; + bool credit_card_was_uploaded_; bool expect_all_unknown_possible_types_; scoped_ptr<base::RunLoop> run_loop_; @@ -774,6 +822,8 @@ class AutofillManagerTest : public testing::Test { test::CreateTestFormField("", "ccyear", "", "text", &field); form->fields.push_back(field); } + test::CreateTestFormField("CVC", "cvc", "", "text", &field); + form->fields.push_back(field); } // Tests if credit card data gets saved @@ -788,13 +838,13 @@ class AutofillManagerTest : public testing::Test { form.fields[1].value = ASCIIToUTF16("4111111111111111"); form.fields[2].value = ASCIIToUTF16("11"); form.fields[3].value = ASCIIToUTF16("2017"); - EXPECT_CALL(autofill_client_, ConfirmSaveCreditCard(_)).Times(1); + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(1); FormSubmitted(form); } void PrepareForRealPanResponse(FormData* form, CreditCard* card) { - // This line silences the warning from RealPanWalletClient about matching - // sync and wallet server types. + // This line silences the warning from PaymentsClient about matching sync + // and Payments server types. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "sync-url", "https://google.com"); @@ -805,7 +855,7 @@ class AutofillManagerTest : public testing::Test { "2017"); card->SetTypeForMaskedCard(kVisaCard); - EXPECT_CALL(autofill_client_, ConfirmSaveCreditCard(_)).Times(0); + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(0); EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)) .Times(AtLeast(1)); autofill_manager_->FillOrPreviewCreditCardForm( @@ -834,6 +884,7 @@ class AutofillManagerTest : public testing::Test { scoped_ptr<TestAutofillManager> autofill_manager_; scoped_ptr<TestAutofillExternalDelegate> external_delegate_; scoped_refptr<net::TestURLRequestContextGetter> request_context_; + TestPaymentsClient* payments_client_; TestAutofillDownloadManager* download_manager_; TestPersonalDataManager personal_data_; }; @@ -2089,9 +2140,9 @@ TEST_F(AutofillManagerTest, FillAutofilledForm) { const int kPageID3 = 3; response_page_id = 0; - FillAutofillFormDataAndSaveResults(kPageID3, form, *form.fields.rbegin(), - MakeFrontendID(guid2, std::string()), - &response_page_id, &response_data); + FillAutofillFormDataAndSaveResults( + kPageID3, form, form.fields[form.fields.size() - 2], + MakeFrontendID(guid2, std::string()), &response_page_id, &response_data); { SCOPED_TRACE("Credit card 2"); ExpectFilledForm(response_page_id, response_data, kPageID3, "", "", "", "", @@ -2684,7 +2735,7 @@ TEST_F(AutofillManagerTest, CreditCardSavedWhenAutocompleteOff) { form.fields[1].value = ASCIIToUTF16("4111111111111111"); form.fields[2].value = ASCIIToUTF16("11"); form.fields[3].value = ASCIIToUTF16("2017"); - EXPECT_CALL(autofill_client_, ConfirmSaveCreditCard(_)).Times(1); + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(1); FormSubmitted(form); } @@ -2703,7 +2754,7 @@ TEST_F(AutofillManagerTest, InvalidCreditCardNumberIsNotSaved) { form.fields[1].value = ASCIIToUTF16(card); form.fields[2].value = ASCIIToUTF16("11"); form.fields[3].value = ASCIIToUTF16("2017"); - EXPECT_CALL(autofill_client_, ConfirmSaveCreditCard(_)).Times(0); + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(0); FormSubmitted(form); } @@ -3176,7 +3227,7 @@ TEST_F(AutofillManagerTest, DontSaveCvcInAutocompleteHistory) { } } -TEST_F(AutofillManagerTest, DontOfferToSaveWalletCard) { +TEST_F(AutofillManagerTest, DontOfferToSavePaymentsCard) { FormData form; CreditCard card; PrepareForRealPanResponse(&form, &card); @@ -3250,10 +3301,92 @@ TEST_F(AutofillManagerTest, FillInUpdatedExpirationDate) { autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, "4012888888881881"); - EXPECT_EQ(ASCIIToUTF16("02"), autofill_manager_->unmasking_card_.GetRawInfo( - CREDIT_CARD_EXP_MONTH)); - EXPECT_EQ(ASCIIToUTF16("2018"), autofill_manager_->unmasking_card_.GetRawInfo( - CREDIT_CARD_EXP_4_DIGIT_YEAR)); + EXPECT_EQ(ASCIIToUTF16("02"), + autofill_manager_->unmask_request_.card.GetRawInfo( + CREDIT_CARD_EXP_MONTH)); + EXPECT_EQ(ASCIIToUTF16("2018"), + autofill_manager_->unmask_request_.card.GetRawInfo( + CREDIT_CARD_EXP_4_DIGIT_YEAR)); +} + +TEST_F(AutofillManagerTest, UploadCreditCard) { + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + // Edit the data, and submit + form.fields[1].value = ASCIIToUTF16("4111111111111111"); + form.fields[2].value = ASCIIToUTF16("11"); + form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[4].value = ASCIIToUTF16("123"); + + FormSubmitted(form); + EXPECT_TRUE(autofill_manager_->credit_card_was_uploaded()); +} + +TEST_F(AutofillManagerTest, DontUploadCreditCardIfFeatureNotEnabled) { + autofill_manager_->set_credit_card_upload_enabled(false); + + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + // Edit the data, and submit + form.fields[1].value = ASCIIToUTF16("4111111111111111"); + form.fields[2].value = ASCIIToUTF16("11"); + form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[4].value = ASCIIToUTF16("123"); + + // The save prompt should be shown instead of doing an upload. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(1); + FormSubmitted(form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); +} + +TEST_F(AutofillManagerTest, DontUploadCreditCardIfCvcUnavailable) { + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + // Edit the data, and submit + form.fields[1].value = ASCIIToUTF16("4111111111111111"); + form.fields[2].value = ASCIIToUTF16("11"); + form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[4].value = ASCIIToUTF16(""); // CVC + + // The save prompt should be shown instead of doing an upload. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(1); + FormSubmitted(form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); +} + +TEST_F(AutofillManagerTest, DontUploadCreditCardIfUploadDetailsFails) { + // Anything other than "en-US" will cause GetUploadDetails to return a failure + // response. + autofill_manager_->set_app_locale("pt-BR"); + + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + // Edit the data, and submit + form.fields[1].value = ASCIIToUTF16("4111111111111111"); + form.fields[2].value = ASCIIToUTF16("11"); + form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[4].value = ASCIIToUTF16("123"); + + // The save prompt should be shown instead of doing an upload. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_)).Times(1); + FormSubmitted(form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); } // Verify that typing "gmail" will match "theking@gmail.com" and diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc index cacfe91..85a3370 100644 --- a/components/autofill/core/browser/autofill_metrics.cc +++ b/components/autofill/core/browser/autofill_metrics.cc @@ -391,34 +391,33 @@ void AutofillMetrics::LogTimeBeforeAbandonUnmasking( // static void AutofillMetrics::LogRealPanResult( - AutofillClient::GetRealPanResult result) { - GetRealPanResult metric_result; + AutofillClient::PaymentsRpcResult result) { + PaymentsRpcResult metric_result; switch (result) { case AutofillClient::SUCCESS: - metric_result = GET_REAL_PAN_RESULT_SUCCESS; + metric_result = PAYMENTS_RESULT_SUCCESS; break; case AutofillClient::TRY_AGAIN_FAILURE: - metric_result = GET_REAL_PAN_RESULT_TRY_AGAIN_FAILURE; + metric_result = PAYMENTS_RESULT_TRY_AGAIN_FAILURE; break; case AutofillClient::PERMANENT_FAILURE: - metric_result = GET_REAL_PAN_RESULT_PERMANENT_FAILURE; + metric_result = PAYMENTS_RESULT_PERMANENT_FAILURE; break; case AutofillClient::NETWORK_ERROR: - metric_result = GET_REAL_PAN_RESULT_NETWORK_ERROR; + metric_result = PAYMENTS_RESULT_NETWORK_ERROR; break; default: NOTREACHED(); return; } UMA_HISTOGRAM_ENUMERATION("Autofill.UnmaskPrompt.GetRealPanResult", - metric_result, - NUM_GET_REAL_PAN_RESULTS); + metric_result, NUM_PAYMENTS_RESULTS); } // static void AutofillMetrics::LogRealPanDuration( const base::TimeDelta& duration, - AutofillClient::GetRealPanResult result) { + AutofillClient::PaymentsRpcResult result) { std::string suffix; switch (result) { case AutofillClient::SUCCESS: @@ -444,7 +443,7 @@ void AutofillMetrics::LogRealPanDuration( // static void AutofillMetrics::LogUnmaskingDuration( const base::TimeDelta& duration, - AutofillClient::GetRealPanResult result) { + AutofillClient::PaymentsRpcResult result) { std::string suffix; switch (result) { case AutofillClient::SUCCESS: diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h index 1219c97..4688d05 100644 --- a/components/autofill/core/browser/autofill_metrics.h +++ b/components/autofill/core/browser/autofill_metrics.h @@ -348,17 +348,17 @@ class AutofillMetrics { NUM_UNMASK_PROMPT_EVENTS, }; - // Possible results of the GetRealPan call. - enum GetRealPanResult { + // Possible results of Payments RPCs. + enum PaymentsRpcResult { // Request succeeded. - GET_REAL_PAN_RESULT_SUCCESS = 0, + PAYMENTS_RESULT_SUCCESS = 0, // Request failed; try again. - GET_REAL_PAN_RESULT_TRY_AGAIN_FAILURE, + PAYMENTS_RESULT_TRY_AGAIN_FAILURE, // Request failed; don't try again. - GET_REAL_PAN_RESULT_PERMANENT_FAILURE, - // Unable to connect to Wallet servers. - GET_REAL_PAN_RESULT_NETWORK_ERROR, - NUM_GET_REAL_PAN_RESULTS, + PAYMENTS_RESULT_PERMANENT_FAILURE, + // Unable to connect to Payments servers. + PAYMENTS_RESULT_NETWORK_ERROR, + NUM_PAYMENTS_RESULTS, }; // For measuring the network request time of various Wallet API calls. See @@ -509,15 +509,15 @@ class AutofillMetrics { static void LogTimeBeforeAbandonUnmasking(const base::TimeDelta& duration); // Logs |result| to the get real pan result histogram. - static void LogRealPanResult(AutofillClient::GetRealPanResult result); + static void LogRealPanResult(AutofillClient::PaymentsRpcResult result); // Logs |result| to duration of the GetRealPan RPC. static void LogRealPanDuration(const base::TimeDelta& duration, - AutofillClient::GetRealPanResult result); + AutofillClient::PaymentsRpcResult result); // Logs |result| to the get real pan result histogram. static void LogUnmaskingDuration(const base::TimeDelta& duration, - AutofillClient::GetRealPanResult result); + AutofillClient::PaymentsRpcResult result); // Logs |metric| to the Wallet errors histogram. static void LogWalletErrorMetric(WalletErrorMetric metric); diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc index f91dcc2..ed36d53 100644 --- a/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -16,10 +16,10 @@ #include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" -#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/form_data.h" diff --git a/components/autofill/core/browser/card_unmask_delegate.cc b/components/autofill/core/browser/card_unmask_delegate.cc index 709acc1..cb785d4 100644 --- a/components/autofill/core/browser/card_unmask_delegate.cc +++ b/components/autofill/core/browser/card_unmask_delegate.cc @@ -7,15 +7,7 @@ namespace autofill { CardUnmaskDelegate::UnmaskResponse::UnmaskResponse() - : should_store_pan(false), - providing_risk_advisory_data(false) { -#if defined(OS_IOS) - // On iOS, we generate a RiskAdvisoryData instead of the - // BrowserNativeFingerprinting produced on other platforms. This field - // directs the Wallet client to configure the request accordingly. - providing_risk_advisory_data = true; -#endif -} + : should_store_pan(false) {} CardUnmaskDelegate::UnmaskResponse::~UnmaskResponse() {} diff --git a/components/autofill/core/browser/card_unmask_delegate.h b/components/autofill/core/browser/card_unmask_delegate.h index 73ec3b0..26103e0 100644 --- a/components/autofill/core/browser/card_unmask_delegate.h +++ b/components/autofill/core/browser/card_unmask_delegate.h @@ -28,13 +28,6 @@ class CardUnmaskDelegate { // State of "copy to this device" checkbox. bool should_store_pan; - - // Risk fingerprint. - std::string risk_data; - - // Whether we're providing the alternative "risk advisory data" in risk_data - // (as opposed to the normal browser fingerprint). - bool providing_risk_advisory_data; }; // Called when the user has attempted a verification. Prompt is still diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc new file mode 100644 index 0000000..aacd534 --- /dev/null +++ b/components/autofill/core/browser/payments/payments_client.cc @@ -0,0 +1,484 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/payments/payments_client.h" + +#include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/payments/payments_request.h" +#include "components/autofill/core/common/autofill_switches.h" +#include "components/data_use_measurement/core/data_use_user_data.h" +#include "google_apis/gaia/identity_provider.h" +#include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context_getter.h" + +namespace autofill { +namespace payments { + +namespace { + +const char kPaymentsRequestHost[] = "https://wallet.google.com"; +const char kPaymentsRequestHostSandbox[] = "https://sandbox.google.com"; + +const char kUnmaskCardRequestPath[] = + "payments/apis-secure/creditcardservice/getrealpan?s7e_suffix=chromewallet"; +const char kUnmaskCardRequestFormat[] = + "requestContentType=application/json; charset=utf-8&request=%s" + "&s7e_13_cvc=%s"; + +const char kGetUploadDetailsRequestPath[] = + "payments/apis/chromepaymentsservice/getdetailsforsavecard"; + +const char kUploadCardRequestPath[] = + "payments/apis-secure/chromepaymentsservice/savecard" + "?s7e_suffix=chromewallet"; +const char kUploadCardRequestFormat[] = + "requestContentType=application/json; charset=utf-8&request=%s" + "&s7e_1_pan=%s&s7e_13_cvc=%s"; + +const char kTokenServiceConsumerId[] = "wallet_client"; +const char kPaymentsOAuth2Scope[] = + "https://www.googleapis.com/auth/wallet.chrome"; + +// This is mostly copied from wallet_service_url.cc, which is currently in +// content/, hence inaccessible from here. +bool IsPaymentsProductionEnabled() { + // If the command line flag exists, it takes precedence. + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + std::string sandbox_enabled( + command_line->GetSwitchValueASCII(switches::kWalletServiceUseSandbox)); + if (!sandbox_enabled.empty()) + return sandbox_enabled != "1"; + +#if defined(ENABLE_PROD_WALLET_SERVICE) + return true; +#else + return false; +#endif +} + +GURL GetRequestUrl(const std::string& path) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch("sync-url")) { + if (IsPaymentsProductionEnabled()) { + LOG(ERROR) << "You are using production Payments but you specified a " + "--sync-url. You likely want to disable the sync sandbox " + "or switch to sandbox Payments. Both are controlled in " + "about:flags."; + } + } else if (!IsPaymentsProductionEnabled()) { + LOG(ERROR) << "You are using sandbox Payments but you didn't specify a " + "--sync-url. You likely want to enable the sync sandbox " + "or switch to production Payments. Both are controlled in " + "about:flags."; + } + + GURL base(IsPaymentsProductionEnabled() ? kPaymentsRequestHost + : kPaymentsRequestHostSandbox); + return base.Resolve(path); +} + +scoped_ptr<base::DictionaryValue> BuildRiskDictionary( + const std::string& encoded_risk_data) { + scoped_ptr<base::DictionaryValue> risk_data(new base::DictionaryValue()); +#if defined(OS_IOS) + // Browser fingerprinting is not available on iOS. Instead, we generate + // RiskAdvisoryData. + risk_data->SetString("message_type", "RISK_ADVISORY_DATA"); + risk_data->SetString("encoding_type", "BASE_64_URL"); +#else + risk_data->SetString("message_type", "BROWSER_NATIVE_FINGERPRINTING"); + risk_data->SetString("encoding_type", "BASE_64"); +#endif + + risk_data->SetString("value", encoded_risk_data); + + return risk_data.Pass(); +} + +class UnmaskCardRequest : public PaymentsRequest { + public: + UnmaskCardRequest(const PaymentsClient::UnmaskRequestDetails& request_details) + : request_details_(request_details) { + DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, + request_details.card.record_type()); + } + ~UnmaskCardRequest() override {} + + std::string GetRequestUrlPath() override { return kUnmaskCardRequestPath; } + + std::string GetRequestContentType() override { + return "application/x-www-form-urlencoded"; + } + + std::string GetRequestContent() override { + base::DictionaryValue request_dict; + request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc"); + request_dict.SetString("credit_card_id", request_details_.card.server_id()); + request_dict.Set("risk_data_encoded", + BuildRiskDictionary(request_details_.risk_data)); + request_dict.Set("context", make_scoped_ptr(new base::DictionaryValue())); + + int value = 0; + if (base::StringToInt(request_details_.user_response.exp_month, &value)) + request_dict.SetInteger("expiration_month", value); + if (base::StringToInt(request_details_.user_response.exp_year, &value)) + request_dict.SetInteger("expiration_year", value); + + std::string json_request; + base::JSONWriter::Write(request_dict, &json_request); + std::string request_content = base::StringPrintf( + kUnmaskCardRequestFormat, + net::EscapeUrlEncodedData(json_request, true).c_str(), + net::EscapeUrlEncodedData( + base::UTF16ToASCII(request_details_.user_response.cvc), true) + .c_str()); + VLOG(3) << "getrealpan request body: " << request_content; + return request_content; + } + + void ParseResponse(scoped_ptr<base::DictionaryValue> response) override { + response->GetString("pan", &real_pan_); + } + + bool IsResponseComplete() override { return !real_pan_.empty(); } + + void RespondToDelegate(PaymentsClientDelegate* delegate, + AutofillClient::PaymentsRpcResult result) override { + delegate->OnDidGetRealPan(result, real_pan_); + } + + private: + PaymentsClient::UnmaskRequestDetails request_details_; + std::string real_pan_; +}; + +class GetUploadDetailsRequest : public PaymentsRequest { + public: + GetUploadDetailsRequest(const std::string& app_locale) + : app_locale_(app_locale) {} + ~GetUploadDetailsRequest() override {} + + std::string GetRequestUrlPath() override { + return kGetUploadDetailsRequestPath; + } + + std::string GetRequestContentType() override { return "application/json"; } + + std::string GetRequestContent() override { + base::DictionaryValue request_dict; + scoped_ptr<base::DictionaryValue> context(new base::DictionaryValue()); + context->SetString("language_code", app_locale_); + request_dict.Set("context", context.Pass()); + + std::string request_content; + base::JSONWriter::Write(request_dict, &request_content); + VLOG(3) << "getsavecarddetails request body: " << request_content; + return request_content; + } + + void ParseResponse(scoped_ptr<base::DictionaryValue> response) override { + response->GetString("context_token", &context_token_); + base::DictionaryValue* unowned_legal_message; + if (response->GetDictionary("legal_message", &unowned_legal_message)) + legal_message_ = unowned_legal_message->CreateDeepCopy(); + } + + bool IsResponseComplete() override { + return !context_token_.empty() && legal_message_; + } + + void RespondToDelegate(PaymentsClientDelegate* delegate, + AutofillClient::PaymentsRpcResult result) override { + delegate->OnDidGetUploadDetails(result, context_token_, + legal_message_.Pass()); + } + + private: + std::string app_locale_; + base::string16 context_token_; + scoped_ptr<base::DictionaryValue> legal_message_; +}; + +class UploadCardRequest : public PaymentsRequest { + public: + UploadCardRequest(const PaymentsClient::UploadRequestDetails& request_details) + : request_details_(request_details) {} + ~UploadCardRequest() override {} + + std::string GetRequestUrlPath() override { return kUploadCardRequestPath; } + + std::string GetRequestContentType() override { + return "application/x-www-form-urlencoded"; + } + + std::string GetRequestContent() override { + base::DictionaryValue request_dict; + request_dict.SetString("encrypted_pan", "__param:s7e_1_pan"); + request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc"); + request_dict.Set("risk_data_encoded", + BuildRiskDictionary(request_details_.risk_data)); + + scoped_ptr<base::DictionaryValue> context(new base::DictionaryValue()); + context->SetString("language_code", request_details_.app_locale); + request_dict.Set("context", context.Pass()); + + request_dict.SetString( + "cardholder_name", + request_details_.card.GetInfo(AutofillType(CREDIT_CARD_NAME), + request_details_.app_locale)); + + // TODO(jdonnelly): Get address(es) from the current session or available + // profiles and add to the request. + + request_dict.SetString("context_token", request_details_.context_token); + + int value = 0; + base::string16 exp_month = request_details_.card.GetInfo( + AutofillType(CREDIT_CARD_EXP_MONTH), request_details_.app_locale); + base::string16 exp_year = request_details_.card.GetInfo( + AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), + request_details_.app_locale); + if (base::StringToInt(exp_month, &value)) + request_dict.SetInteger("expiration_month", value); + if (base::StringToInt(exp_year, &value)) + request_dict.SetInteger("expiration_year", value); + + base::string16 pan = request_details_.card.GetInfo( + AutofillType(CREDIT_CARD_NUMBER), request_details_.app_locale); + std::string json_request; + base::JSONWriter::Write(request_dict, &json_request); + std::string request_content = base::StringPrintf( + kUploadCardRequestFormat, + net::EscapeUrlEncodedData(json_request, true).c_str(), + net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(), + net::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc), + true) + .c_str()); + VLOG(3) << "savecard request body: " << request_content; + return request_content; + } + + void ParseResponse(scoped_ptr<base::DictionaryValue> response) override {} + + bool IsResponseComplete() override { return true; } + + void RespondToDelegate(PaymentsClientDelegate* delegate, + AutofillClient::PaymentsRpcResult result) override { + delegate->OnDidUploadCard(result); + } + + private: + PaymentsClient::UploadRequestDetails request_details_; +}; + +} // namespace + +PaymentsClient::UnmaskRequestDetails::UnmaskRequestDetails() {} +PaymentsClient::UnmaskRequestDetails::~UnmaskRequestDetails() {} + +PaymentsClient::UploadRequestDetails::UploadRequestDetails() {} +PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {} + +PaymentsClient::PaymentsClient(net::URLRequestContextGetter* context_getter, + PaymentsClientDelegate* delegate) + : OAuth2TokenService::Consumer(kTokenServiceConsumerId), + context_getter_(context_getter), + delegate_(delegate), + has_retried_authorization_(false), + weak_ptr_factory_(this) { + DCHECK(delegate); +} + +PaymentsClient::~PaymentsClient() {} + +void PaymentsClient::Prepare() { + if (access_token_.empty()) + StartTokenFetch(false); +} + +void PaymentsClient::UnmaskCard( + const PaymentsClient::UnmaskRequestDetails& request_details) { + IssueRequest(make_scoped_ptr(new UnmaskCardRequest(request_details)), true); +} + +void PaymentsClient::GetUploadDetails(const std::string& app_locale) { + IssueRequest(make_scoped_ptr(new GetUploadDetailsRequest(app_locale)), false); +} + +void PaymentsClient::UploadCard( + const PaymentsClient::UploadRequestDetails& request_details) { + IssueRequest(make_scoped_ptr(new UploadCardRequest(request_details)), true); +} + +void PaymentsClient::IssueRequest(scoped_ptr<PaymentsRequest> request, + bool authenticate) { + request_ = request.Pass(); + has_retried_authorization_ = false; + InitializeUrlFetcher(); + + if (!authenticate) + url_fetcher_->Start(); + else if (access_token_.empty()) + StartTokenFetch(false); + else + SetOAuth2TokenAndStartRequest(); +} + +void PaymentsClient::InitializeUrlFetcher() { + url_fetcher_ = + net::URLFetcher::Create(0, GetRequestUrl(request_->GetRequestUrlPath()), + net::URLFetcher::POST, this); + + data_use_measurement::DataUseUserData::AttachToFetcher( + url_fetcher_.get(), data_use_measurement::DataUseUserData::AUTOFILL); + url_fetcher_->SetRequestContext(context_getter_.get()); + url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DISABLE_CACHE); + + url_fetcher_->SetUploadData(request_->GetRequestContentType(), + request_->GetRequestContent()); +} + +void PaymentsClient::CancelRequest() { + request_.reset(); + url_fetcher_.reset(); + access_token_request_.reset(); + access_token_.clear(); + has_retried_authorization_ = false; +} + +void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK_EQ(source, url_fetcher_.get()); + + // |url_fetcher_|, which is aliased to |source|, might continue to be used in + // this method, but should be freed once control leaves the method. + scoped_ptr<net::URLFetcher> scoped_url_fetcher(url_fetcher_.Pass()); + scoped_ptr<base::DictionaryValue> response_dict; + int response_code = source->GetResponseCode(); + std::string data; + source->GetResponseAsString(&data); + VLOG(2) << "Got data: " << data; + + AutofillClient::PaymentsRpcResult result = AutofillClient::SUCCESS; + + switch (response_code) { + // Valid response. + case net::HTTP_OK: { + std::string error_code; + scoped_ptr<base::Value> message_value = base::JSONReader::Read(data); + if (message_value.get() && + message_value->IsType(base::Value::TYPE_DICTIONARY)) { + response_dict.reset( + static_cast<base::DictionaryValue*>(message_value.release())); + response_dict->GetString("error.code", &error_code); + request_->ParseResponse(response_dict.Pass()); + } + + if (base::LowerCaseEqualsASCII(error_code, "internal")) + result = AutofillClient::TRY_AGAIN_FAILURE; + else if (!error_code.empty() || !request_->IsResponseComplete()) + result = AutofillClient::PERMANENT_FAILURE; + + break; + } + + case net::HTTP_UNAUTHORIZED: { + if (has_retried_authorization_) { + result = AutofillClient::PERMANENT_FAILURE; + break; + } + has_retried_authorization_ = true; + + InitializeUrlFetcher(); + StartTokenFetch(true); + return; + } + + // TODO(estade): is this actually how network connectivity issues are + // reported? + case net::HTTP_REQUEST_TIMEOUT: { + result = AutofillClient::NETWORK_ERROR; + break; + } + + // Handle anything else as a generic (permanent) failure. + default: { + result = AutofillClient::PERMANENT_FAILURE; + break; + } + } + + if (result != AutofillClient::SUCCESS) { + VLOG(1) << "Payments returned error: " << response_code + << " with data: " << data; + } + + request_->RespondToDelegate(delegate_, result); +} + +void PaymentsClient::OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) { + DCHECK_EQ(request, access_token_request_.get()); + access_token_ = access_token; + if (url_fetcher_) + SetOAuth2TokenAndStartRequest(); + + access_token_request_.reset(); +} + +void PaymentsClient::OnGetTokenFailure( + const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) { + DCHECK_EQ(request, access_token_request_.get()); + VLOG(1) << "Unhandled OAuth2 error: " << error.ToString(); + if (url_fetcher_) { + url_fetcher_.reset(); + request_->RespondToDelegate(delegate_, AutofillClient::PERMANENT_FAILURE); + } + access_token_request_.reset(); +} + +void PaymentsClient::StartTokenFetch(bool invalidate_old) { + // We're still waiting for the last request to come back. + if (!invalidate_old && access_token_request_) + return; + + OAuth2TokenService::ScopeSet payments_scopes; + payments_scopes.insert(kPaymentsOAuth2Scope); + IdentityProvider* identity = delegate_->GetIdentityProvider(); + if (invalidate_old) { + DCHECK(!access_token_.empty()); + identity->GetTokenService()->InvalidateAccessToken( + identity->GetActiveAccountId(), payments_scopes, access_token_); + } + access_token_.clear(); + access_token_request_ = identity->GetTokenService()->StartRequest( + identity->GetActiveAccountId(), payments_scopes, this); +} + +void PaymentsClient::SetOAuth2TokenAndStartRequest() { + url_fetcher_->AddExtraRequestHeader(net::HttpRequestHeaders::kAuthorization + + std::string(": Bearer ") + access_token_); + + url_fetcher_->Start(); +} + +} // namespace payments +} // namespace autofill diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h new file mode 100644 index 0000000..a6a23af --- /dev/null +++ b/components/autofill/core/browser/payments/payments_client.h @@ -0,0 +1,171 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_CLIENT_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_CLIENT_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/card_unmask_delegate.h" +#include "components/autofill/core/browser/credit_card.h" +#include "google_apis/gaia/oauth2_token_service.h" +#include "net/url_request/url_fetcher_delegate.h" + +class IdentityProvider; + +namespace net { +class URLFetcher; +class URLRequestContextGetter; +} + +namespace autofill { + +namespace payments { + +class PaymentsRequest; + +class PaymentsClientDelegate { + public: + // The identity provider used to get OAuth2 tokens. + virtual IdentityProvider* GetIdentityProvider() = 0; + + // Returns the real PAN retrieved from Payments. |real_pan| will be empty + // on failure. + virtual void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, + const std::string& real_pan) = 0; + + // Returns the legal message retrieved from Payments. On failure or not + // meeting Payments's conditions for upload, |legal_message| will contain + // nullptr. + virtual void OnDidGetUploadDetails( + AutofillClient::PaymentsRpcResult result, + const base::string16& context_token, + scoped_ptr<base::DictionaryValue> legal_message) = 0; + + // Returns the result of an upload request. + virtual void OnDidUploadCard(AutofillClient::PaymentsRpcResult result) = 0; +}; + +// PaymentsClient issues Payments RPCs and manages responses and failure +// conditions. Only one request may be active at a time. Initiating a new +// request will cancel a pending request. +// Tests are located in +// src/components/autofill/content/browser/wallet/payments_client_unittest.cc. +class PaymentsClient : public net::URLFetcherDelegate, + public OAuth2TokenService::Consumer { + public: + // A collection of the information required to make a credit card unmask + // request. + struct UnmaskRequestDetails { + UnmaskRequestDetails(); + ~UnmaskRequestDetails(); + + CreditCard card; + std::string risk_data; + CardUnmaskDelegate::UnmaskResponse user_response; + }; + + // A collection of the information required to make a credit card upload + // request. + struct UploadRequestDetails { + UploadRequestDetails(); + ~UploadRequestDetails(); + + CreditCard card; + base::string16 cvc; + base::string16 context_token; + std::string risk_data; + std::string app_locale; + }; + + // |context_getter| is reference counted so it has no lifetime or ownership + // requirements. |delegate| must outlive |this|. |source_url| is the url + // of the merchant page. + PaymentsClient(net::URLRequestContextGetter* context_getter, + PaymentsClientDelegate* delegate); + + ~PaymentsClient() override; + + // Starts fetching the OAuth2 token in anticipation of future Payments + // requests. Called as an optimization, but not strictly necessary. Should + // *not* be called in advance of GetUploadDetails or UploadCard because + // identifying information should not be sent until the user has explicitly + // accepted an upload prompt. + void Prepare(); + + // The user has attempted to unmask a card with the given cvc. + void UnmaskCard(const UnmaskRequestDetails& request_details); + + // Determine if the user meets the Payments service's conditions for upload. + // If so, the required legal message for display will be returned via + // OnDidGetUploadDetails. + virtual void GetUploadDetails(const std::string& app_locale); + + // The user has indicated that they would like to upload a card with the given + // cvc. This request will fail server-side if a successful call to + // GetUploadDetails has not already been made. + virtual void UploadCard(const UploadRequestDetails& details); + + // Cancels and clears the current |request_|. + void CancelRequest(); + + private: + // Initiates a Payments request using the state in |request|. If + // |authenticate| is true, ensures that an OAuth token is avialble first. + // Takes ownership of |request|. + void IssueRequest(scoped_ptr<PaymentsRequest> request, bool authenticate); + + // net::URLFetcherDelegate: + void OnURLFetchComplete(const net::URLFetcher* source) override; + + // OAuth2TokenService::Consumer implementation. + void OnGetTokenSuccess(const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) override; + void OnGetTokenFailure(const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) override; + + // Creates |url_fetcher_| based on the current state of |request_|. + void InitializeUrlFetcher(); + + // Initiates a new OAuth2 token request. + void StartTokenFetch(bool invalidate_old); + + // Adds the token to |url_fetcher_| and starts the request. + void SetOAuth2TokenAndStartRequest(); + + // The context for the request. + scoped_refptr<net::URLRequestContextGetter> context_getter_; + + // Observer class that has its various On* methods called based on the results + // of a Payments request. + PaymentsClientDelegate* const delegate_; // must outlive |this|. + + // The current request. + scoped_ptr<PaymentsRequest> request_; + + // The fetcher being used to issue the current request. + scoped_ptr<net::URLFetcher> url_fetcher_; + + // The current OAuth2 token request object. + scoped_ptr<OAuth2TokenService::Request> access_token_request_; + + // The OAuth2 token, or empty if not fetched. + std::string access_token_; + + // True if |request_| has already retried due to a 401 response from the + // server. + bool has_retried_authorization_; + + base::WeakPtrFactory<PaymentsClient> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PaymentsClient); +}; + +} // namespace payments +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_CLIENT_H_ diff --git a/components/autofill/core/browser/payments/payments_request.h b/components/autofill/core/browser/payments/payments_request.h new file mode 100644 index 0000000..be59661 --- /dev/null +++ b/components/autofill/core/browser/payments/payments_request.h @@ -0,0 +1,46 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUEST_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUEST_H_ + +namespace autofill { + +class AutofillClient; + +namespace payments { + +class PaymentsClientDelegate; + +// Interface for the various Payments request types. +class PaymentsRequest { + public: + virtual ~PaymentsRequest() {} + + // Returns the URL path for this type of request. + virtual std::string GetRequestUrlPath() = 0; + + // Returns the content type that should be used in the HTTP request. + virtual std::string GetRequestContentType() = 0; + + // Returns the content that should be provided in the HTTP request. + virtual std::string GetRequestContent() = 0; + + // Parses the required elements of the HTTP response. + virtual void ParseResponse(scoped_ptr<base::DictionaryValue> response) = 0; + + // Returns true if all of the required elements were successfully retrieved by + // a call to ParseResponse. + virtual bool IsResponseComplete() = 0; + + // Invokes the appropriate callback in the delegate based on what type of + // request this is. + virtual void RespondToDelegate(PaymentsClientDelegate* delegate, + AutofillClient::PaymentsRpcResult result) = 0; +}; + +} // namespace payments +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUEST_H_ diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc index 0b1f279..f6c2278 100644 --- a/components/autofill/core/browser/test_autofill_client.cc +++ b/components/autofill/core/browser/test_autofill_client.cc @@ -48,11 +48,20 @@ void TestAutofillClient::ShowUnmaskPrompt( base::WeakPtr<CardUnmaskDelegate> delegate) { } -void TestAutofillClient::OnUnmaskVerificationResult(GetRealPanResult result) { +void TestAutofillClient::OnUnmaskVerificationResult(PaymentsRpcResult result) {} + +void TestAutofillClient::ConfirmSaveCreditCardLocally( + const base::Closure& callback) {} + +void TestAutofillClient::ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) { + callback.Run(); } -void TestAutofillClient::ConfirmSaveCreditCard( - const base::Closure& save_card_callback) { +void TestAutofillClient::LoadRiskData( + const base::Callback<void(const std::string&)>& callback) { + callback.Run("some risk data"); } bool TestAutofillClient::HasCreditCardScanFeature() { diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h index bc423d2..11bebc8 100644 --- a/components/autofill/core/browser/test_autofill_client.h +++ b/components/autofill/core/browser/test_autofill_client.h @@ -32,8 +32,13 @@ class TestAutofillClient : public AutofillClient { void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, base::WeakPtr<CardUnmaskDelegate> delegate) override; - void OnUnmaskVerificationResult(GetRealPanResult result) override; - void ConfirmSaveCreditCard(const base::Closure& save_card_callback) override; + void OnUnmaskVerificationResult(PaymentsRpcResult result) override; + void ConfirmSaveCreditCardLocally(const base::Closure& callback) override; + void ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) override; + void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(const CreditCardScanCallback& callback) override; void ShowRequestAutocompleteDialog(const FormData& form, diff --git a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc index 22628d9..1ac9687 100644 --- a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc +++ b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc @@ -20,19 +20,16 @@ namespace autofill { CardUnmaskPromptControllerImpl::CardUnmaskPromptControllerImpl( - const RiskDataCallback& risk_data_callback, PrefService* pref_service, bool is_off_the_record) - : risk_data_callback_(risk_data_callback), - pref_service_(pref_service), + : pref_service_(pref_service), new_card_link_clicked_(false), is_off_the_record_(is_off_the_record), card_unmask_view_(nullptr), unmasking_result_(AutofillClient::NONE), unmasking_initial_should_store_pan_(false), unmasking_number_of_attempts_(0), - weak_pointer_factory_(this) { -} + weak_pointer_factory_(this) {} CardUnmaskPromptControllerImpl::~CardUnmaskPromptControllerImpl() { if (card_unmask_view_) @@ -49,7 +46,6 @@ void CardUnmaskPromptControllerImpl::ShowPrompt( new_card_link_clicked_ = false; shown_timestamp_ = base::Time::Now(); pending_response_ = CardUnmaskDelegate::UnmaskResponse(); - LoadRiskFingerprint(); card_unmask_view_ = card_unmask_view; card_ = card; delegate_ = delegate; @@ -61,7 +57,7 @@ void CardUnmaskPromptControllerImpl::ShowPrompt( } bool CardUnmaskPromptControllerImpl::AllowsRetry( - AutofillClient::GetRealPanResult result) { + AutofillClient::PaymentsRpcResult result) { if (result == AutofillClient::NETWORK_ERROR || result == AutofillClient::PERMANENT_FAILURE) { return false; @@ -70,7 +66,7 @@ bool CardUnmaskPromptControllerImpl::AllowsRetry( } void CardUnmaskPromptControllerImpl::OnVerificationResult( - AutofillClient::GetRealPanResult result) { + AutofillClient::PaymentsRpcResult result) { if (!card_unmask_view_) return; @@ -205,8 +201,7 @@ void CardUnmaskPromptControllerImpl::OnUnmaskResponse( pending_response_.should_store_pan = false; } - if (!pending_response_.risk_data.empty()) - delegate_->OnUnmaskResponse(pending_response_); + delegate_->OnUnmaskResponse(pending_response_); } void CardUnmaskPromptControllerImpl::NewCardLinkClicked() { @@ -311,17 +306,4 @@ base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration() return base::TimeDelta::FromMilliseconds(500); } -void CardUnmaskPromptControllerImpl::LoadRiskFingerprint() { - risk_data_callback_.Run( - base::Bind(&CardUnmaskPromptControllerImpl::OnDidLoadRiskFingerprint, - weak_pointer_factory_.GetWeakPtr())); -} - -void CardUnmaskPromptControllerImpl::OnDidLoadRiskFingerprint( - const std::string& risk_data) { - pending_response_.risk_data = risk_data; - if (!pending_response_.cvc.empty()) - delegate_->OnUnmaskResponse(pending_response_); -} - } // namespace autofill diff --git a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h index 59ac7b7..df2b8c3 100644 --- a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h +++ b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h @@ -22,11 +22,7 @@ class CardUnmaskPromptView; class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { public: - typedef base::Callback<void(const base::Callback<void(const std::string&)>&)> - RiskDataCallback; - CardUnmaskPromptControllerImpl( - const RiskDataCallback& risk_data_callback, PrefService* pref_service, bool is_off_the_record); virtual ~CardUnmaskPromptControllerImpl(); @@ -36,7 +32,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { const CreditCard& card, base::WeakPtr<CardUnmaskDelegate> delegate); // The CVC the user entered went through validation. - void OnVerificationResult(AutofillClient::GetRealPanResult result); + void OnVerificationResult(AutofillClient::PaymentsRpcResult result); // CardUnmaskPromptController implementation. void OnUnmaskDialogClosed() override; @@ -57,21 +53,14 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { base::TimeDelta GetSuccessMessageDuration() const override; protected: - // Virtual so tests can suppress it. - virtual void LoadRiskFingerprint(); - - // Protected so tests can call it. - void OnDidLoadRiskFingerprint(const std::string& risk_data); - // Exposed for testing. CardUnmaskPromptView* view() { return card_unmask_view_; } private: - bool AllowsRetry(AutofillClient::GetRealPanResult result); + bool AllowsRetry(AutofillClient::PaymentsRpcResult result); void LogOnCloseEvents(); AutofillMetrics::UnmaskPromptEvent GetCloseReasonEvent(); - RiskDataCallback risk_data_callback_; PrefService* pref_service_; bool new_card_link_clicked_; bool is_off_the_record_; @@ -79,7 +68,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { base::WeakPtr<CardUnmaskDelegate> delegate_; CardUnmaskPromptView* card_unmask_view_; - AutofillClient::GetRealPanResult unmasking_result_; + AutofillClient::PaymentsRpcResult unmasking_result_; bool unmasking_initial_should_store_pan_; int unmasking_number_of_attempts_; base::Time shown_timestamp_; diff --git a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc index f45e79e..e0ff1ea 100644 --- a/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc +++ b/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc @@ -54,23 +54,14 @@ class TestCardUnmaskPromptView : public CardUnmaskPromptView { bool allow_retry) override {} }; -void TestGetRiskData(const base::Callback<void(const std::string&)>& callback) { - callback.Run("some risk data"); -} - class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl { public: explicit TestCardUnmaskPromptController( TestingPrefServiceSimple* pref_service) - : CardUnmaskPromptControllerImpl( - base::Bind(&TestGetRiskData), pref_service, false), + : CardUnmaskPromptControllerImpl(pref_service, false), can_store_locally_(true), weak_factory_(this) {} - void LoadRiskFingerprint() override { - OnDidLoadRiskFingerprint("risk aversion"); - } - bool CanStoreLocally() const override { return can_store_locally_; } void set_can_store_locally(bool can) { can_store_locally_ = can; } @@ -394,9 +385,9 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanResultSuccess) { base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.GetRealPanResult", - AutofillMetrics::GET_REAL_PAN_RESULT_SUCCESS, 1); + histogram_tester.ExpectBucketCount("Autofill.UnmaskPrompt.GetRealPanResult", + AutofillMetrics::PAYMENTS_RESULT_SUCCESS, + 1); } TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanTryAgainFailure) { @@ -407,7 +398,7 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanTryAgainFailure) { histogram_tester.ExpectBucketCount( "Autofill.UnmaskPrompt.GetRealPanResult", - AutofillMetrics::GET_REAL_PAN_RESULT_TRY_AGAIN_FAILURE, 1); + AutofillMetrics::PAYMENTS_RESULT_TRY_AGAIN_FAILURE, 1); } TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskingDurationResultSuccess) { diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc b/components/autofill/core/browser/wallet/real_pan_wallet_client.cc deleted file mode 100644 index d9055b5..0000000 --- a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h" - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "components/autofill/core/browser/credit_card.h" -#include "components/autofill/core/common/autofill_switches.h" -#include "components/data_use_measurement/core/data_use_user_data.h" -#include "google_apis/gaia/identity_provider.h" -#include "net/base/escape.h" -#include "net/base/load_flags.h" -#include "net/http/http_status_code.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_request_context_getter.h" - -namespace autofill { -namespace wallet { - -namespace { - -const char kUnmaskCardRequestFormat[] = - "requestContentType=application/json; charset=utf-8&request=%s" - "&s7e_13_cvc=%s"; - -const char kUnmaskCardRequestHost[] = "https://wallet.google.com"; -const char kUnmaskCardRequestHostSandbox[] = "https://sandbox.google.com"; -const char kUnmaskCardRequestPath[] = - "payments/apis-secure/creditcardservice/getrealpan?s7e_suffix=chromewallet"; - -const char kTokenServiceConsumerId[] = "real_pan_wallet_client"; -const char kWalletOAuth2Scope[] = - "https://www.googleapis.com/auth/wallet.chrome"; - -// This is mostly copied from wallet_service_url.cc, which is currently in -// content/, hence inaccessible from here. -bool IsWalletProductionEnabled() { - // If the command line flag exists, it takes precedence. - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - std::string sandbox_enabled( - command_line->GetSwitchValueASCII(switches::kWalletServiceUseSandbox)); - if (!sandbox_enabled.empty()) - return sandbox_enabled != "1"; - -#if defined(ENABLE_PROD_WALLET_SERVICE) - return true; -#else - return false; -#endif -} - -GURL GetUnmaskCardRequestUrl() { - if (base::CommandLine::ForCurrentProcess()->HasSwitch("sync-url")) { - if (IsWalletProductionEnabled()) { - LOG(ERROR) << "You are using production Wallet but you specified a " - "--sync-url. You likely want to disable the sync sandbox " - "or switch to sandbox Wallet. Both are controlled in " - "about:flags."; - } - } else if (!IsWalletProductionEnabled()) { - LOG(ERROR) << "You are using sandbox Wallet but you didn't specify a " - "--sync-url. You likely want to enable the sync sandbox " - "or switch to production Wallet. Both are controlled in " - "about:flags."; - } - - GURL base(IsWalletProductionEnabled() ? kUnmaskCardRequestHost - : kUnmaskCardRequestHostSandbox); - return base.Resolve(kUnmaskCardRequestPath); -} - -} // namespace - -RealPanWalletClient::RealPanWalletClient( - net::URLRequestContextGetter* context_getter, - Delegate* delegate) - : OAuth2TokenService::Consumer(kTokenServiceConsumerId), - context_getter_(context_getter), - delegate_(delegate), - has_retried_authorization_(false), - weak_ptr_factory_(this) { - DCHECK(delegate); -} - -RealPanWalletClient::~RealPanWalletClient() { -} - -void RealPanWalletClient::Prepare() { - if (access_token_.empty()) - StartTokenFetch(false); -} - -void RealPanWalletClient::UnmaskCard( - const CreditCard& card, - const CardUnmaskDelegate::UnmaskResponse& response) { - DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type()); - card_ = card; - response_ = response; - has_retried_authorization_ = false; - - CreateRequest(); - if (access_token_.empty()) - StartTokenFetch(false); - else - SetOAuth2TokenAndStartRequest(); -} - -void RealPanWalletClient::CancelRequest() { - request_.reset(); -} - -void RealPanWalletClient::OnURLFetchComplete(const net::URLFetcher* source) { - DCHECK_EQ(source, request_.get()); - - // |request_|, which is aliased to |source|, might continue to be used in this - // |method, but should be freed once control leaves the method. - scoped_ptr<net::URLFetcher> scoped_request(request_.Pass()); - scoped_ptr<base::DictionaryValue> response_dict; - int response_code = source->GetResponseCode(); - std::string data; - source->GetResponseAsString(&data); - VLOG(2) << "Got data: " << data; - - std::string real_pan; - AutofillClient::GetRealPanResult result = AutofillClient::SUCCESS; - - switch (response_code) { - // Valid response. - case net::HTTP_OK: { - std::string error_code; - scoped_ptr<base::Value> message_value = base::JSONReader::Read(data); - if (message_value.get() && - message_value->IsType(base::Value::TYPE_DICTIONARY)) { - response_dict.reset( - static_cast<base::DictionaryValue*>(message_value.release())); - response_dict->GetString("pan", &real_pan); - response_dict->GetString("error.code", &error_code); - } - - if (base::LowerCaseEqualsASCII(error_code, "internal")) - result = AutofillClient::TRY_AGAIN_FAILURE; - else if (real_pan.empty() || !error_code.empty()) - result = AutofillClient::PERMANENT_FAILURE; - - break; - } - - case net::HTTP_UNAUTHORIZED: { - if (has_retried_authorization_) { - result = AutofillClient::PERMANENT_FAILURE; - break; - } - has_retried_authorization_ = true; - - CreateRequest(); - StartTokenFetch(true); - return; - } - - // TODO(estade): is this actually how network connectivity issues are - // reported? - case net::HTTP_REQUEST_TIMEOUT: { - result = AutofillClient::NETWORK_ERROR; - break; - } - - // Handle anything else as a generic (permanent) failure. - default: { - result = AutofillClient::PERMANENT_FAILURE; - break; - } - } - - if (real_pan.empty()) { - VLOG(1) << "Wallet returned error: " << response_code - << " with data: " << data; - } - - DCHECK_EQ(result != AutofillClient::SUCCESS, real_pan.empty()); - delegate_->OnDidGetRealPan(result, real_pan); -} - -void RealPanWalletClient::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) { - DCHECK_EQ(request, access_token_request_.get()); - access_token_ = access_token; - if (request_) - SetOAuth2TokenAndStartRequest(); - - access_token_request_.reset(); -} - -void RealPanWalletClient::OnGetTokenFailure( - const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) { - DCHECK_EQ(request, access_token_request_.get()); - VLOG(1) << "Unhandled OAuth2 error: " << error.ToString(); - if (request_) { - request_.reset(); - delegate_->OnDidGetRealPan(AutofillClient::PERMANENT_FAILURE, - std::string()); - } - access_token_request_.reset(); -} - -void RealPanWalletClient::CreateRequest() { - request_ = net::URLFetcher::Create(0, GetUnmaskCardRequestUrl(), - net::URLFetcher::POST, this); - data_use_measurement::DataUseUserData::AttachToFetcher( - request_.get(), data_use_measurement::DataUseUserData::AUTOFILL); - request_->SetRequestContext(context_getter_.get()); - request_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DISABLE_CACHE); - - base::DictionaryValue request_dict; - request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc"); - request_dict.SetString("credit_card_id", card_.server_id()); - scoped_ptr<base::DictionaryValue> risk_data(new base::DictionaryValue()); - if (response_.providing_risk_advisory_data) { - risk_data->SetString("message_type", "RISK_ADVISORY_DATA"); - risk_data->SetString("encoding_type", "BASE_64_URL"); - } else { - risk_data->SetString("message_type", "BROWSER_NATIVE_FINGERPRINTING"); - risk_data->SetString("encoding_type", "BASE_64"); - } - risk_data->SetString("value", response_.risk_data); - request_dict.Set("risk_data_encoded", risk_data.Pass()); - request_dict.Set("context", make_scoped_ptr(new base::DictionaryValue())); - - int value = 0; - if (base::StringToInt(response_.exp_month, &value)) - request_dict.SetInteger("expiration_month", value); - if (base::StringToInt(response_.exp_year, &value)) - request_dict.SetInteger("expiration_year", value); - - std::string json_request; - base::JSONWriter::Write(request_dict, &json_request); - std::string post_body = - base::StringPrintf(kUnmaskCardRequestFormat, - net::EscapeUrlEncodedData(json_request, true).c_str(), - net::EscapeUrlEncodedData( - base::UTF16ToASCII(response_.cvc), true).c_str()); - VLOG(3) << "getrealpan request body: " << post_body; - request_->SetUploadData("application/x-www-form-urlencoded", post_body); -} - -void RealPanWalletClient::StartTokenFetch(bool invalidate_old) { - // We're still waiting for the last request to come back. - if (!invalidate_old && access_token_request_) - return; - - OAuth2TokenService::ScopeSet wallet_scopes; - wallet_scopes.insert(kWalletOAuth2Scope); - IdentityProvider* identity = delegate_->GetIdentityProvider(); - if (invalidate_old) { - DCHECK(!access_token_.empty()); - identity->GetTokenService()->InvalidateAccessToken( - identity->GetActiveAccountId(), wallet_scopes, access_token_); - } - access_token_.clear(); - access_token_request_ = identity->GetTokenService()->StartRequest( - identity->GetActiveAccountId(), wallet_scopes, this); -} - -void RealPanWalletClient::SetOAuth2TokenAndStartRequest() { - request_->AddExtraRequestHeader(net::HttpRequestHeaders::kAuthorization + - std::string(": Bearer ") + access_token_); - - request_->Start(); -} - -} // namespace wallet -} // namespace autofill diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.h b/components/autofill/core/browser/wallet/real_pan_wallet_client.h deleted file mode 100644 index b656d4a..0000000 --- a/components/autofill/core/browser/wallet/real_pan_wallet_client.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WALLET_REAL_PAN_WALLET_CLIENT_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_WALLET_REAL_PAN_WALLET_CLIENT_H_ - -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "components/autofill/core/browser/autofill_client.h" -#include "components/autofill/core/browser/card_unmask_delegate.h" -#include "components/autofill/core/browser/credit_card.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "net/url_request/url_fetcher_delegate.h" - -class IdentityProvider; - -namespace net { -class URLFetcher; -class URLRequestContextGetter; -} - -namespace autofill { - -namespace wallet { - -// RealPanWalletClient is modelled on WalletClient. Whereas the latter is used -// for requestAutocomplete-related requests, RealPanWalletClient is used to -// import user data from Wallet for normal web Autofill. -// Tests: See content/browser/wallet/real_pan_wallet_client_unittest.cc -class RealPanWalletClient : public net::URLFetcherDelegate, - public OAuth2TokenService::Consumer { - public: - class Delegate { - public: - // The identity provider used to get OAuth2 tokens. - virtual IdentityProvider* GetIdentityProvider() = 0; - - // Returns the real PAN retrieved from Wallet. |real_pan| will be empty - // on failure. - virtual void OnDidGetRealPan(AutofillClient::GetRealPanResult result, - const std::string& real_pan) = 0; - }; - - // |context_getter| is reference counted so it has no lifetime or ownership - // requirements. |delegate| must outlive |this|. |source_url| is the url - // of the merchant page. - RealPanWalletClient(net::URLRequestContextGetter* context_getter, - Delegate* delegate); - - ~RealPanWalletClient() override; - - // Starts fetching the OAuth2 token in anticipation of future wallet requests. - // Called as an optimization, but not strictly necessary. - void Prepare(); - - // The user has attempted to unmask a card with the given cvc. - void UnmaskCard(const CreditCard& card, - const CardUnmaskDelegate::UnmaskResponse& response); - - // Cancels and clears the current |request_|. - void CancelRequest(); - - private: - // net::URLFetcherDelegate: - void OnURLFetchComplete(const net::URLFetcher* source) override; - - // OAuth2TokenService::Consumer implementation. - void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) override; - - // Creates |request_| from |card_| and |response_|. - void CreateRequest(); - - // Initiates a new OAuth2 token request. - void StartTokenFetch(bool invalidate_old); - - // Adds the token to |request_| and starts the request. - void SetOAuth2TokenAndStartRequest(); - - // The context for the request. - scoped_refptr<net::URLRequestContextGetter> context_getter_; - - // Observer class that has its various On* methods called based on the results - // of a request to Online Wallet. - Delegate* const delegate_; // must outlive |this|. - - // The card and response for the latest unmask request. - CreditCard card_; - CardUnmaskDelegate::UnmaskResponse response_; - - // The current Wallet request object. - scoped_ptr<net::URLFetcher> request_; - - // The current OAuth2 token request object; - scoped_ptr<OAuth2TokenService::Request> access_token_request_; - - // The OAuth2 token, or empty if not fetched. - std::string access_token_; - - // True if |this| has already retried due to a 401 response from the server. - bool has_retried_authorization_; - - base::WeakPtrFactory<RealPanWalletClient> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(RealPanWalletClient); -}; - -} // namespace wallet -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WALLET_REAL_PAN_WALLET_CLIENT_H_ diff --git a/components/autofill/core/common/autofill_switches.cc b/components/autofill/core/common/autofill_switches.cc index 79e8db4..63edd24 100644 --- a/components/autofill/core/common/autofill_switches.cc +++ b/components/autofill/core/common/autofill_switches.cc @@ -24,6 +24,10 @@ const char kDisableFullFormAutofillIOS[] = "disable-full-form-autofill-ios"; const char kDisableOfferStoreUnmaskedWalletCards[] = "disable-offer-store-unmasked-wallet-cards"; +// Disables offering to upload credit cards. +const char kDisableOfferUploadCreditCards[] = + "disable-offer-upload-credit-cards"; + // Disables password generation when we detect that the user is going through // account creation. const char kDisablePasswordGeneration[] = "disable-password-generation"; @@ -53,6 +57,9 @@ const char kEnableFullFormAutofillIOS[] = "enable-full-form-autofill-ios"; const char kEnableOfferStoreUnmaskedWalletCards[] = "enable-offer-store-unmasked-wallet-cards"; +// Enables offering to upload credit cards. +const char kEnableOfferUploadCreditCards[] = "enable-offer-upload-credit-cards"; + // Enables password generation when we detect that the user is going through // account creation. const char kEnablePasswordGeneration[] = "enable-password-generation"; diff --git a/components/autofill/core/common/autofill_switches.h b/components/autofill/core/common/autofill_switches.h index b4e7a35..1c34827 100644 --- a/components/autofill/core/common/autofill_switches.h +++ b/components/autofill/core/common/autofill_switches.h @@ -16,6 +16,7 @@ extern const char kDisableCreditCardScan[]; extern const char kDisableFillOnAccountSelect[]; extern const char kDisableFullFormAutofillIOS[]; extern const char kDisableOfferStoreUnmaskedWalletCards[]; +extern const char kDisableOfferUploadCreditCards[]; extern const char kDisablePasswordGeneration[]; extern const char kDisableSingleClickAutofill[]; extern const char kEnableCreditCardScan[]; @@ -23,6 +24,7 @@ extern const char kEnableFillOnAccountSelect[]; extern const char kEnableFillOnAccountSelectNoHighlighting[]; extern const char kEnableFullFormAutofillIOS[]; extern const char kEnableOfferStoreUnmaskedWalletCards[]; +extern const char kEnableOfferUploadCreditCards[]; extern const char kEnablePasswordGeneration[]; extern const char kEnableSingleClickAutofill[]; extern const char kEnableSuggestionsWithSubstringMatch[]; diff --git a/components/components_tests.gyp b/components/components_tests.gyp index f9975f0..bb5cb2c 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -19,8 +19,8 @@ 'autofill/content/browser/content_autofill_driver_unittest.cc', 'autofill/content/browser/request_autocomplete_manager_unittest.cc', 'autofill/content/browser/wallet/full_wallet_unittest.cc', - 'autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc', 'autofill/content/browser/wallet/wallet_address_unittest.cc', + 'autofill/content/browser/wallet/payments_client_unittest.cc', 'autofill/content/browser/wallet/wallet_service_url_unittest.cc', 'autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc', 'autofill/core/browser/address_field_unittest.cc', diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.h b/ios/chrome/browser/ui/autofill/autofill_client_ios.h index 856bc24..34d51a11 100644 --- a/ios/chrome/browser/ui/autofill/autofill_client_ios.h +++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.h @@ -53,8 +53,13 @@ class AutofillClientIOS : public AutofillClient { void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, base::WeakPtr<CardUnmaskDelegate> delegate) override; - void OnUnmaskVerificationResult(GetRealPanResult result) override; - void ConfirmSaveCreditCard(const base::Closure& save_card_callback) override; + void OnUnmaskVerificationResult(PaymentsRpcResult result) override; + void ConfirmSaveCreditCardLocally(const base::Closure& callback) override; + void ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) override; + void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(const CreditCardScanCallback& callback) override; void ShowRequestAutocompleteDialog( diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm index 7f8afc5..1aefa98 100644 --- a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm +++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm @@ -21,11 +21,6 @@ namespace autofill { -void GetRiskDataWithCallback( - const base::Callback<void(const std::string&)>& callback) { - callback.Run(ios::GetChromeBrowserProvider()->GetRiskData()); -} - AutofillClientIOS::AutofillClientIOS( ios::ChromeBrowserState* browser_state, infobars::InfoBarManager* infobar_manager, @@ -37,10 +32,8 @@ AutofillClientIOS::AutofillClientIOS( bridge_(bridge), password_generation_manager_(password_generation_manager), identity_provider_(identity_provider.Pass()), - unmask_controller_(base::Bind(&GetRiskDataWithCallback), - browser_state->GetPrefs(), - browser_state->IsOffTheRecord()) { -} + unmask_controller_(browser_state->GetPrefs(), + browser_state->IsOffTheRecord()) {} AutofillClientIOS::~AutofillClientIOS() { HideAutofillPopup(); @@ -79,18 +72,29 @@ void AutofillClientIOS::ShowUnmaskPrompt( delegate); } -void AutofillClientIOS::OnUnmaskVerificationResult(GetRealPanResult result) { +void AutofillClientIOS::OnUnmaskVerificationResult(PaymentsRpcResult result) { unmask_controller_.OnVerificationResult(result); } -void AutofillClientIOS::ConfirmSaveCreditCard( - const base::Closure& save_card_callback) { +void AutofillClientIOS::ConfirmSaveCreditCardLocally( + const base::Closure& callback) { // This method is invoked synchronously from // AutofillManager::OnFormSubmitted(); at the time of detecting that a form // was submitted, the WebContents is guaranteed to be live. Since the // InfoBarService is a WebContentsUserData, it must also be alive at this // time. - AutofillCCInfoBarDelegate::Create(infobar_manager_, this, save_card_callback); + AutofillCCInfoBarDelegate::Create(infobar_manager_, this, callback); +} + +void AutofillClientIOS::ConfirmSaveCreditCardToCloud( + const base::Closure& callback, + scoped_ptr<base::DictionaryValue> legal_message) { + NOTIMPLEMENTED(); +} + +void AutofillClientIOS::LoadRiskData( + const base::Callback<void(const std::string&)>& callback) { + callback.Run(ios::GetChromeBrowserProvider()->GetRiskData()); } bool AutofillClientIOS::HasCreditCardScanFeature() { |