OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/prefs/pref_service.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "chrome/browser/profiles/profile.h" | |
13 #include "chrome/browser/ui/autofill/card_unmask_prompt_view.h" | |
14 #include "chrome/grit/generated_resources.h" | |
15 #include "components/autofill/core/browser/autofill_experiments.h" | |
16 #include "components/autofill/core/browser/autofill_metrics.h" | |
17 #include "components/autofill/core/common/autofill_pref_names.h" | |
18 #include "grit/theme_resources.h" | |
19 #include "ui/base/l10n/l10n_util.h" | |
20 | |
21 namespace autofill { | |
22 | |
23 CardUnmaskPromptControllerImpl::CardUnmaskPromptControllerImpl( | |
24 content::WebContents* web_contents, | |
25 const RiskDataCallback& risk_data_callback, | |
26 PrefService* pref_service, | |
27 bool is_off_the_record) | |
28 : web_contents_(web_contents), | |
29 risk_data_callback_(risk_data_callback), | |
30 pref_service_(pref_service), | |
31 new_card_link_clicked_(false), | |
32 is_off_the_record_(is_off_the_record), | |
33 card_unmask_view_(nullptr), | |
34 unmasking_result_(AutofillClient::NONE), | |
35 unmasking_initial_should_store_pan_(false), | |
36 unmasking_number_of_attempts_(0), | |
37 weak_pointer_factory_(this) { | |
38 } | |
39 | |
40 CardUnmaskPromptControllerImpl::~CardUnmaskPromptControllerImpl() { | |
41 if (card_unmask_view_) | |
42 card_unmask_view_->ControllerGone(); | |
43 } | |
44 | |
45 void CardUnmaskPromptControllerImpl::ShowPrompt( | |
46 const CreditCard& card, | |
47 base::WeakPtr<CardUnmaskDelegate> delegate) { | |
48 if (card_unmask_view_) | |
49 card_unmask_view_->ControllerGone(); | |
50 | |
51 shown_timestamp_ = base::Time::Now(); | |
52 pending_response_ = CardUnmaskDelegate::UnmaskResponse(); | |
53 LoadRiskFingerprint(); | |
54 card_ = card; | |
55 delegate_ = delegate; | |
56 card_unmask_view_ = CreateAndShowView(); | |
57 unmasking_result_ = AutofillClient::NONE; | |
58 unmasking_number_of_attempts_ = 0; | |
59 unmasking_initial_should_store_pan_ = GetStoreLocallyStartState(); | |
60 AutofillMetrics::LogUnmaskPromptEvent(AutofillMetrics::UNMASK_PROMPT_SHOWN); | |
61 } | |
62 | |
63 bool CardUnmaskPromptControllerImpl::AllowsRetry( | |
64 AutofillClient::GetRealPanResult result) { | |
65 if (result == AutofillClient::NETWORK_ERROR || | |
66 result == AutofillClient::PERMANENT_FAILURE) { | |
67 return false; | |
68 } | |
69 return true; | |
70 } | |
71 | |
72 void CardUnmaskPromptControllerImpl::OnVerificationResult( | |
73 AutofillClient::GetRealPanResult result) { | |
74 if (!card_unmask_view_) | |
75 return; | |
76 | |
77 base::string16 error_message; | |
78 switch (result) { | |
79 case AutofillClient::SUCCESS: | |
80 break; | |
81 | |
82 case AutofillClient::TRY_AGAIN_FAILURE: { | |
83 error_message = l10n_util::GetStringUTF16( | |
84 IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN); | |
85 break; | |
86 } | |
87 | |
88 case AutofillClient::PERMANENT_FAILURE: { | |
89 error_message = l10n_util::GetStringUTF16( | |
90 IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT); | |
91 break; | |
92 } | |
93 | |
94 case AutofillClient::NETWORK_ERROR: { | |
95 error_message = l10n_util::GetStringUTF16( | |
96 IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_NETWORK); | |
97 break; | |
98 } | |
99 | |
100 case AutofillClient::NONE: | |
101 NOTREACHED(); | |
102 return; | |
103 } | |
104 | |
105 unmasking_result_ = result; | |
106 AutofillMetrics::LogRealPanResult(result); | |
107 AutofillMetrics::LogUnmaskingDuration(base::Time::Now() - verify_timestamp_, | |
108 result); | |
109 card_unmask_view_->GotVerificationResult(error_message, | |
110 AllowsRetry(result)); | |
111 } | |
112 | |
113 void CardUnmaskPromptControllerImpl::OnUnmaskDialogClosed() { | |
114 card_unmask_view_ = nullptr; | |
115 LogOnCloseEvents(); | |
116 if (delegate_.get()) | |
117 delegate_->OnUnmaskPromptClosed(); | |
118 } | |
119 | |
120 void CardUnmaskPromptControllerImpl::LogOnCloseEvents() { | |
121 AutofillMetrics::UnmaskPromptEvent close_reason_event = GetCloseReasonEvent(); | |
122 AutofillMetrics::LogUnmaskPromptEvent(close_reason_event); | |
123 AutofillMetrics::LogUnmaskPromptEventDuration( | |
124 base::Time::Now() - shown_timestamp_, close_reason_event); | |
125 | |
126 if (close_reason_event == AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS) | |
127 return; | |
128 | |
129 if (close_reason_event == | |
130 AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING) { | |
131 AutofillMetrics::LogTimeBeforeAbandonUnmasking(base::Time::Now() - | |
132 verify_timestamp_); | |
133 } | |
134 | |
135 bool final_should_store_pan = pending_response_.should_store_pan; | |
136 if (unmasking_result_ == AutofillClient::SUCCESS && final_should_store_pan) { | |
137 AutofillMetrics::LogUnmaskPromptEvent( | |
138 AutofillMetrics::UNMASK_PROMPT_SAVED_CARD_LOCALLY); | |
139 } | |
140 | |
141 if (CanStoreLocally()) { | |
142 // Tracking changes in local save preference. | |
143 AutofillMetrics::UnmaskPromptEvent event; | |
144 if (unmasking_initial_should_store_pan_ && final_should_store_pan) { | |
145 event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_OUT; | |
146 } else if (!unmasking_initial_should_store_pan_ && | |
147 !final_should_store_pan) { | |
148 event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_IN; | |
149 } else if (unmasking_initial_should_store_pan_ && !final_should_store_pan) { | |
150 event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_OUT; | |
151 } else { | |
152 event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_IN; | |
153 } | |
154 AutofillMetrics::LogUnmaskPromptEvent(event); | |
155 } | |
156 } | |
157 | |
158 AutofillMetrics::UnmaskPromptEvent | |
159 CardUnmaskPromptControllerImpl::GetCloseReasonEvent() { | |
160 if (unmasking_number_of_attempts_ == 0) | |
161 return AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS; | |
162 | |
163 // If NONE and we have a pending request, we have a pending GetRealPan | |
164 // request. | |
165 if (unmasking_result_ == AutofillClient::NONE) | |
166 return AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING; | |
167 | |
168 if (unmasking_result_ == AutofillClient::SUCCESS) { | |
169 return unmasking_number_of_attempts_ == 1 | |
170 ? AutofillMetrics::UNMASK_PROMPT_UNMASKED_CARD_FIRST_ATTEMPT | |
171 : AutofillMetrics:: | |
172 UNMASK_PROMPT_UNMASKED_CARD_AFTER_FAILED_ATTEMPTS; | |
173 } else { | |
174 return AllowsRetry(unmasking_result_) | |
175 ? AutofillMetrics:: | |
176 UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_RETRIABLE_FAILURE | |
177 : AutofillMetrics:: | |
178 UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_NON_RETRIABLE_FAILURE
; | |
179 } | |
180 } | |
181 | |
182 void CardUnmaskPromptControllerImpl::OnUnmaskResponse( | |
183 const base::string16& cvc, | |
184 const base::string16& exp_month, | |
185 const base::string16& exp_year, | |
186 bool should_store_pan) { | |
187 verify_timestamp_ = base::Time::Now(); | |
188 unmasking_number_of_attempts_++; | |
189 unmasking_result_ = AutofillClient::NONE; | |
190 card_unmask_view_->DisableAndWaitForVerification(); | |
191 | |
192 DCHECK(InputCvcIsValid(cvc)); | |
193 base::TrimWhitespace(cvc, base::TRIM_ALL, &pending_response_.cvc); | |
194 if (ShouldRequestExpirationDate()) { | |
195 DCHECK(InputExpirationIsValid(exp_month, exp_year)); | |
196 pending_response_.exp_month = exp_month; | |
197 pending_response_.exp_year = exp_year; | |
198 } | |
199 if (CanStoreLocally()) { | |
200 pending_response_.should_store_pan = should_store_pan; | |
201 // Remember the last choice the user made (on this device). | |
202 pref_service_->SetBoolean( | |
203 prefs::kAutofillWalletImportStorageCheckboxState, should_store_pan); | |
204 } else { | |
205 DCHECK(!should_store_pan); | |
206 pending_response_.should_store_pan = false; | |
207 } | |
208 | |
209 if (!pending_response_.risk_data.empty()) | |
210 delegate_->OnUnmaskResponse(pending_response_); | |
211 } | |
212 | |
213 void CardUnmaskPromptControllerImpl::NewCardLinkClicked() { | |
214 new_card_link_clicked_ = true; | |
215 } | |
216 | |
217 content::WebContents* CardUnmaskPromptControllerImpl::GetWebContents() { | |
218 return web_contents_; | |
219 } | |
220 | |
221 base::string16 CardUnmaskPromptControllerImpl::GetWindowTitle() const { | |
222 int ids = ShouldRequestExpirationDate() | |
223 ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE | |
224 : IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE; | |
225 return l10n_util::GetStringFUTF16(ids, card_.TypeAndLastFourDigits()); | |
226 } | |
227 | |
228 base::string16 CardUnmaskPromptControllerImpl::GetInstructionsMessage() const { | |
229 if (ShouldRequestExpirationDate()) { | |
230 return l10n_util::GetStringUTF16( | |
231 card_.type() == kAmericanExpressCard | |
232 ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED_AMEX | |
233 : IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED); | |
234 } | |
235 | |
236 return l10n_util::GetStringUTF16( | |
237 card_.type() == kAmericanExpressCard | |
238 ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_AMEX | |
239 : IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS); | |
240 } | |
241 | |
242 int CardUnmaskPromptControllerImpl::GetCvcImageRid() const { | |
243 return card_.type() == kAmericanExpressCard ? IDR_CREDIT_CARD_CVC_HINT_AMEX | |
244 : IDR_CREDIT_CARD_CVC_HINT; | |
245 } | |
246 | |
247 bool CardUnmaskPromptControllerImpl::ShouldRequestExpirationDate() const { | |
248 return card_.GetServerStatus() == CreditCard::EXPIRED || | |
249 new_card_link_clicked_; | |
250 } | |
251 | |
252 bool CardUnmaskPromptControllerImpl::CanStoreLocally() const { | |
253 // Never offer to save for incognito. | |
254 if (is_off_the_record_) | |
255 return false; | |
256 return autofill::OfferStoreUnmaskedCards(); | |
257 } | |
258 | |
259 bool CardUnmaskPromptControllerImpl::GetStoreLocallyStartState() const { | |
260 return pref_service_->GetBoolean( | |
261 prefs::kAutofillWalletImportStorageCheckboxState); | |
262 } | |
263 | |
264 bool CardUnmaskPromptControllerImpl::InputCvcIsValid( | |
265 const base::string16& input_text) const { | |
266 base::string16 trimmed_text; | |
267 base::TrimWhitespace(input_text, base::TRIM_ALL, &trimmed_text); | |
268 size_t input_size = card_.type() == kAmericanExpressCard ? 4 : 3; | |
269 return trimmed_text.size() == input_size && | |
270 base::ContainsOnlyChars(trimmed_text, | |
271 base::ASCIIToUTF16("0123456789")); | |
272 } | |
273 | |
274 bool CardUnmaskPromptControllerImpl::InputExpirationIsValid( | |
275 const base::string16& month, | |
276 const base::string16& year) const { | |
277 if ((month.size() != 2U && month.size() != 1U) || | |
278 (year.size() != 4U && year.size() != 2U)) { | |
279 return false; | |
280 } | |
281 | |
282 int month_value = 0, year_value = 0; | |
283 if (!base::StringToInt(month, &month_value) || | |
284 !base::StringToInt(year, &year_value)) { | |
285 return false; | |
286 } | |
287 | |
288 if (month_value < 1 || month_value > 12) | |
289 return false; | |
290 | |
291 base::Time::Exploded now; | |
292 base::Time::Now().LocalExplode(&now); | |
293 | |
294 // Convert 2 digit year to 4 digit year. | |
295 if (year_value < 100) | |
296 year_value += (now.year / 100) * 100; | |
297 | |
298 if (year_value < now.year) | |
299 return false; | |
300 | |
301 if (year_value > now.year) | |
302 return true; | |
303 | |
304 return month_value >= now.month; | |
305 } | |
306 | |
307 base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration() | |
308 const { | |
309 return base::TimeDelta::FromMilliseconds(500); | |
310 } | |
311 | |
312 CardUnmaskPromptView* CardUnmaskPromptControllerImpl::CreateAndShowView() { | |
313 return CardUnmaskPromptView::CreateAndShow(this); | |
314 } | |
315 | |
316 void CardUnmaskPromptControllerImpl::LoadRiskFingerprint() { | |
317 risk_data_callback_.Run( | |
318 base::Bind(&CardUnmaskPromptControllerImpl::OnDidLoadRiskFingerprint, | |
319 weak_pointer_factory_.GetWeakPtr())); | |
320 } | |
321 | |
322 void CardUnmaskPromptControllerImpl::OnDidLoadRiskFingerprint( | |
323 const std::string& risk_data) { | |
324 pending_response_.risk_data = risk_data; | |
325 if (!pending_response_.cvc.empty()) | |
326 delegate_->OnUnmaskResponse(pending_response_); | |
327 } | |
328 | |
329 } // namespace autofill | |
OLD | NEW |