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