Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / components / autofill / content / browser / wallet / wallet_items.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/autofill/content/browser/wallet/wallet_items.h"
6
7 #include <limits>
8
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "components/autofill/content/browser/wallet/gaia_account.h"
14 #include "components/autofill/core/browser/autofill_type.h"
15 #include "components/autofill/core/browser/credit_card.h"
16 #include "grit/components_scaled_resources.h"
17 #include "grit/components_strings.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/image/image.h"
21 #include "url/gurl.h"
22
23 namespace autofill {
24 namespace wallet {
25
26 namespace {
27
28 const char kLegalDocumentUrl[] =
29     "https://wallet.google.com/legaldocument?docId=";
30 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
31
32 // TODO(estade): move to base/.
33 template<class T>
34 bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) {
35   if (a.size() != b.size())
36     return false;
37
38   for (size_t i = 0; i < a.size(); ++i) {
39     if (*a[i] != *b[i])
40       return false;
41   }
42
43   return true;
44 }
45
46 WalletItems::MaskedInstrument::Type
47     TypeFromString(const std::string& type_string) {
48   if (type_string == "VISA")
49     return WalletItems::MaskedInstrument::VISA;
50   if (type_string == "MASTER_CARD")
51     return WalletItems::MaskedInstrument::MASTER_CARD;
52   if (type_string == "AMEX")
53     return WalletItems::MaskedInstrument::AMEX;
54   if (type_string == "DISCOVER")
55     return WalletItems::MaskedInstrument::DISCOVER;
56   if (type_string == "SOLO")
57     return WalletItems::MaskedInstrument::SOLO;
58   if (type_string == "MAESTRO")
59     return WalletItems::MaskedInstrument::MAESTRO;
60   if (type_string == "SWITCH")
61     return WalletItems::MaskedInstrument::SWITCH;
62   return WalletItems::MaskedInstrument::UNKNOWN;
63 }
64
65 WalletItems::MaskedInstrument::Status
66     StatusFromString(const std::string& status_string) {
67   if (status_string == "AMEX_NOT_SUPPORTED")
68     return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED;
69   if (status_string == "PENDING")
70     return WalletItems::MaskedInstrument::PENDING;
71   if (status_string == "VALID")
72     return WalletItems::MaskedInstrument::VALID;
73   if (status_string == "DECLINED")
74     return WalletItems::MaskedInstrument::DECLINED;
75   if (status_string == "DISABLED_FOR_THIS_MERCHANT")
76     return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT;
77   if (status_string == "UNSUPPORTED_COUNTRY")
78     return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY;
79   if (status_string == "EXPIRED")
80     return WalletItems::MaskedInstrument::EXPIRED;
81   if (status_string == "BILLING_INCOMPLETE")
82     return WalletItems::MaskedInstrument::BILLING_INCOMPLETE;
83   return WalletItems::MaskedInstrument::INAPPLICABLE;
84 }
85
86 base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) {
87   switch (type) {
88     case WalletItems::MaskedInstrument::AMEX:
89       return CreditCard::TypeForDisplay(kAmericanExpressCard);
90     case WalletItems::MaskedInstrument::DISCOVER:
91       return CreditCard::TypeForDisplay(kDiscoverCard);
92     case WalletItems::MaskedInstrument::MASTER_CARD:
93       return CreditCard::TypeForDisplay(kMasterCard);
94     case WalletItems::MaskedInstrument::VISA:
95       return CreditCard::TypeForDisplay(kVisaCard);
96     default:
97       return CreditCard::TypeForDisplay(kGenericCard);
98   }
99 }
100
101 }  // anonymous namespace
102
103 WalletItems::MaskedInstrument::MaskedInstrument(
104     const base::string16& descriptive_name,
105     const WalletItems::MaskedInstrument::Type& type,
106     const base::string16& last_four_digits,
107     int expiration_month,
108     int expiration_year,
109     scoped_ptr<Address> address,
110     const WalletItems::MaskedInstrument::Status& status,
111     const std::string& object_id)
112     : descriptive_name_(descriptive_name),
113       type_(type),
114       last_four_digits_(last_four_digits),
115       expiration_month_(expiration_month),
116       expiration_year_(expiration_year),
117       address_(address.Pass()),
118       status_(status),
119       object_id_(object_id) {
120   DCHECK(address_);
121 }
122
123 WalletItems::MaskedInstrument::~MaskedInstrument() {}
124
125 scoped_ptr<WalletItems::MaskedInstrument>
126     WalletItems::MaskedInstrument::CreateMaskedInstrument(
127     const base::DictionaryValue& dictionary) {
128   std::string type_string;
129   Type type;
130   if (dictionary.GetString("type", &type_string)) {
131     type = TypeFromString(type_string);
132   } else {
133     DLOG(ERROR) << "Response from Google Wallet missing card type";
134     return scoped_ptr<MaskedInstrument>();
135   }
136
137   base::string16 last_four_digits;
138   if (!dictionary.GetString("last_four_digits", &last_four_digits)) {
139     DLOG(ERROR) << "Response from Google Wallet missing last four digits";
140     return scoped_ptr<MaskedInstrument>();
141   }
142
143   std::string status_string;
144   Status status;
145   if (dictionary.GetString("status", &status_string)) {
146     status = StatusFromString(status_string);
147   } else {
148     DLOG(ERROR) << "Response from Google Wallet missing status";
149     return scoped_ptr<MaskedInstrument>();
150   }
151
152   std::string object_id;
153   if (!dictionary.GetString("object_id", &object_id)) {
154     DLOG(ERROR) << "Response from Google Wallet missing object id";
155     return scoped_ptr<MaskedInstrument>();
156   }
157
158   const base::DictionaryValue* address_dict;
159   if (!dictionary.GetDictionary("billing_address", &address_dict)) {
160     DLOG(ERROR) << "Response from Google wallet missing address";
161     return scoped_ptr<MaskedInstrument>();
162   }
163   scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict);
164
165   if (!address) {
166     DLOG(ERROR) << "Response from Google wallet contained malformed address";
167     return scoped_ptr<MaskedInstrument>();
168   }
169
170   int expiration_month;
171   if (!dictionary.GetInteger("expiration_month", &expiration_month))
172     DVLOG(1) << "Response from Google Wallet missing expiration month";
173
174   int expiration_year;
175   if (!dictionary.GetInteger("expiration_year", &expiration_year))
176     DVLOG(1) << "Response from Google Wallet missing expiration year";
177
178   base::string16 descriptive_name;
179   if (!dictionary.GetString("descriptive_name", &descriptive_name))
180     DVLOG(1) << "Response from Google Wallet missing descriptive name";
181
182   return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
183                                                            type,
184                                                            last_four_digits,
185                                                            expiration_month,
186                                                            expiration_year,
187                                                            address.Pass(),
188                                                            status,
189                                                            object_id));
190 }
191
192 bool WalletItems::MaskedInstrument::operator==(
193     const WalletItems::MaskedInstrument& other) const {
194   if (descriptive_name_ != other.descriptive_name_)
195     return false;
196   if (type_ != other.type_)
197     return false;
198   if (last_four_digits_ != other.last_four_digits_)
199     return false;
200   if (expiration_month_ != other.expiration_month_)
201     return false;
202   if (expiration_year_ != other.expiration_year_)
203     return false;
204   if (address_) {
205     if (other.address_) {
206       if (*address_ != *other.address_)
207         return false;
208     } else {
209       return false;
210     }
211   } else if (other.address_) {
212     return false;
213   }
214   if (status_ != other.status_)
215     return false;
216   if (object_id_ != other.object_id_)
217     return false;
218   return true;
219 }
220
221 bool WalletItems::MaskedInstrument::operator!=(
222     const WalletItems::MaskedInstrument& other) const {
223   return !(*this == other);
224 }
225
226 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
227     const std::string& object_id) const {
228   if (object_id.empty())
229     return NULL;
230
231   for (size_t i = 0; i < instruments_.size(); ++i) {
232     if (instruments_[i]->object_id() == object_id)
233       return instruments_[i];
234   }
235
236   return NULL;
237 }
238
239 bool WalletItems::HasRequiredAction(RequiredAction action) const {
240   DCHECK(ActionAppliesToWalletItems(action));
241   return std::find(required_actions_.begin(),
242                    required_actions_.end(),
243                    action) != required_actions_.end();
244 }
245
246 bool WalletItems::SupportsCard(const base::string16& card_number,
247                                base::string16* message) const {
248   const char* const card_type = CreditCard::GetCreditCardType(card_number);
249
250   if (card_type == kVisaCard ||
251       card_type == kMasterCard ||
252       card_type == kDiscoverCard) {
253     return true;
254   }
255
256   if (card_type == kAmericanExpressCard) {
257     if (amex_permission_ == AMEX_ALLOWED)
258       return true;
259
260     *message = l10n_util::GetStringUTF16(
261         IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT);
262     return false;
263   }
264
265   *message = l10n_util::GetStringUTF16(
266       IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET);
267    return false;
268 }
269
270 std::string WalletItems::ObfuscatedGaiaId() const {
271   if (active_account_index_ >= gaia_accounts_.size())
272     return std::string();
273
274   return gaia_accounts_[active_account_index_]->obfuscated_id();
275 }
276
277 base::string16 WalletItems::MaskedInstrument::DisplayName() const {
278 #if defined(OS_ANDROID)
279   // TODO(aruslan): improve this stub implementation.
280   return descriptive_name();
281 #else
282   return descriptive_name();
283 #endif
284 }
285
286 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
287 #if defined(OS_ANDROID)
288   // TODO(aruslan): improve this stub implementation.
289   return address().DisplayName();
290 #else
291   return base::string16();
292 #endif
293 }
294
295 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
296   // TODO(dbeam): i18n.
297   return DisplayStringFromType(type_) + base::ASCIIToUTF16(" - ") +
298          last_four_digits();
299 }
300
301 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
302   int idr = 0;
303   switch (type_) {
304     case AMEX:
305       idr = IDR_AUTOFILL_CC_AMEX;
306       break;
307
308     case DISCOVER:
309       idr = IDR_AUTOFILL_CC_DISCOVER;
310       break;
311
312     case MASTER_CARD:
313       idr = IDR_AUTOFILL_CC_MASTERCARD;
314       break;
315
316     case VISA:
317       idr = IDR_AUTOFILL_CC_VISA;
318       break;
319
320     case SOLO:
321     case MAESTRO:
322     case SWITCH:
323     case UNKNOWN:
324       idr = IDR_AUTOFILL_CC_GENERIC;
325       break;
326   }
327
328   return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
329 }
330
331 base::string16 WalletItems::MaskedInstrument::GetInfo(
332     const AutofillType& type,
333     const std::string& app_locale) const {
334   if (type.group() != CREDIT_CARD)
335     return address().GetInfo(type, app_locale);
336
337   switch (type.GetStorableType()) {
338     case CREDIT_CARD_NAME:
339       return address().recipient_name();
340
341     case CREDIT_CARD_NUMBER:
342       return DisplayName();
343
344     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
345       return base::IntToString16(expiration_year());
346
347     case CREDIT_CARD_VERIFICATION_CODE:
348       break;
349
350     case CREDIT_CARD_TYPE:
351       return DisplayStringFromType(type_);
352
353     default:
354       NOTREACHED();
355   }
356
357   return base::string16();
358 }
359
360 WalletItems::LegalDocument::~LegalDocument() {}
361
362 scoped_ptr<WalletItems::LegalDocument>
363     WalletItems::LegalDocument::CreateLegalDocument(
364     const base::DictionaryValue& dictionary) {
365   std::string id;
366   if (!dictionary.GetString("legal_document_id", &id)) {
367     DLOG(ERROR) << "Response from Google Wallet missing legal document id";
368     return scoped_ptr<LegalDocument>();
369   }
370
371   base::string16 display_name;
372   if (!dictionary.GetString("display_name", &display_name)) {
373     DLOG(ERROR) << "Response from Google Wallet missing display name";
374     return scoped_ptr<LegalDocument>();
375   }
376
377   return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name));
378 }
379
380 scoped_ptr<WalletItems::LegalDocument>
381     WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
382   return scoped_ptr<LegalDocument>(new LegalDocument(
383       GURL(kPrivacyNoticeUrl),
384       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK)));
385 }
386
387 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
388   return id_ == other.id_ &&
389          url_ == other.url_ &&
390          display_name_ == other.display_name_;
391 }
392
393 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
394   return !(*this == other);
395 }
396
397 WalletItems::LegalDocument::LegalDocument(const std::string& id,
398                                           const base::string16& display_name)
399     : id_(id),
400       url_(kLegalDocumentUrl + id),
401       display_name_(display_name) {}
402
403 WalletItems::LegalDocument::LegalDocument(const GURL& url,
404                                           const base::string16& display_name)
405     : url_(url),
406       display_name_(display_name) {}
407
408 WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions,
409                          const std::string& google_transaction_id,
410                          const std::string& default_instrument_id,
411                          const std::string& default_address_id,
412                          AmexPermission amex_permission)
413     : required_actions_(required_actions),
414       google_transaction_id_(google_transaction_id),
415       default_instrument_id_(default_instrument_id),
416       default_address_id_(default_address_id),
417       active_account_index_(std::numeric_limits<size_t>::max()),
418       amex_permission_(amex_permission) {}
419
420 WalletItems::~WalletItems() {}
421
422 scoped_ptr<WalletItems>
423     WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) {
424   std::vector<RequiredAction> required_action;
425   const base::ListValue* required_action_list;
426   if (dictionary.GetList("required_action", &required_action_list)) {
427     for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
428       std::string action_string;
429       if (required_action_list->GetString(i, &action_string)) {
430         RequiredAction action = ParseRequiredActionFromString(action_string);
431         if (!ActionAppliesToWalletItems(action)) {
432           DLOG(ERROR) << "Response from Google wallet with bad required action:"
433                          " \"" << action_string << "\"";
434           return scoped_ptr<WalletItems>();
435         }
436         required_action.push_back(action);
437       }
438     }
439   } else {
440     DVLOG(1) << "Response from Google wallet missing required actions";
441   }
442
443   std::string google_transaction_id;
444   if (!dictionary.GetString("google_transaction_id", &google_transaction_id) &&
445       required_action.empty()) {
446     DLOG(ERROR) << "Response from Google wallet missing google transaction id";
447     return scoped_ptr<WalletItems>();
448   }
449
450   std::string default_instrument_id;
451   if (!dictionary.GetString("default_instrument_id", &default_instrument_id))
452     DVLOG(1) << "Response from Google wallet missing default instrument id";
453
454   std::string default_address_id;
455   if (!dictionary.GetString("default_address_id", &default_address_id))
456     DVLOG(1) << "Response from Google wallet missing default_address_id";
457
458   // obfuscated_gaia_id is deprecated.
459
460   bool amex_disallowed = true;
461   if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed))
462     DVLOG(1) << "Response from Google wallet missing the amex_disallowed field";
463   AmexPermission amex_permission =
464       amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED;
465
466   scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
467                                                        google_transaction_id,
468                                                        default_instrument_id,
469                                                        default_address_id,
470                                                        amex_permission));
471   std::vector<std::string> gaia_accounts;
472   const base::ListValue* gaia_profiles;
473   if (dictionary.GetList("gaia_profile", &gaia_profiles)) {
474     for (size_t i = 0; i < gaia_profiles->GetSize(); ++i) {
475       const base::DictionaryValue* account_dict;
476       std::string email;
477       if (!gaia_profiles->GetDictionary(i, &account_dict))
478         continue;
479
480       scoped_ptr<GaiaAccount> gaia_account(
481           GaiaAccount::Create(*account_dict));
482       if (gaia_account)
483         wallet_items->AddAccount(gaia_account.Pass());
484     }
485   } else {
486     DVLOG(1) << "Response from Google wallet missing GAIA accounts";
487   }
488
489   const base::ListValue* legal_docs;
490   if (dictionary.GetList("required_legal_document", &legal_docs)) {
491     for (size_t i = 0; i < legal_docs->GetSize(); ++i) {
492       const base::DictionaryValue* legal_doc_dict;
493       if (legal_docs->GetDictionary(i, &legal_doc_dict)) {
494         scoped_ptr<LegalDocument> legal_doc(
495             LegalDocument::CreateLegalDocument(*legal_doc_dict));
496         if (legal_doc)
497           wallet_items->AddLegalDocument(legal_doc.Pass());
498         else
499           return scoped_ptr<WalletItems>();
500       }
501     }
502
503     if (!legal_docs->empty()) {
504       // Always append the privacy policy link as well.
505       wallet_items->AddLegalDocument(
506           LegalDocument::CreatePrivacyPolicyDocument());
507     }
508   } else {
509     DVLOG(1) << "Response from Google wallet missing legal docs";
510   }
511
512   const base::ListValue* instruments;
513   if (dictionary.GetList("instrument", &instruments)) {
514     for (size_t i = 0; i < instruments->GetSize(); ++i) {
515       const base::DictionaryValue* instrument_dict;
516       if (instruments->GetDictionary(i, &instrument_dict)) {
517         scoped_ptr<MaskedInstrument> instrument(
518             MaskedInstrument::CreateMaskedInstrument(*instrument_dict));
519         if (instrument)
520           wallet_items->AddInstrument(instrument.Pass());
521       }
522     }
523   } else {
524     DVLOG(1) << "Response from Google wallet missing instruments";
525   }
526
527   const base::ListValue* addresses;
528   if (dictionary.GetList("address", &addresses)) {
529     for (size_t i = 0; i < addresses->GetSize(); ++i) {
530       const base::DictionaryValue* address_dict;
531       if (addresses->GetDictionary(i, &address_dict)) {
532         scoped_ptr<Address> address(
533             Address::CreateAddressWithID(*address_dict));
534         if (address)
535           wallet_items->AddAddress(address.Pass());
536       }
537     }
538   } else {
539     DVLOG(1) << "Response from Google wallet missing addresses";
540   }
541
542   const base::ListValue* allowed_shipping_countries;
543   if (dictionary.GetList("allowed_shipping_spec_by_country",
544                          &allowed_shipping_countries)) {
545     for (size_t i = 0; i < allowed_shipping_countries->GetSize(); ++i) {
546       const base::DictionaryValue* country_spec;
547       std::string country_code;
548       if (allowed_shipping_countries->GetDictionary(i, &country_spec) &&
549           country_spec->GetString("country_code", &country_code)) {
550         wallet_items->AddAllowedShippingCountry(country_code);
551       }
552     }
553   } else {
554     DVLOG(1) << "Response from Google wallet missing allowed shipping"
555                 " countries";
556   }
557
558   return wallet_items.Pass();
559 }
560
561 void WalletItems::AddAccount(scoped_ptr<GaiaAccount> account) {
562   if (account->index() != gaia_accounts_.size()) {
563     DVLOG(1) << "Tried to add account out of order";
564     return;
565   }
566
567   if (account->is_active())
568     active_account_index_ = account->index();
569
570   gaia_accounts_.push_back(account.release());
571 }
572
573 bool WalletItems::operator==(const WalletItems& other) const {
574   return google_transaction_id_ == other.google_transaction_id_ &&
575          default_instrument_id_ == other.default_instrument_id_ &&
576          default_address_id_ == other.default_address_id_ &&
577          required_actions_ == other.required_actions_ &&
578          // This check is technically redundant, but is useful for tests.
579          ObfuscatedGaiaId() == other.ObfuscatedGaiaId() &&
580          active_account_index() == other.active_account_index() &&
581          VectorsAreEqual<GaiaAccount>(gaia_accounts(),
582                                       other.gaia_accounts()) &&
583          VectorsAreEqual<MaskedInstrument>(instruments(),
584                                             other.instruments()) &&
585          VectorsAreEqual<Address>(addresses(), other.addresses()) &&
586          VectorsAreEqual<LegalDocument>(legal_documents(),
587                                          other.legal_documents()) &&
588          allowed_shipping_countries() == other.allowed_shipping_countries();
589 }
590
591 bool WalletItems::operator!=(const WalletItems& other) const {
592   return !(*this == other);
593 }
594
595 }  // namespace wallet
596 }  // namespace autofill