0

[VCN] Add card info chip to the unmask cvc prompt

Mock: https://docs.google.com/presentation/d/1LZHzYcuTaVMZ7h3H71k3NZ2hB1VZMbuPbaBaGAqlCug/edit?pli=1&resourcekey=0-MBuDkOkUtHPUtAcPN9VPKg#slide=id.g219780aa2d0_0_1

Implementation: https://screenshot.googleplex.com/BevdsXaVnAYYyFM

Uses the TableViewDetailIconItem. The main text will always be the card
product/network name, while the detail text will be the expiration date
of the card if it is an actual card, or "Virtual card" if it is a
virtual card. The icon will use card art image icon first then the
static network icon.

Bug: b/303715882
Change-Id: I070322cf19cc4f5fbdd788961908158089319277
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5255821
Commit-Queue: Siyu An <siyua@chromium.org>
Reviewed-by: Tommy Martino <tmartino@chromium.org>
Reviewed-by: Slobodan Pejic <slobodan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1255846}
This commit is contained in:
Siyu An
2024-02-03 00:29:38 +00:00
committed by Chromium LUCI CQ
parent a363830a97
commit bc7b9a4bdc
14 changed files with 202 additions and 53 deletions

@ -38,6 +38,9 @@ class CardUnmaskPromptController {
virtual std::u16string GetOkButtonLabel() const = 0;
virtual int GetCvcImageRid() const = 0;
virtual bool ShouldRequestExpirationDate() const = 0;
// TODO(b/303715882): Should consider removing these detailed information
// accessors and instead return the credit card object directly. Only exposing
// necessary information is good but this list is growing larger.
#if BUILDFLAG(IS_ANDROID)
virtual Suggestion::Icon GetCardIcon() const = 0;
virtual std::u16string GetCardName() const = 0;
@ -52,6 +55,7 @@ class CardUnmaskPromptController {
virtual base::TimeDelta GetSuccessMessageDuration() const = 0;
virtual AutofillClient::PaymentsRpcResult GetVerificationResult() const = 0;
virtual bool IsVirtualCard() const = 0;
virtual const CreditCard& GetCreditCard() const = 0;
#if !BUILDFLAG(IS_IOS)
virtual int GetCvcTooltipResourceId() = 0;
#endif

@ -439,6 +439,10 @@ bool CardUnmaskPromptControllerImpl::IsVirtualCard() const {
return card_.record_type() == CreditCard::RecordType::kVirtualCard;
}
const CreditCard& CardUnmaskPromptControllerImpl::GetCreditCard() const {
return card_;
}
#if !BUILDFLAG(IS_IOS)
int CardUnmaskPromptControllerImpl::GetCvcTooltipResourceId() {
return IsCvcInFront()

@ -86,6 +86,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController {
base::TimeDelta GetSuccessMessageDuration() const override;
AutofillClient::PaymentsRpcResult GetVerificationResult() const override;
bool IsVirtualCard() const override;
const CreditCard& GetCreditCard() const override;
#if !BUILDFLAG(IS_IOS)
int GetCvcTooltipResourceId() override;
#endif

@ -84,6 +84,7 @@ source_set("bridges") {
"//base",
"//components/autofill/core/browser",
"//components/strings",
"//ios/chrome/browser/autofill/model/credit_card:credit_card",
"//ios/chrome/browser/net/model:crurl",
"//ios/chrome/browser/shared/ui/list_model",
"//ios/chrome/browser/shared/ui/table_view:utils",
@ -91,6 +92,7 @@ source_set("bridges") {
"//ios/chrome/browser/shared/ui/util",
"//ios/chrome/browser/ui/autofill/cells",
"//ios/chrome/common/ui/colors",
"//ios/chrome/common/ui/table_view:cells_constants",
"//ui/base",
"//url",
]

@ -12,16 +12,20 @@
@class CardUnmaskPromptViewController;
@class UIViewController;
@class UINavigationController;
@class CreditCardData;
@class UIImage;
namespace autofill {
class CardUnmaskPromptController;
class PersonalDataManager;
// iOS implementation of the unmask prompt UI.
class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
public:
CardUnmaskPromptViewBridge(CardUnmaskPromptController* controller,
UIViewController* base_view_controller);
UIViewController* base_view_controller,
PersonalDataManager* personal_data_manager);
CardUnmaskPromptViewBridge(const CardUnmaskPromptViewBridge&) = delete;
CardUnmaskPromptViewBridge& operator=(const CardUnmaskPromptViewBridge&) =
delete;
@ -44,6 +48,8 @@ class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
// This call destroys `this`.
void NavigationControllerDismissed();
CreditCardData* credit_card_data() { return credit_card_data_; }
protected:
// The presented UINavigationController containing `prompt_view_controller_`.
UINavigationController* navigation_controller_;
@ -59,9 +65,15 @@ class CardUnmaskPromptViewBridge : public CardUnmaskPromptView {
// dismissing its own UI elements.
void DeleteSelf();
UIImage* GetCardIcon();
// Weak reference to the view controller used to present UI.
__weak UIViewController* base_view_controller_;
raw_ptr<PersonalDataManager> personal_data_manager_;
CreditCardData* credit_card_data_;
base::WeakPtrFactory<CardUnmaskPromptViewBridge> weak_ptr_factory_;
};

@ -4,9 +4,14 @@
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
#import "base/apple/foundation_util.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/data_model/credit_card.h"
#import "components/autofill/core/browser/personal_data_manager.h"
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
#import "ios/chrome/browser/autofill/model/credit_card/credit_card_data.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.h"
namespace autofill {
@ -15,11 +20,17 @@ namespace autofill {
CardUnmaskPromptViewBridge::CardUnmaskPromptViewBridge(
CardUnmaskPromptController* controller,
UIViewController* base_view_controller)
UIViewController* base_view_controller,
PersonalDataManager* personal_data_manager)
: controller_(controller),
base_view_controller_(base_view_controller),
personal_data_manager_(personal_data_manager),
weak_ptr_factory_(this) {
DCHECK(controller_);
CHECK(controller_);
CHECK(personal_data_manager_);
credit_card_data_ =
[[CreditCardData alloc] initWithCreditCard:&(controller_->GetCreditCard())
icon:GetCardIcon()];
}
CardUnmaskPromptViewBridge::~CardUnmaskPromptViewBridge() {
@ -100,4 +111,21 @@ void CardUnmaskPromptViewBridge::DeleteSelf() {
delete this;
}
UIImage* CardUnmaskPromptViewBridge::GetCardIcon() {
// Firstly check if card art image is available.
const CreditCard& credit_card = GetController()->GetCreditCard();
gfx::Image* image = personal_data_manager_->GetCreditCardArtImageForUrl(
credit_card.card_art_url());
if (image) {
return image->ToUIImage();
}
// Use card network icon.
Suggestion::Icon icon = credit_card.CardIconForAutofillSuggestion();
return icon == Suggestion::Icon::kNoIcon
? nil
: NativeImage(CreditCard::IconResourceId(
credit_card.CardIconForAutofillSuggestion()));
}
} // namespace autofill

@ -9,8 +9,11 @@
#import "base/memory/raw_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
#import "components/autofill/core/common/autofill_payments_features.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/autofill/model/credit_card/credit_card_data.h"
#import "ios/chrome/browser/net/model/crurl.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item_delegate.h"
@ -21,6 +24,7 @@
#import "ios/chrome/browser/ui/autofill/cells/expiration_date_edit_item.h"
#import "ios/chrome/browser/ui/autofill/cells/expiration_date_edit_item_delegate.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"
@ -29,6 +33,11 @@ NSString* const kCardUnmaskPromptTableViewAccessibilityID =
namespace {
BOOL VirtualCardFeatureEnabled() {
return base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableVirtualCards);
}
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierHeader = kSectionIdentifierEnumZero,
SectionIdentifierInputs,
@ -36,6 +45,7 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) {
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeHeader = kItemTypeEnumZero,
ItemTypeCardInfo,
ItemTypeCVCInput,
ItemTypeFooter,
ItemTypeExpirationDateInput,
@ -73,6 +83,8 @@ const char kFooterDummyLinkTarget[] = "about:blank";
// Owns `self`. A value of nullptr means the view controller is dismissed or
// about to be dismissed.
raw_ptr<autofill::CardUnmaskPromptViewBridge> _bridge; // weak
// Model of the card info cell.
TableViewDetailIconItem* _cardInfoItem;
// Model of the CVC input cell.
TableViewTextEditItem* _CVCInputItem;
// Model of the footer.
@ -161,6 +173,14 @@ const char kFooterDummyLinkTarget[] = "about:blank";
[model setHeader:_headerItem
forSectionWithIdentifier:SectionIdentifierHeader];
if (VirtualCardFeatureEnabled()) {
_cardInfoItem = [self createCardInfoItem];
if (_cardInfoItem != nil) {
[self.tableViewModel addItem:_cardInfoItem
toSectionWithIdentifier:SectionIdentifierHeader];
}
}
[model addSectionWithIdentifier:SectionIdentifierInputs];
_CVCInputItem = [self createCVCInputItem];
@ -380,6 +400,24 @@ const char kFooterDummyLinkTarget[] = "about:blank";
return confirmButton;
}
// Returns the model for the card info cell.
- (TableViewDetailIconItem*)createCardInfoItem {
if (_bridge == nullptr) {
return nil;
}
CreditCardData* data = _bridge->credit_card_data();
TableViewDetailIconItem* cardInfoItem =
[[TableViewDetailIconItem alloc] initWithType:ItemTypeCardInfo];
cardInfoItem.textLayoutConstraintAxis = UILayoutConstraintAxisVertical;
cardInfoItem.text = data.cardNameAndLastFourDigits;
cardInfoItem.detailText = data.cardDetails;
cardInfoItem.iconBackgroundColor = UIColor.clearColor;
cardInfoItem.iconImage = data.icon;
return cardInfoItem;
}
// Returns the model for the CVC input cell.
- (TableViewTextEditItem*)createCVCInputItem {
if (_bridge == nullptr) {
@ -615,6 +653,16 @@ const char kFooterDummyLinkTarget[] = "about:blank";
rowCell.identifyingIconButton.isAccessibilityElement = NO;
}
if (rowItemType == ItemTypeCardInfo) {
TableViewDetailIconCell* rowCell =
base::apple::ObjCCastStrict<TableViewDetailIconCell>(cell);
rowCell.backgroundColor = [UIColor colorNamed:kGrey200Color];
rowCell.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
rowCell.textLabel.numberOfLines = 1;
rowCell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
rowCell.detailTextLabel.numberOfLines = 1;
}
return cell;
}

@ -8,9 +8,14 @@
#import "base/apple/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/test/scoped_feature_list.h"
#import "components/autofill/core/browser/autofill_test_utils.h"
#import "components/autofill/core/browser/payments/card_unmask_delegate.h"
#import "components/autofill/core/browser/test_personal_data_manager.h"
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h"
#import "components/autofill/core/common/autofill_payments_features.h"
#import "components/prefs/testing_pref_service.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h"
@ -54,6 +59,11 @@ class MockCardUnmaskPromptController
bool was_checkbox_visible),
(override));
MOCK_METHOD(const autofill::CreditCard&,
GetCreditCard,
(),
(const, override));
MOCK_METHOD(bool, ShouldRequestExpirationDate, (), (const, override));
MOCK_METHOD(bool,
@ -66,9 +76,11 @@ class MockCardUnmaskPromptViewBridge
: public autofill::CardUnmaskPromptViewBridge {
public:
explicit MockCardUnmaskPromptViewBridge(
autofill::CardUnmaskPromptControllerImpl* controller)
autofill::CardUnmaskPromptControllerImpl* controller,
autofill::TestPersonalDataManager* personal_data_manager)
: CardUnmaskPromptViewBridge(controller,
[[UIViewController alloc] init]) {}
[[UIViewController alloc] init],
personal_data_manager) {}
MockCardUnmaskPromptViewBridge(const MockCardUnmaskPromptViewBridge&) =
delete;
@ -87,11 +99,17 @@ class MockCardUnmaskPromptViewBridge
};
class CardUnmaskPromptViewControllerTest
: public LegacyChromeTableViewControllerTest {
: public LegacyChromeTableViewControllerTest,
public ::testing::WithParamInterface<bool> {
protected:
CardUnmaskPromptViewControllerTest() = default;
CardUnmaskPromptViewControllerTest()
: virtual_card_enrollment_enabled_(GetParam()) {}
void SetUp() override {
feature_list_.InitWithFeatureState(
autofill::features::kAutofillEnableVirtualCards,
virtual_card_enrollment_enabled_);
LegacyChromeTableViewControllerTest::SetUp();
root_view_controller_ = [[UIViewController alloc] init];
@ -100,10 +118,15 @@ class CardUnmaskPromptViewControllerTest
card_unmask_prompt_controller_ =
std::make_unique<NiceMock<MockCardUnmaskPromptController>>(
pref_service_.get());
ON_CALL(*card_unmask_prompt_controller_, GetCreditCard)
.WillByDefault(testing::ReturnRef(card_));
personal_data_manager_ =
std::make_unique<autofill::TestPersonalDataManager>();
card_unmask_prompt_bridge_ =
std::make_unique<NiceMock<MockCardUnmaskPromptViewBridge>>(
card_unmask_prompt_controller_.get());
card_unmask_prompt_controller_.get(), personal_data_manager_.get());
CreateController();
}
@ -149,6 +172,17 @@ class CardUnmaskPromptViewControllerTest
cellForRowAtIndexPath:cvc_index_path];
}
// Fetches the card info cell from the tableView's datasource.
TableViewDetailIconCell* GetCardInfoCell() {
return virtual_card_enrollment_enabled_ ? GetCell(/*item=*/0, /*section*/ 0)
: nil;
}
TableViewDetailIconItem* GetCardInfoItem() {
return virtual_card_enrollment_enabled_ ? GetItem(/*item=*/0, /*section=*/0)
: nil;
}
// Fetches the CVC input cell from the tableView's datasource.
TableViewTextEditCell* GetCVCInputCell() {
return GetCell(/*item=*/0, /*section*/ 1);
@ -274,26 +308,32 @@ class CardUnmaskPromptViewControllerTest
EXPECT_EQ(footer.urls.count, 1LU);
}
// Verifies that the correct items are in the tableViewModel when the
// expiration date form is displayed.
void CheckUpdateExpirationDateForm() {
// Verifies that the correct items are in the tableViewModel.
void CheckTableViewModel(bool with_expiration_date_item) {
// Check expected number of sections and items.
EXPECT_EQ(NumberOfSections(), 2);
// First section only has a header.
EXPECT_EQ(NumberOfItemsInSection(0), 0);
// CVC and expiration date fields.
EXPECT_EQ(NumberOfItemsInSection(1), 2);
// First section only has a header when experiment is disabled. It has two
// (a header and a card info cell) when experiment is enabled.
EXPECT_EQ(NumberOfItemsInSection(0),
virtual_card_enrollment_enabled_ ? 1 : 0);
// CVC and expiration date fields if applicable.
EXPECT_EQ(NumberOfItemsInSection(1), with_expiration_date_item ? 2 : 1);
CheckHeaderAndInstructions();
// Verifying card info is in model.
EXPECT_EQ(virtual_card_enrollment_enabled_, !!GetCardInfoItem());
// Verifing CVC field is in model.
EXPECT_TRUE(CVCInputItem());
// Verifying expiration date field is in model.
EXPECT_TRUE(ExpirationDateItem());
// Verifying expiration date field is updated correctly.
EXPECT_EQ(with_expiration_date_item, !!ExpirationDateItem());
// Footer shouldn't be in model.
EXPECT_FALSE(FooterItem());
}
// Validates that the first responder has the given accessibility identifier.
void CheckFirstResponderHasAccessibilityIdentifier(
NSString* accessibility_identifier) {
@ -309,30 +349,26 @@ class CardUnmaskPromptViewControllerTest
std::unique_ptr<NiceMock<MockCardUnmaskPromptController>>
card_unmask_prompt_controller_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<autofill::TestPersonalDataManager> personal_data_manager_;
ScopedKeyWindow scoped_window_;
UIViewController* root_view_controller_;
base::test::ScopedFeatureList feature_list_;
autofill::CreditCard card_ = autofill::test::GetMaskedServerCard();
bool virtual_card_enrollment_enabled_;
};
INSTANTIATE_TEST_SUITE_P(,
CardUnmaskPromptViewControllerTest,
::testing::Bool());
} // namespace
// Validates that the CVC form is displayed as the initial state of the
// controller when the card is not expired.
TEST_F(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
TEST_P(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
CheckController();
// Check expected number of sections and items.
EXPECT_EQ(NumberOfSections(), 2);
// First section only has a header.
EXPECT_EQ(NumberOfItemsInSection(0), 0);
// CVC and expiration date fields.
EXPECT_EQ(NumberOfItemsInSection(1), 1);
CheckHeaderAndInstructions();
// Verifing CVC field is in model.
EXPECT_TRUE(CVCInputItem());
// Footer shouldn't be in model.
EXPECT_FALSE(FooterItem());
CheckTableViewModel(/*with_expiration_date_item=*/false);
// Confirm button should be disabled until valid input is entered.
EXPECT_FALSE(ConfirmButton().enabled);
@ -344,7 +380,7 @@ TEST_F(CardUnmaskPromptViewControllerTest, CVCFormDisplayedAsInitialState) {
// Validates that the Expiration Date form is displayed as the initial state of
// the controller when the card is expired.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
ExpirationDateFormDisplayedAsInitialState) {
// Recreate controller for the expiration date form state.
ResetController();
@ -354,7 +390,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
CreateController();
CheckUpdateExpirationDateForm();
CheckTableViewModel(/*with_expiration_date_item=*/true);
// Add controller to view hierarchy to verify CVC field is focused.
PresentController();
@ -363,7 +399,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
// Validates that the tableViewModel is properly setup for displaying update
// expiration date form.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
UpdateExpirationDateFormStateHasExpectedItems) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
@ -374,7 +410,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
[prompt_controller showUpdateExpirationDateForm];
CheckUpdateExpirationDateForm();
CheckTableViewModel(/*with_expiration_date_item=*/true);
// Add controller to view hierarchy to verify Expiration Date field is
// focused.
@ -384,7 +420,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
// Validates the model is properly setup for displaying the expiration card link
// in the controller's footer.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
ShowUpdateExpirationCardLinkAddsFooterWithLink) {
// Footer shouldn't be in model before link is shown.
EXPECT_FALSE(FooterItem());
@ -399,7 +435,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
// Validates that the loading state displays an activity indicator in the
// navigation bar and disables interactions with the input fields.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
ShowLoadingStateDisplaysActivityIndicatorAndDisablesInteractions) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
@ -414,7 +450,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
}
// Validates that an alert is presented for the error state.
TEST_F(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
TEST_P(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
PresentController();
auto* prompt_controller =
@ -441,13 +477,13 @@ TEST_F(CardUnmaskPromptViewControllerTest, ShowErrorPresentsAlert) {
// Verifies that submitting the CVC form forwards the CVC value to
// CardUnmaskPromptController.
TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingCVCForm) {
TEST_P(CardUnmaskPromptViewControllerTest, TestSubmittingCVCForm) {
CheckSubmittingForm(/*CVC=*/@"123");
}
// Verifies that submitting the update expiration date form forwards CVC and
// expiration date to CardUnmaskPromptController/
TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
TEST_P(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
[prompt_controller showUpdateExpirationDateForm];
@ -456,7 +492,7 @@ TEST_F(CardUnmaskPromptViewControllerTest, TestSubmittingExpirationDateForm) {
}
// Verifies that the controller is dismissed after an error.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
TestDismissingViewControllerAfterError) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
@ -467,7 +503,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
}
// Verifies that the expiration date link is displayed after an error.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
TestShowingUpdateExpirationDateLinkAfterError) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
@ -478,7 +514,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
}
// Verifies that the expiration date form is displayed after an error.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
TestShowingUpdateExpirationDateFormAfterError) {
auto* prompt_controller =
static_cast<CardUnmaskPromptViewController*>(controller());
@ -488,11 +524,11 @@ TEST_F(CardUnmaskPromptViewControllerTest,
[prompt_controller onErrorAlertDismissedAndShouldCloseOnDismiss:NO];
CheckUpdateExpirationDateForm();
CheckTableViewModel(/*with_expiration_date_item=*/true);
}
// Verifies that the icon in the CVC input cell is hidden from Voice Over.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
TestCVCCellIconIsHiddenFromVoiceOver) {
auto* CVC_cell = GetCVCInputCell();
ASSERT_TRUE(CVC_cell);
@ -501,7 +537,7 @@ TEST_F(CardUnmaskPromptViewControllerTest,
// Verifies that the textField in the CVC input cell does not accept more than 4
// digits.
TEST_F(CardUnmaskPromptViewControllerTest,
TEST_P(CardUnmaskPromptViewControllerTest,
TestCVCTextFieldRejectsTooLongCVCValues) {
auto* CVC_field = GetCVCInputCell().textField;
ASSERT_TRUE(CVC_field);

@ -287,7 +287,8 @@ void ChromeAutofillClientIOS::ShowUnmaskPrompt(
unmask_controller_.ShowPrompt(
base::BindOnce(&CreateCardUnmaskPromptViewBridge,
base::Unretained(&unmask_controller_),
base::Unretained(base_view_controller_)),
base::Unretained(base_view_controller_),
base::Unretained(personal_data_manager_)),
card, card_unmask_prompt_options, delegate);
}

@ -12,12 +12,14 @@
namespace autofill {
class CardUnmaskPromptController;
class PersonalDataManager;
// Creates the view bridge for the iOS implementation of the Card Unmask
// Prompt.
CardUnmaskPromptView* CreateCardUnmaskPromptViewBridge(
CardUnmaskPromptController* unmask_controller,
UIViewController* base_view_controller);
UIViewController* base_view_controller,
PersonalDataManager* personal_data_manager);
} // namespace autofill
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_CREATE_CARD_UNMASK_PROMPT_VIEW_BRIDGE_H_

@ -4,6 +4,7 @@
#import "ios/chrome/browser/ui/autofill/create_card_unmask_prompt_view_bridge.h"
#import "components/autofill/core/browser/personal_data_manager.h"
#import "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h"
#import "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
@ -11,9 +12,10 @@ namespace autofill {
CardUnmaskPromptView* CreateCardUnmaskPromptViewBridge(
CardUnmaskPromptController* unmask_controller,
UIViewController* base_view_controller) {
return new CardUnmaskPromptViewBridge(unmask_controller,
base_view_controller);
UIViewController* base_view_controller,
PersonalDataManager* personal_data_manager) {
return new CardUnmaskPromptViewBridge(unmask_controller, base_view_controller,
personal_data_manager);
}
} // namespace autofill

@ -222,6 +222,7 @@ source_set("requesters") {
"//components/autofill/core/browser",
"//components/autofill/core/common",
"//components/autofill/ios/browser",
"//ios/chrome/browser/autofill/model",
"//ios/chrome/browser/shared/model/browser_state",
"//ios/chrome/browser/shared/model/web_state_list:web_state_list",
"//ios/chrome/browser/ui/autofill:bridges",

@ -19,6 +19,7 @@ namespace autofill {
class BrowserAutofillManager;
class CreditCard;
struct CardUnmaskPromptOptions;
class PersonalDataManager;
} // namespace autofill
// Receives the full credit card details. Also displays the unmask prompt UI.
@ -49,6 +50,7 @@ class FullCardRequester
private:
__weak UIViewController* base_view_controller_;
autofill::CardUnmaskPromptControllerImpl unmask_controller_;
raw_ptr<autofill::PersonalDataManager> personal_data_manager_;
};
#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_FULL_CARD_REQUESTER_H_

@ -5,13 +5,18 @@
#import "ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.h"
#import "components/autofill/core/browser/browser_autofill_manager.h"
#import "components/autofill/core/browser/personal_data_manager.h"
#import "ios/chrome/browser/autofill/model/personal_data_manager_factory.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/ui/autofill/create_card_unmask_prompt_view_bridge.h"
FullCardRequester::FullCardRequester(UIViewController* base_view_controller,
ChromeBrowserState* browser_state)
: base_view_controller_(base_view_controller),
unmask_controller_(browser_state->GetPrefs()) {}
unmask_controller_(browser_state->GetPrefs()),
personal_data_manager_(
autofill::PersonalDataManagerFactory::GetForBrowserState(
browser_state)) {}
void FullCardRequester::GetFullCard(
const autofill::CreditCard& card,
@ -36,7 +41,8 @@ void FullCardRequester::ShowUnmaskPrompt(
unmask_controller_.ShowPrompt(
base::BindOnce(&autofill::CreateCardUnmaskPromptViewBridge,
base::Unretained(&unmask_controller_),
base::Unretained(base_view_controller_)),
base::Unretained(base_view_controller_),
base::Unretained(personal_data_manager_)),
card, card_unmask_prompt_options, delegate);
}