Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / autofill / core / browser / personal_data_manager_mac.mm
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/core/browser/personal_data_manager.h"
6
7 #include <math.h>
8
9 #import <AddressBook/AddressBook.h>
10
11 #include "base/format_macros.h"
12 #include "base/guid.h"
13 #include "base/logging.h"
14 #import "base/mac/scoped_nsexception_enabler.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/metrics/histogram.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "components/autofill/core/browser/autofill_country.h"
22 #include "components/autofill/core/browser/autofill_profile.h"
23 #include "components/autofill/core/browser/autofill_type.h"
24 #include "components/autofill/core/browser/form_structure.h"
25 #include "components/autofill/core/browser/phone_number.h"
26 #include "components/autofill/core/common/autofill_pref_names.h"
27 #include "components/autofill/core/common/form_data.h"
28 #include "grit/components_strings.h"
29 #include "ui/base/l10n/l10n_util_mac.h"
30
31 namespace autofill {
32 namespace {
33
34 // The maximum number of instances when the access Address Book prompt should
35 // be shown.
36 int kMaxTimesToShowMacAddressBook = 5;
37
38 // There is an uncommon sequence of events that causes the Address Book
39 // permissions dialog to appear more than once for a given install of Chrome.
40 //  1. Chrome has previously presented the Address Book permissions dialog.
41 //  2. Chrome is launched.
42 //  3. Chrome performs an auto-update, and changes its binary.
43 //  4. Chrome attempts to access the Address Book for the first time since (2).
44 // This sequence of events is rare because Chrome attempts to acess the Address
45 // Book when the user focuses most form fields, so (4) generally occurs before
46 // (3). For more details, see http://crbug.com/381763.
47 //
48 // When this sequence of events does occur, Chrome should not attempt to access
49 // the Address Book unless the user explicitly asks Chrome to do so. The
50 // jarring nature of the permissions dialog is worse than the potential benefit
51 // of pulling information from the Address Book.
52 //
53 // Set to true after the Address Book is accessed for the first time.
54 static bool g_accessed_address_book = false;
55
56 // Set to true after the Chrome binary has been changed.
57 static bool g_binary_changed = false;
58
59 const char kAddressBookOrigin[] = "OS X Address Book";
60
61 // Whether Chrome has attempted to access the Mac Address Book.
62 bool HasQueriedMacAddressBook(PrefService* pref_service) {
63   return pref_service->GetBoolean(prefs::kAutofillMacAddressBookQueried);
64 }
65
66 // Whether the user wants Chrome to use the AddressBook to populate Autofill
67 // entries.
68 bool ShouldUseAddressBook(PrefService* pref_service) {
69   return pref_service->GetBoolean(prefs::kAutofillUseMacAddressBook);
70 }
71
72 // Records a UMA metric indicating whether an attempt to access the Address
73 // Book was skipped because doing so would cause the Address Book permissions
74 // prompt to incorrectly appear.
75 void RecordAccessSkipped(bool skipped) {
76   UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBook.AccessSkipped", skipped);
77 }
78
79 ABAddressBook* GetAddressBook(PrefService* pref_service) {
80   bool first_access = !HasQueriedMacAddressBook(pref_service);
81
82   // +[ABAddressBook sharedAddressBook] throws an exception internally in
83   // circumstances that aren't clear. The exceptions are only observed in crash
84   // reports, so it is unknown whether they would be caught by AppKit and nil
85   // returned, or if they would take down the app. In either case, avoid
86   // crashing. http://crbug.com/129022
87   ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions(
88       ^{ return [ABAddressBook sharedAddressBook]; });
89   UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailable", addressBook != nil);
90
91   if (first_access) {
92     UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailableOnFirstAttempt",
93                           addressBook != nil);
94   }
95
96   g_accessed_address_book = true;
97   pref_service->SetBoolean(prefs::kAutofillMacAddressBookQueried, true);
98   return addressBook;
99 }
100
101 // This implementation makes use of the Address Book API.  Profiles are
102 // generated that correspond to addresses in the "me" card that reside in the
103 // user's Address Book.  The caller passes a vector of profiles into the
104 // the constructer and then initiate the fetch from the Mac Address Book "me"
105 // card using the main |GetAddressBookMeCard()| method.  This clears any
106 // existing addresses and populates new addresses derived from the data found
107 // in the "me" card.
108 class AuxiliaryProfilesImpl {
109  public:
110   // Constructor takes a reference to the |profiles| that will be filled in
111   // by the subsequent call to |GetAddressBookMeCard()|.  |profiles| may not
112   // be NULL.
113   explicit AuxiliaryProfilesImpl(ScopedVector<AutofillProfile>* profiles)
114       : profiles_(*profiles) {
115   }
116   virtual ~AuxiliaryProfilesImpl() {}
117
118   // Import the "me" card from the Mac Address Book and fill in |profiles_|.
119   void GetAddressBookMeCard(const std::string& app_locale,
120                             PrefService* pref_service,
121                             bool record_metrics);
122
123  private:
124   void GetAddressBookNames(ABPerson* me,
125                            NSString* addressLabelRaw,
126                            AutofillProfile* profile);
127   void GetAddressBookAddress(const std::string& app_locale,
128                              NSDictionary* address,
129                              AutofillProfile* profile);
130   void GetAddressBookEmail(ABPerson* me,
131                            NSString* addressLabelRaw,
132                            AutofillProfile* profile);
133   void GetAddressBookPhoneNumbers(ABPerson* me,
134                                   NSString* addressLabelRaw,
135                                   AutofillProfile* profile);
136
137  private:
138   // A reference to the profiles this class populates.
139   ScopedVector<AutofillProfile>& profiles_;
140
141   DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesImpl);
142 };
143
144 // This method uses the |ABAddressBook| system service to fetch the "me" card
145 // from the active user's address book.  It looks for the user address
146 // information and translates it to the internal list of |AutofillProfile| data
147 // structures.
148 void AuxiliaryProfilesImpl::GetAddressBookMeCard(const std::string& app_locale,
149                                                  PrefService* pref_service,
150                                                  bool record_metrics) {
151   profiles_.clear();
152
153   // The user does not want Chrome to use the AddressBook to populate Autofill
154   // entries.
155   if (!ShouldUseAddressBook(pref_service))
156     return;
157
158   // See the comment at the definition of g_accessed_address_book for an
159   // explanation of this logic.
160   if (g_binary_changed && !g_accessed_address_book) {
161     if (record_metrics)
162       RecordAccessSkipped(true);
163     return;
164   }
165
166   if (record_metrics)
167     RecordAccessSkipped(false);
168
169   ABAddressBook* addressBook = GetAddressBook(pref_service);
170
171   ABPerson* me = [addressBook me];
172   if (!me)
173     return;
174
175   ABMultiValue* addresses = [me valueForProperty:kABAddressProperty];
176
177   // The number of characters at the end of the GUID to reserve for
178   // distinguishing addresses within the "me" card.  Cap the number of addresses
179   // we will fetch to the number that can be distinguished by this fragment of
180   // the GUID.
181   const size_t kNumAddressGUIDChars = 2;
182   const size_t kNumHexDigits = 16;
183   const size_t kMaxAddressCount = pow(kNumHexDigits, kNumAddressGUIDChars);
184   NSUInteger count = MIN([addresses count], kMaxAddressCount);
185   for (NSUInteger i = 0; i < count; i++) {
186     NSDictionary* address = [addresses valueAtIndex:i];
187     NSString* addressLabelRaw = [addresses labelAtIndex:i];
188
189     // Create a new profile where the guid is set to the guid portion of the
190     // |kABUIDProperty| taken from from the "me" address.  The format of
191     // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the
192     // raw guid here and using it directly, with one modification: we update the
193     // last |kNumAddressGUIDChars| characters in the GUID to reflect the address
194     // variant.  Note that we capped the number of addresses above, so this is
195     // safe.
196     const size_t kGUIDLength = 36U;
197     const size_t kTrimmedGUIDLength = kGUIDLength - kNumAddressGUIDChars;
198     std::string guid = base::SysNSStringToUTF8(
199         [me valueForProperty:kABUIDProperty]).substr(0, kTrimmedGUIDLength);
200
201     // The format string to print |kNumAddressGUIDChars| hexadecimal characters,
202     // left-padded with 0's.
203     const std::string kAddressGUIDFormat =
204         base::StringPrintf("%%0%" PRIuS "X", kNumAddressGUIDChars);
205     guid += base::StringPrintf(kAddressGUIDFormat.c_str(), i);
206     DCHECK_EQ(kGUIDLength, guid.size());
207
208     scoped_ptr<AutofillProfile> profile(
209         new AutofillProfile(guid, kAddressBookOrigin));
210     DCHECK(base::IsValidGUID(profile->guid()));
211
212     // Fill in name and company information.
213     GetAddressBookNames(me, addressLabelRaw, profile.get());
214
215     // Fill in address information.
216     GetAddressBookAddress(app_locale, address, profile.get());
217
218     // Fill in email information.
219     GetAddressBookEmail(me, addressLabelRaw, profile.get());
220
221     // Fill in phone number information.
222     GetAddressBookPhoneNumbers(me, addressLabelRaw, profile.get());
223
224     profiles_.push_back(profile.release());
225   }
226 }
227
228 // Name and company information is stored once in the Address Book against
229 // multiple addresses.  We replicate that information for each profile.
230 // We only propagate the company name to work profiles.
231 void AuxiliaryProfilesImpl::GetAddressBookNames(
232     ABPerson* me,
233     NSString* addressLabelRaw,
234     AutofillProfile* profile) {
235   NSString* firstName = [me valueForProperty:kABFirstNameProperty];
236   NSString* middleName = [me valueForProperty:kABMiddleNameProperty];
237   NSString* lastName = [me valueForProperty:kABLastNameProperty];
238   NSString* companyName = [me valueForProperty:kABOrganizationProperty];
239
240   profile->SetRawInfo(NAME_FIRST, base::SysNSStringToUTF16(firstName));
241   profile->SetRawInfo(NAME_MIDDLE, base::SysNSStringToUTF16(middleName));
242   profile->SetRawInfo(NAME_LAST, base::SysNSStringToUTF16(lastName));
243   if ([addressLabelRaw isEqualToString:kABAddressWorkLabel])
244     profile->SetRawInfo(COMPANY_NAME, base::SysNSStringToUTF16(companyName));
245 }
246
247 // Addresss information from the Address Book may span multiple lines.
248 // If it does then we represent the address with two lines in the profile.  The
249 // second line we join with commas.
250 // For example:  "c/o John Doe\n1122 Other Avenue\nApt #7" translates to
251 // line 1: "c/o John Doe", line 2: "1122 Other Avenue, Apt #7".
252 void AuxiliaryProfilesImpl::GetAddressBookAddress(const std::string& app_locale,
253                                                   NSDictionary* address,
254                                                   AutofillProfile* profile) {
255   if (NSString* addressField = [address objectForKey:kABAddressStreetKey]) {
256     // If there are newlines in the address, split into two lines.
257     if ([addressField rangeOfCharacterFromSet:
258             [NSCharacterSet newlineCharacterSet]].location != NSNotFound) {
259       NSArray* chunks = [addressField componentsSeparatedByCharactersInSet:
260           [NSCharacterSet newlineCharacterSet]];
261       DCHECK([chunks count] > 1);
262
263       NSString* separator =
264           l10n_util::GetNSString(IDS_AUTOFILL_ADDRESS_LINE_SEPARATOR);
265
266       NSString* addressField1 = [chunks objectAtIndex:0];
267       NSString* addressField2 =
268           [[chunks subarrayWithRange:NSMakeRange(1, [chunks count] - 1)]
269               componentsJoinedByString:separator];
270       profile->SetRawInfo(ADDRESS_HOME_LINE1,
271                           base::SysNSStringToUTF16(addressField1));
272       profile->SetRawInfo(ADDRESS_HOME_LINE2,
273                           base::SysNSStringToUTF16(addressField2));
274     } else {
275       profile->SetRawInfo(ADDRESS_HOME_LINE1,
276                           base::SysNSStringToUTF16(addressField));
277     }
278   }
279
280   if (NSString* city = [address objectForKey:kABAddressCityKey])
281     profile->SetRawInfo(ADDRESS_HOME_CITY, base::SysNSStringToUTF16(city));
282
283   if (NSString* state = [address objectForKey:kABAddressStateKey])
284     profile->SetRawInfo(ADDRESS_HOME_STATE, base::SysNSStringToUTF16(state));
285
286   if (NSString* zip = [address objectForKey:kABAddressZIPKey])
287     profile->SetRawInfo(ADDRESS_HOME_ZIP, base::SysNSStringToUTF16(zip));
288
289   if (NSString* country = [address objectForKey:kABAddressCountryKey]) {
290     profile->SetInfo(AutofillType(ADDRESS_HOME_COUNTRY),
291                      base::SysNSStringToUTF16(country),
292                      app_locale);
293   }
294 }
295
296 // Fills in email address matching current address label.  Note that there may
297 // be multiple matching email addresses for a given label.  We take the
298 // first we find (topmost) as preferred.
299 void AuxiliaryProfilesImpl::GetAddressBookEmail(
300     ABPerson* me,
301     NSString* addressLabelRaw,
302     AutofillProfile* profile) {
303   ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty];
304   NSString* emailAddress = nil;
305   for (NSUInteger j = 0, emailCount = [emailAddresses count];
306        j < emailCount; j++) {
307     NSString* emailAddressLabelRaw = [emailAddresses labelAtIndex:j];
308     if ([emailAddressLabelRaw isEqualToString:addressLabelRaw]) {
309       emailAddress = [emailAddresses valueAtIndex:j];
310       break;
311     }
312   }
313   profile->SetRawInfo(EMAIL_ADDRESS, base::SysNSStringToUTF16(emailAddress));
314 }
315
316 // Fills in telephone numbers.  Each of these are special cases.
317 // We match two cases: home/tel, work/tel.
318 // Note, we traverse in reverse order so that top values in address book
319 // take priority.
320 void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers(
321     ABPerson* me,
322     NSString* addressLabelRaw,
323     AutofillProfile* profile) {
324   ABMultiValue* phoneNumbers = [me valueForProperty:kABPhoneProperty];
325   for (NSUInteger k = 0, phoneCount = [phoneNumbers count];
326        k < phoneCount; k++) {
327     NSUInteger reverseK = phoneCount - k - 1;
328     NSString* phoneLabelRaw = [phoneNumbers labelAtIndex:reverseK];
329     if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] &&
330         [phoneLabelRaw isEqualToString:kABPhoneHomeLabel]) {
331       base::string16 homePhone = base::SysNSStringToUTF16(
332           [phoneNumbers valueAtIndex:reverseK]);
333       profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, homePhone);
334     } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
335                [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) {
336       base::string16 workPhone = base::SysNSStringToUTF16(
337           [phoneNumbers valueAtIndex:reverseK]);
338       profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, workPhone);
339     } else if ([phoneLabelRaw isEqualToString:kABPhoneMobileLabel] ||
340                [phoneLabelRaw isEqualToString:kABPhoneMainLabel]) {
341       base::string16 phone = base::SysNSStringToUTF16(
342           [phoneNumbers valueAtIndex:reverseK]);
343       profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, phone);
344     }
345   }
346 }
347
348 }  // namespace
349
350 // Populate |auxiliary_profiles_| with the Address Book data.
351 void PersonalDataManager::LoadAuxiliaryProfiles(bool record_metrics) const {
352   AuxiliaryProfilesImpl impl(&auxiliary_profiles_);
353   impl.GetAddressBookMeCard(app_locale_, pref_service_, record_metrics);
354 }
355
356 bool PersonalDataManager::AccessAddressBook() {
357   // The user is attempting to give Chrome access to the user's Address Book.
358   // This implicitly acknowledges that the user wants to use auxiliary
359   // profiles.
360   pref_service_->SetBoolean(prefs::kAutofillUseMacAddressBook, true);
361
362   // Request permissions.
363   GetAddressBook(pref_service_);
364   return true;
365 }
366
367 bool PersonalDataManager::ShouldShowAccessAddressBookSuggestion(
368     AutofillType type) {
369   // Don't show the access Address Book prompt if the user has built up any
370   // Autofill state.
371   if (!web_profiles_.empty())
372     return false;
373
374   if (!enabled_pref_->GetValue())
375     return false;
376
377   if (HasQueriedMacAddressBook(pref_service_))
378     return false;
379
380   if (AccessAddressBookPromptCount() >= kMaxTimesToShowMacAddressBook)
381     return false;
382
383   switch (type.group()) {
384     case ADDRESS_BILLING:
385     case ADDRESS_HOME:
386     case EMAIL:
387     case NAME:
388     case NAME_BILLING:
389     case PHONE_BILLING:
390     case PHONE_HOME:
391       return true;
392     case NO_GROUP:
393     case COMPANY:
394     case CREDIT_CARD:
395     case PASSWORD_FIELD:
396     case TRANSACTION:
397       return false;
398   }
399
400   return false;
401 }
402
403 void PersonalDataManager::ShowedAccessAddressBookPrompt() {
404   pref_service_->SetInteger(prefs::kAutofillMacAddressBookShowedCount,
405                             AccessAddressBookPromptCount() + 1);
406 }
407
408 int PersonalDataManager::AccessAddressBookPromptCount() {
409   return pref_service_->GetInteger(prefs::kAutofillMacAddressBookShowedCount);
410 }
411
412 void PersonalDataManager::BinaryChanging() {
413   g_binary_changed = true;
414 }
415
416 }  // namespace autofill