Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(759)

Side by Side Diff: components/autofill/browser/personal_data_manager_mac.mm

Issue 17392006: In components/autofill, move browser/ to core/browser/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase to fix conflicts Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/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/strings/stringprintf.h"
18 #include "base/strings/sys_string_conversions.h"
19 #include "components/autofill/browser/autofill_country.h"
20 #include "components/autofill/browser/autofill_profile.h"
21 #include "components/autofill/browser/phone_number.h"
22 #include "grit/component_strings.h"
23 #include "ui/base/l10n/l10n_util_mac.h"
24
25 namespace autofill {
26 namespace {
27
28 const char kAddressBookOrigin[] = "OS X Address Book";
29
30 // This implementation makes use of the Address Book API. Profiles are
31 // generated that correspond to addresses in the "me" card that reside in the
32 // user's Address Book. The caller passes a vector of profiles into the
33 // the constructer and then initiate the fetch from the Mac Address Book "me"
34 // card using the main |GetAddressBookMeCard()| method. This clears any
35 // existing addresses and populates new addresses derived from the data found
36 // in the "me" card.
37 class AuxiliaryProfilesImpl {
38 public:
39 // Constructor takes a reference to the |profiles| that will be filled in
40 // by the subsequent call to |GetAddressBookMeCard()|. |profiles| may not
41 // be NULL.
42 explicit AuxiliaryProfilesImpl(ScopedVector<AutofillProfile>* profiles)
43 : profiles_(*profiles) {
44 }
45 virtual ~AuxiliaryProfilesImpl() {}
46
47 // Import the "me" card from the Mac Address Book and fill in |profiles_|.
48 void GetAddressBookMeCard(const std::string& app_locale);
49
50 private:
51 void GetAddressBookNames(ABPerson* me,
52 NSString* addressLabelRaw,
53 AutofillProfile* profile);
54 void GetAddressBookAddress(const std::string& app_locale,
55 NSDictionary* address,
56 AutofillProfile* profile);
57 void GetAddressBookEmail(ABPerson* me,
58 NSString* addressLabelRaw,
59 AutofillProfile* profile);
60 void GetAddressBookPhoneNumbers(ABPerson* me,
61 NSString* addressLabelRaw,
62 AutofillProfile* profile);
63
64 private:
65 // A reference to the profiles this class populates.
66 ScopedVector<AutofillProfile>& profiles_;
67
68 DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesImpl);
69 };
70
71 // This method uses the |ABAddressBook| system service to fetch the "me" card
72 // from the active user's address book. It looks for the user address
73 // information and translates it to the internal list of |AutofillProfile| data
74 // structures.
75 void AuxiliaryProfilesImpl::GetAddressBookMeCard(
76 const std::string& app_locale) {
77 profiles_.clear();
78
79 // +[ABAddressBook sharedAddressBook] throws an exception internally in
80 // circumstances that aren't clear. The exceptions are only observed in crash
81 // reports, so it is unknown whether they would be caught by AppKit and nil
82 // returned, or if they would take down the app. In either case, avoid
83 // crashing. http://crbug.com/129022
84 ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions(^{
85 return [ABAddressBook sharedAddressBook];
86 });
87 ABPerson* me = [addressBook me];
88 if (!me)
89 return;
90
91 ABMultiValue* addresses = [me valueForProperty:kABAddressProperty];
92
93 // The number of characters at the end of the GUID to reserve for
94 // distinguishing addresses within the "me" card. Cap the number of addresses
95 // we will fetch to the number that can be distinguished by this fragment of
96 // the GUID.
97 const size_t kNumAddressGUIDChars = 2;
98 const size_t kNumHexDigits = 16;
99 const size_t kMaxAddressCount = pow(kNumHexDigits, kNumAddressGUIDChars);
100 NSUInteger count = MIN([addresses count], kMaxAddressCount);
101 for (NSUInteger i = 0; i < count; i++) {
102 NSDictionary* address = [addresses valueAtIndex:i];
103 NSString* addressLabelRaw = [addresses labelAtIndex:i];
104
105 // Create a new profile where the guid is set to the guid portion of the
106 // |kABUIDProperty| taken from from the "me" address. The format of
107 // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the
108 // raw guid here and using it directly, with one modification: we update the
109 // last |kNumAddressGUIDChars| characters in the GUID to reflect the address
110 // variant. Note that we capped the number of addresses above, so this is
111 // safe.
112 const size_t kGUIDLength = 36U;
113 const size_t kTrimmedGUIDLength = kGUIDLength - kNumAddressGUIDChars;
114 std::string guid = base::SysNSStringToUTF8(
115 [me valueForProperty:kABUIDProperty]).substr(0, kTrimmedGUIDLength);
116
117 // The format string to print |kNumAddressGUIDChars| hexadecimal characters,
118 // left-padded with 0's.
119 const std::string kAddressGUIDFormat =
120 base::StringPrintf("%%0%" PRIuS "X", kNumAddressGUIDChars);
121 guid += base::StringPrintf(kAddressGUIDFormat.c_str(), i);
122 DCHECK_EQ(kGUIDLength, guid.size());
123
124 scoped_ptr<AutofillProfile> profile(
125 new AutofillProfile(guid, kAddressBookOrigin));
126 DCHECK(base::IsValidGUID(profile->guid()));
127
128 // Fill in name and company information.
129 GetAddressBookNames(me, addressLabelRaw, profile.get());
130
131 // Fill in address information.
132 GetAddressBookAddress(app_locale, address, profile.get());
133
134 // Fill in email information.
135 GetAddressBookEmail(me, addressLabelRaw, profile.get());
136
137 // Fill in phone number information.
138 GetAddressBookPhoneNumbers(me, addressLabelRaw, profile.get());
139
140 profiles_.push_back(profile.release());
141 }
142 }
143
144 // Name and company information is stored once in the Address Book against
145 // multiple addresses. We replicate that information for each profile.
146 // We only propagate the company name to work profiles.
147 void AuxiliaryProfilesImpl::GetAddressBookNames(
148 ABPerson* me,
149 NSString* addressLabelRaw,
150 AutofillProfile* profile) {
151 NSString* firstName = [me valueForProperty:kABFirstNameProperty];
152 NSString* middleName = [me valueForProperty:kABMiddleNameProperty];
153 NSString* lastName = [me valueForProperty:kABLastNameProperty];
154 NSString* companyName = [me valueForProperty:kABOrganizationProperty];
155
156 profile->SetRawInfo(NAME_FIRST, base::SysNSStringToUTF16(firstName));
157 profile->SetRawInfo(NAME_MIDDLE, base::SysNSStringToUTF16(middleName));
158 profile->SetRawInfo(NAME_LAST, base::SysNSStringToUTF16(lastName));
159 if ([addressLabelRaw isEqualToString:kABAddressWorkLabel])
160 profile->SetRawInfo(COMPANY_NAME, base::SysNSStringToUTF16(companyName));
161 }
162
163 // Addresss information from the Address Book may span multiple lines.
164 // If it does then we represent the address with two lines in the profile. The
165 // second line we join with commas.
166 // For example: "c/o John Doe\n1122 Other Avenue\nApt #7" translates to
167 // line 1: "c/o John Doe", line 2: "1122 Other Avenue, Apt #7".
168 void AuxiliaryProfilesImpl::GetAddressBookAddress(const std::string& app_locale,
169 NSDictionary* address,
170 AutofillProfile* profile) {
171 if (NSString* addressField = [address objectForKey:kABAddressStreetKey]) {
172 // If there are newlines in the address, split into two lines.
173 if ([addressField rangeOfCharacterFromSet:
174 [NSCharacterSet newlineCharacterSet]].location != NSNotFound) {
175 NSArray* chunks = [addressField componentsSeparatedByCharactersInSet:
176 [NSCharacterSet newlineCharacterSet]];
177 DCHECK([chunks count] > 1);
178
179 NSString* separator = l10n_util::GetNSString(
180 IDS_AUTOFILL_MAC_ADDRESS_LINE_SEPARATOR);
181
182 NSString* addressField1 = [chunks objectAtIndex:0];
183 NSString* addressField2 =
184 [[chunks subarrayWithRange:NSMakeRange(1, [chunks count] - 1)]
185 componentsJoinedByString:separator];
186 profile->SetRawInfo(ADDRESS_HOME_LINE1,
187 base::SysNSStringToUTF16(addressField1));
188 profile->SetRawInfo(ADDRESS_HOME_LINE2,
189 base::SysNSStringToUTF16(addressField2));
190 } else {
191 profile->SetRawInfo(ADDRESS_HOME_LINE1,
192 base::SysNSStringToUTF16(addressField));
193 }
194 }
195
196 if (NSString* city = [address objectForKey:kABAddressCityKey])
197 profile->SetRawInfo(ADDRESS_HOME_CITY, base::SysNSStringToUTF16(city));
198
199 if (NSString* state = [address objectForKey:kABAddressStateKey])
200 profile->SetRawInfo(ADDRESS_HOME_STATE, base::SysNSStringToUTF16(state));
201
202 if (NSString* zip = [address objectForKey:kABAddressZIPKey])
203 profile->SetRawInfo(ADDRESS_HOME_ZIP, base::SysNSStringToUTF16(zip));
204
205 if (NSString* country = [address objectForKey:kABAddressCountryKey]) {
206 profile->SetInfo(ADDRESS_HOME_COUNTRY,
207 base::SysNSStringToUTF16(country),
208 app_locale);
209 }
210 }
211
212 // Fills in email address matching current address label. Note that there may
213 // be multiple matching email addresses for a given label. We take the
214 // first we find (topmost) as preferred.
215 void AuxiliaryProfilesImpl::GetAddressBookEmail(
216 ABPerson* me,
217 NSString* addressLabelRaw,
218 AutofillProfile* profile) {
219 ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty];
220 NSString* emailAddress = nil;
221 for (NSUInteger j = 0, emailCount = [emailAddresses count];
222 j < emailCount; j++) {
223 NSString* emailAddressLabelRaw = [emailAddresses labelAtIndex:j];
224 if ([emailAddressLabelRaw isEqualToString:addressLabelRaw]) {
225 emailAddress = [emailAddresses valueAtIndex:j];
226 break;
227 }
228 }
229 profile->SetRawInfo(EMAIL_ADDRESS, base::SysNSStringToUTF16(emailAddress));
230 }
231
232 // Fills in telephone numbers. Each of these are special cases.
233 // We match two cases: home/tel, work/tel.
234 // Note, we traverse in reverse order so that top values in address book
235 // take priority.
236 void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers(
237 ABPerson* me,
238 NSString* addressLabelRaw,
239 AutofillProfile* profile) {
240 ABMultiValue* phoneNumbers = [me valueForProperty:kABPhoneProperty];
241 for (NSUInteger k = 0, phoneCount = [phoneNumbers count];
242 k < phoneCount; k++) {
243 NSUInteger reverseK = phoneCount - k - 1;
244 NSString* phoneLabelRaw = [phoneNumbers labelAtIndex:reverseK];
245 if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] &&
246 [phoneLabelRaw isEqualToString:kABPhoneHomeLabel]) {
247 base::string16 homePhone = base::SysNSStringToUTF16(
248 [phoneNumbers valueAtIndex:reverseK]);
249 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, homePhone);
250 } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
251 [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) {
252 base::string16 workPhone = base::SysNSStringToUTF16(
253 [phoneNumbers valueAtIndex:reverseK]);
254 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, workPhone);
255 } else if ([phoneLabelRaw isEqualToString:kABPhoneMobileLabel] ||
256 [phoneLabelRaw isEqualToString:kABPhoneMainLabel]) {
257 base::string16 phone = base::SysNSStringToUTF16(
258 [phoneNumbers valueAtIndex:reverseK]);
259 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, phone);
260 }
261 }
262 }
263
264 } // namespace
265
266 // Populate |auxiliary_profiles_| with the Address Book data.
267 void PersonalDataManager::LoadAuxiliaryProfiles() {
268 AuxiliaryProfilesImpl impl(&auxiliary_profiles_);
269 impl.GetAddressBookMeCard(app_locale_);
270 }
271
272 } // namespace autofill
OLDNEW
« no previous file with comments | « components/autofill/browser/personal_data_manager.cc ('k') | components/autofill/browser/personal_data_manager_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698