| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "base/lazy_instance.h" | |
| 6 #include "base/mac/mac_util.h" | |
| 7 #include "base/sys_string_conversions.h" | |
| 8 #import "chrome/browser/autofill/autofill-inl.h" | |
| 9 #import "chrome/browser/autofill/autofill_address_model_mac.h" | |
| 10 #import "chrome/browser/autofill/autofill_address_sheet_controller_mac.h" | |
| 11 #import "chrome/browser/autofill/autofill_credit_card_model_mac.h" | |
| 12 #import "chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h" | |
| 13 #import "chrome/browser/autofill/autofill_dialog_controller_mac.h" | |
| 14 #import "chrome/browser/autofill/personal_data_manager.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/prefs/pref_service.h" | |
| 17 #include "chrome/browser/profiles/profile.h" | |
| 18 #include "chrome/browser/ui/browser.h" | |
| 19 #include "chrome/browser/ui/browser_list.h" | |
| 20 #import "chrome/browser/ui/cocoa/window_size_autosaver.h" | |
| 21 #include "chrome/common/pref_names.h" | |
| 22 #include "content/common/notification_details.h" | |
| 23 #include "content/common/notification_observer.h" | |
| 24 #include "grit/app_resources.h" | |
| 25 #include "grit/generated_resources.h" | |
| 26 #include "grit/theme_resources.h" | |
| 27 #include "ui/base/l10n/l10n_util.h" | |
| 28 #include "ui/base/resource/resource_bundle.h" | |
| 29 #include "ui/gfx/image.h" | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 // Type for singleton object that contains the instance of the visible | |
| 34 // dialog. | |
| 35 typedef std::map<Profile*, AutoFillDialogController*> ProfileControllerMap; | |
| 36 | |
| 37 static base::LazyInstance<ProfileControllerMap> g_profile_controller_map( | |
| 38 base::LINKER_INITIALIZED); | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 // Delegate protocol that needs to be in place for the AutoFillTableView's | |
| 43 // handling of delete and backspace keys. | |
| 44 @protocol DeleteKeyDelegate | |
| 45 - (IBAction)deleteSelection:(id)sender; | |
| 46 @end | |
| 47 | |
| 48 // A subclass of NSTableView that allows for deleting selected elements using | |
| 49 // the delete or backspace keys. | |
| 50 @interface AutoFillTableView : NSTableView { | |
| 51 } | |
| 52 @end | |
| 53 | |
| 54 @implementation AutoFillTableView | |
| 55 | |
| 56 // We override the keyDown method to dispatch the |deleteSelection:| action | |
| 57 // when the user presses the delete or backspace keys. Note a delegate must | |
| 58 // be present that conforms to the DeleteKeyDelegate protocol. | |
| 59 - (void)keyDown:(NSEvent *)event { | |
| 60 id object = [self delegate]; | |
| 61 unichar c = [[event characters] characterAtIndex: 0]; | |
| 62 | |
| 63 // If the user pressed delete and the delegate supports deleteSelection: | |
| 64 if ((c == NSDeleteFunctionKey || | |
| 65 c == NSDeleteCharFunctionKey || | |
| 66 c == NSDeleteCharacter) && | |
| 67 [object respondsToSelector:@selector(deleteSelection:)]) { | |
| 68 id <DeleteKeyDelegate> delegate = (id <DeleteKeyDelegate>) object; | |
| 69 | |
| 70 [delegate deleteSelection:self]; | |
| 71 } else { | |
| 72 [super keyDown:event]; | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 @end | |
| 77 | |
| 78 // Private interface. | |
| 79 @interface AutoFillDialogController (PrivateMethods) | |
| 80 // Save profiles and credit card information after user modification. | |
| 81 - (void)save; | |
| 82 | |
| 83 // Asyncronous handler for when PersonalDataManager data loads. The | |
| 84 // personal data manager notifies the dialog with this method when the | |
| 85 // data loading is complete and ready to be used. | |
| 86 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles | |
| 87 creditCards:(const std::vector<CreditCard*>&)creditCards; | |
| 88 | |
| 89 // Asyncronous handler for when PersonalDataManager data changes. The | |
| 90 // personal data manager notifies the dialog with this method when the | |
| 91 // data has changed. | |
| 92 - (void)onPersonalDataChanged:(const std::vector<AutoFillProfile*>&)profiles | |
| 93 creditCards:(const std::vector<CreditCard*>&)creditCards; | |
| 94 | |
| 95 // Called upon changes to AutoFill preferences that should be reflected in the | |
| 96 // UI. | |
| 97 - (void)preferenceDidChange:(const std::string&)preferenceName; | |
| 98 | |
| 99 // Adjust the selected index when underlying data changes. | |
| 100 // Selects the previous row if possible, else current row, else deselect all. | |
| 101 - (void) adjustSelectionOnDelete:(NSInteger)selectedRow; | |
| 102 | |
| 103 // Adjust the selected index when underlying data changes. | |
| 104 // Selects the current row if possible, else previous row, else deselect all. | |
| 105 - (void) adjustSelectionOnReload:(NSInteger)selectedRow; | |
| 106 | |
| 107 // Returns true if |row| is an index to a valid profile in |tableView_|, and | |
| 108 // false otherwise. | |
| 109 - (BOOL)isProfileRow:(NSInteger)row; | |
| 110 | |
| 111 // Returns true if |row| is an index to the profile group row in |tableView_|, | |
| 112 // and false otherwise. | |
| 113 - (BOOL)isProfileGroupRow:(NSInteger)row; | |
| 114 | |
| 115 // Returns true if |row| is an index to a valid credit card in |tableView_|, and | |
| 116 // false otherwise. | |
| 117 - (BOOL)isCreditCardRow:(NSInteger)row; | |
| 118 | |
| 119 // Returns true if |row| is the index to the credit card group row in | |
| 120 // |tableView_|, and false otherwise. | |
| 121 - (BOOL)isCreditCardGroupRow:(NSInteger)row; | |
| 122 | |
| 123 // Returns the index to |profiles_| of the corresponding |row| in |tableView_|. | |
| 124 - (size_t)profileIndexFromRow:(NSInteger)row; | |
| 125 | |
| 126 // Returns the index to |creditCards_| of the corresponding |row| in | |
| 127 // |tableView_|. | |
| 128 - (size_t)creditCardIndexFromRow:(NSInteger)row; | |
| 129 | |
| 130 // Returns the |row| in |tableView_| that corresponds to the index |i| into | |
| 131 // |profiles_|. | |
| 132 - (NSInteger)rowFromProfileIndex:(size_t)i; | |
| 133 | |
| 134 // Returns the |row| in |tableView_| that corresponds to the index |i| into | |
| 135 // |creditCards_|. | |
| 136 - (NSInteger)rowFromCreditCardIndex:(size_t)row; | |
| 137 | |
| 138 @end | |
| 139 | |
| 140 namespace AutoFillDialogControllerInternal { | |
| 141 | |
| 142 // PersonalDataManagerObserver facilitates asynchronous loading of | |
| 143 // PersonalDataManager data before showing the AutoFill settings data to the | |
| 144 // user. It acts as a C++-based delegate for the |AutoFillDialogController|. | |
| 145 class PersonalDataManagerObserver : public PersonalDataManager::Observer { | |
| 146 public: | |
| 147 explicit PersonalDataManagerObserver( | |
| 148 AutoFillDialogController* controller, | |
| 149 PersonalDataManager* personal_data_manager, | |
| 150 Profile* profile) | |
| 151 : controller_(controller), | |
| 152 personal_data_manager_(personal_data_manager), | |
| 153 profile_(profile) { | |
| 154 } | |
| 155 | |
| 156 virtual ~PersonalDataManagerObserver(); | |
| 157 | |
| 158 // Notifies the observer that the PersonalDataManager has finished loading. | |
| 159 virtual void OnPersonalDataLoaded(); | |
| 160 | |
| 161 // Notifies the observer that the PersonalDataManager data has changed. | |
| 162 virtual void OnPersonalDataChanged(); | |
| 163 | |
| 164 private: | |
| 165 // Utility method to remove |this| from |personal_data_manager_| as an | |
| 166 // observer. | |
| 167 void RemoveObserver(); | |
| 168 | |
| 169 // The dialog controller to be notified when the data loading completes. | |
| 170 // Weak reference. | |
| 171 AutoFillDialogController* controller_; | |
| 172 | |
| 173 // The object in which we are registered as an observer. We hold on to | |
| 174 // it to facilitate un-registering ourself in the destructor and in the | |
| 175 // |OnPersonalDataLoaded| method. This may be NULL. | |
| 176 // Weak reference. | |
| 177 PersonalDataManager* personal_data_manager_; | |
| 178 | |
| 179 // Profile of caller. Held as weak reference. May not be NULL. | |
| 180 Profile* profile_; | |
| 181 | |
| 182 private: | |
| 183 DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerObserver); | |
| 184 }; | |
| 185 | |
| 186 // During destruction ensure that we are removed from the | |
| 187 // |personal_data_manager_| as an observer. | |
| 188 PersonalDataManagerObserver::~PersonalDataManagerObserver() { | |
| 189 RemoveObserver(); | |
| 190 } | |
| 191 | |
| 192 void PersonalDataManagerObserver::RemoveObserver() { | |
| 193 if (personal_data_manager_) { | |
| 194 personal_data_manager_->RemoveObserver(this); | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 // The data has been loaded, notify the controller. | |
| 199 void PersonalDataManagerObserver::OnPersonalDataLoaded() { | |
| 200 [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles() | |
| 201 creditCards:personal_data_manager_->credit_cards()]; | |
| 202 } | |
| 203 | |
| 204 // The data has changed, notify the controller. | |
| 205 void PersonalDataManagerObserver::OnPersonalDataChanged() { | |
| 206 [controller_ onPersonalDataChanged:personal_data_manager_->web_profiles() | |
| 207 creditCards:personal_data_manager_->credit_cards()]; | |
| 208 } | |
| 209 | |
| 210 // Bridges preference changed notifications to the dialog controller. | |
| 211 class PreferenceObserver : public NotificationObserver { | |
| 212 public: | |
| 213 explicit PreferenceObserver(AutoFillDialogController* controller) | |
| 214 : controller_(controller) {} | |
| 215 | |
| 216 // Overridden from NotificationObserver: | |
| 217 virtual void Observe(NotificationType type, | |
| 218 const NotificationSource& source, | |
| 219 const NotificationDetails& details) { | |
| 220 if (type == NotificationType::PREF_CHANGED) { | |
| 221 const std::string* pref = Details<std::string>(details).ptr(); | |
| 222 if (pref) { | |
| 223 [controller_ preferenceDidChange:*pref]; | |
| 224 } | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 private: | |
| 229 AutoFillDialogController* controller_; | |
| 230 | |
| 231 DISALLOW_COPY_AND_ASSIGN(PreferenceObserver); | |
| 232 }; | |
| 233 | |
| 234 } // namespace AutoFillDialogControllerInternal | |
| 235 | |
| 236 @implementation AutoFillDialogController | |
| 237 | |
| 238 @synthesize autoFillManaged = autoFillManaged_; | |
| 239 @synthesize autoFillManagedAndDisabled = autoFillManagedAndDisabled_; | |
| 240 @synthesize itemIsSelected = itemIsSelected_; | |
| 241 @synthesize multipleSelected = multipleSelected_; | |
| 242 | |
| 243 + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer | |
| 244 profile:(Profile*)profile { | |
| 245 AutoFillDialogController* controller = | |
| 246 [AutoFillDialogController controllerWithObserver:observer | |
| 247 profile:profile]; | |
| 248 [controller runModelessDialog]; | |
| 249 } | |
| 250 | |
| 251 - (void)awakeFromNib { | |
| 252 PersonalDataManager* personal_data_manager = | |
| 253 profile_->GetPersonalDataManager(); | |
| 254 DCHECK(personal_data_manager); | |
| 255 | |
| 256 if (personal_data_manager->IsDataLoaded()) { | |
| 257 // |personalDataManager| data is loaded, we can proceed with the contents. | |
| 258 [self onPersonalDataLoaded:personal_data_manager->web_profiles() | |
| 259 creditCards:personal_data_manager->credit_cards()]; | |
| 260 } | |
| 261 | |
| 262 // Register as listener to listen to subsequent data change notifications. | |
| 263 personalDataManagerObserver_.reset( | |
| 264 new AutoFillDialogControllerInternal::PersonalDataManagerObserver( | |
| 265 self, personal_data_manager, profile_)); | |
| 266 personal_data_manager->SetObserver(personalDataManagerObserver_.get()); | |
| 267 | |
| 268 // Explicitly load the data in the table before window displays to avoid | |
| 269 // nasty flicker as tables update. | |
| 270 [tableView_ reloadData]; | |
| 271 | |
| 272 // Set up edit when double-clicking on a table row. | |
| 273 [tableView_ setDoubleAction:@selector(editSelection:)]; | |
| 274 } | |
| 275 | |
| 276 // NSWindow Delegate callback. When the window closes the controller can | |
| 277 // be released. | |
| 278 - (void)windowWillClose:(NSNotification *)notification { | |
| 279 [tableView_ setDataSource:nil]; | |
| 280 [tableView_ setDelegate:nil]; | |
| 281 [self autorelease]; | |
| 282 | |
| 283 // Remove ourself from the map. | |
| 284 ProfileControllerMap* map = g_profile_controller_map.Pointer(); | |
| 285 ProfileControllerMap::iterator it = map->find(profile_); | |
| 286 if (it != map->end()) { | |
| 287 map->erase(it); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 // Invokes the "Add" sheet for address information. If user saves then the new | |
| 292 // information is added to |profiles_| in |addressAddDidEnd:| method. | |
| 293 - (IBAction)addNewAddress:(id)sender { | |
| 294 DCHECK(!addressSheetController.get()); | |
| 295 | |
| 296 // Create a new default address. | |
| 297 AutoFillProfile newAddress; | |
| 298 | |
| 299 // Create a new address sheet controller in "Add" mode. | |
| 300 addressSheetController.reset( | |
| 301 [[AutoFillAddressSheetController alloc] | |
| 302 initWithProfile:newAddress | |
| 303 mode:kAutoFillAddressAddMode]); | |
| 304 | |
| 305 // Show the sheet. | |
| 306 [NSApp beginSheet:[addressSheetController window] | |
| 307 modalForWindow:[self window] | |
| 308 modalDelegate:self | |
| 309 didEndSelector:@selector(addressAddDidEnd:returnCode:contextInfo:) | |
| 310 contextInfo:NULL]; | |
| 311 } | |
| 312 | |
| 313 // Invokes the "Add" sheet for credit card information. If user saves then the | |
| 314 // new information is added to |creditCards_| in |creditCardAddDidEnd:| method. | |
| 315 - (IBAction)addNewCreditCard:(id)sender { | |
| 316 DCHECK(!creditCardSheetController.get()); | |
| 317 | |
| 318 // Create a new default credit card. | |
| 319 CreditCard newCreditCard; | |
| 320 | |
| 321 // Create a new address sheet controller in "Add" mode. | |
| 322 creditCardSheetController.reset( | |
| 323 [[AutoFillCreditCardSheetController alloc] | |
| 324 initWithCreditCard:newCreditCard | |
| 325 mode:kAutoFillCreditCardAddMode]); | |
| 326 | |
| 327 // Show the sheet. | |
| 328 [NSApp beginSheet:[creditCardSheetController window] | |
| 329 modalForWindow:[self window] | |
| 330 modalDelegate:self | |
| 331 didEndSelector:@selector(creditCardAddDidEnd:returnCode:contextInfo:) | |
| 332 contextInfo:NULL]; | |
| 333 } | |
| 334 | |
| 335 // Add address sheet was dismissed. Non-zero |returnCode| indicates a save. | |
| 336 - (void)addressAddDidEnd:(NSWindow*)sheet | |
| 337 returnCode:(int)returnCode | |
| 338 contextInfo:(void*)contextInfo { | |
| 339 DCHECK(!contextInfo); | |
| 340 | |
| 341 if (returnCode) { | |
| 342 // Create a new address and save it to the |profiles_| list. | |
| 343 AutoFillProfile newAddress; | |
| 344 [addressSheetController copyModelToProfile:&newAddress]; | |
| 345 if (!newAddress.IsEmpty() && !FindByContents(profiles_, newAddress)) { | |
| 346 profiles_.push_back(newAddress); | |
| 347 | |
| 348 // Saving will save to the PDM and the table will refresh when PDM sends | |
| 349 // notification that the underlying model has changed. | |
| 350 [self save]; | |
| 351 | |
| 352 // Update the selection to the newly added item. | |
| 353 NSInteger row = [self rowFromProfileIndex:profiles_.size() - 1]; | |
| 354 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] | |
| 355 byExtendingSelection:NO]; | |
| 356 } | |
| 357 } | |
| 358 [sheet orderOut:self]; | |
| 359 addressSheetController.reset(nil); | |
| 360 } | |
| 361 | |
| 362 // Add credit card sheet was dismissed. Non-zero |returnCode| indicates a save. | |
| 363 - (void)creditCardAddDidEnd:(NSWindow*)sheet | |
| 364 returnCode:(int)returnCode | |
| 365 contextInfo:(void*)contextInfo { | |
| 366 DCHECK(!contextInfo); | |
| 367 | |
| 368 if (returnCode) { | |
| 369 // Create a new credit card and save it to the |creditCards_| list. | |
| 370 CreditCard newCreditCard; | |
| 371 [creditCardSheetController copyModelToCreditCard:&newCreditCard]; | |
| 372 if (!newCreditCard.IsEmpty() && | |
| 373 !FindByContents(creditCards_, newCreditCard)) { | |
| 374 creditCards_.push_back(newCreditCard); | |
| 375 | |
| 376 // Saving will save to the PDM and the table will refresh when PDM sends | |
| 377 // notification that the underlying model has changed. | |
| 378 [self save]; | |
| 379 | |
| 380 // Update the selection to the newly added item. | |
| 381 NSInteger row = [self rowFromCreditCardIndex:creditCards_.size() - 1]; | |
| 382 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] | |
| 383 byExtendingSelection:NO]; | |
| 384 } | |
| 385 } | |
| 386 [sheet orderOut:self]; | |
| 387 creditCardSheetController.reset(nil); | |
| 388 } | |
| 389 | |
| 390 // Deletes selected items; either addresses, credit cards, or a mixture of the | |
| 391 // two depending on the items selected. | |
| 392 - (IBAction)deleteSelection:(id)sender { | |
| 393 NSIndexSet* selection = [tableView_ selectedRowIndexes]; | |
| 394 NSInteger selectedRow = [tableView_ selectedRow]; | |
| 395 | |
| 396 // Loop through from last to first deleting selected items as we go. | |
| 397 for (NSUInteger i = [selection lastIndex]; | |
| 398 i != NSNotFound; | |
| 399 i = [selection indexLessThanIndex:i]) { | |
| 400 // We keep track of the "top most" selection in the list so we know where | |
| 401 // to set new selection below. | |
| 402 selectedRow = i; | |
| 403 | |
| 404 if ([self isProfileRow:i]) { | |
| 405 profiles_.erase( | |
| 406 profiles_.begin() + [self profileIndexFromRow:i]); | |
| 407 } else if ([self isCreditCardRow:i]) { | |
| 408 creditCards_.erase( | |
| 409 creditCards_.begin() + [self creditCardIndexFromRow:i]); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 // Select the previous row if possible, else current row, else deselect all. | |
| 414 [self adjustSelectionOnDelete:selectedRow]; | |
| 415 | |
| 416 // Saving will save to the PDM and the table will refresh when PDM sends | |
| 417 // notification that the underlying model has changed. | |
| 418 [self save]; | |
| 419 } | |
| 420 | |
| 421 // Edits the selected item, either address or credit card depending on the item | |
| 422 // selected. | |
| 423 - (IBAction)editSelection:(id)sender { | |
| 424 NSInteger selectedRow = [tableView_ selectedRow]; | |
| 425 if ([self isProfileRow:selectedRow]) { | |
| 426 if (!addressSheetController.get()) { | |
| 427 int i = [self profileIndexFromRow:selectedRow]; | |
| 428 | |
| 429 // Create a new address sheet controller in "Edit" mode. | |
| 430 addressSheetController.reset( | |
| 431 [[AutoFillAddressSheetController alloc] | |
| 432 initWithProfile:profiles_[i] | |
| 433 mode:kAutoFillAddressEditMode]); | |
| 434 | |
| 435 // Show the sheet. | |
| 436 [NSApp beginSheet:[addressSheetController window] | |
| 437 modalForWindow:[self window] | |
| 438 modalDelegate:self | |
| 439 didEndSelector:@selector(addressEditDidEnd:returnCode:contextInfo:) | |
| 440 contextInfo:&profiles_[i]]; | |
| 441 } | |
| 442 } else if ([self isCreditCardRow:selectedRow]) { | |
| 443 if (!creditCardSheetController.get()) { | |
| 444 int i = [self creditCardIndexFromRow:selectedRow]; | |
| 445 | |
| 446 // Create a new credit card sheet controller in "Edit" mode. | |
| 447 creditCardSheetController.reset( | |
| 448 [[AutoFillCreditCardSheetController alloc] | |
| 449 initWithCreditCard:creditCards_[i] | |
| 450 mode:kAutoFillCreditCardEditMode]); | |
| 451 | |
| 452 // Show the sheet. | |
| 453 [NSApp beginSheet:[creditCardSheetController window] | |
| 454 modalForWindow:[self window] | |
| 455 modalDelegate:self | |
| 456 didEndSelector:@selector(creditCardEditDidEnd:returnCode:contextInfo:) | |
| 457 contextInfo:&creditCards_[i]]; | |
| 458 } | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 // Navigates to the AutoFill help url. | |
| 463 - (IBAction)openHelp:(id)sender { | |
| 464 Browser* browser = BrowserList::GetLastActive(); | |
| 465 | |
| 466 if (!browser || !browser->GetSelectedTabContents()) | |
| 467 browser = Browser::Create(profile_); | |
| 468 browser->OpenAutoFillHelpTabAndActivate(); | |
| 469 } | |
| 470 | |
| 471 // Edit address sheet was dismissed. Non-zero |returnCode| indicates a save. | |
| 472 - (void)addressEditDidEnd:(NSWindow *)sheet | |
| 473 returnCode:(int)returnCode | |
| 474 contextInfo:(void *)contextInfo { | |
| 475 DCHECK(contextInfo != NULL); | |
| 476 if (returnCode) { | |
| 477 AutoFillProfile* profile = static_cast<AutoFillProfile*>(contextInfo); | |
| 478 [addressSheetController copyModelToProfile:profile]; | |
| 479 | |
| 480 if (profile->IsEmpty()) | |
| 481 [tableView_ deselectAll:self]; | |
| 482 profiles_.erase( | |
| 483 std::remove_if(profiles_.begin(), profiles_.end(), | |
| 484 std::mem_fun_ref(&AutoFillProfile::IsEmpty)), | |
| 485 profiles_.end()); | |
| 486 | |
| 487 // Saving will save to the PDM and the table will refresh when PDM sends | |
| 488 // notification that the underlying model has changed. | |
| 489 [self save]; | |
| 490 } | |
| 491 [sheet orderOut:self]; | |
| 492 addressSheetController.reset(nil); | |
| 493 } | |
| 494 | |
| 495 // Edit credit card sheet was dismissed. Non-zero |returnCode| indicates a | |
| 496 // save. | |
| 497 - (void)creditCardEditDidEnd:(NSWindow *)sheet | |
| 498 returnCode:(int)returnCode | |
| 499 contextInfo:(void *)contextInfo { | |
| 500 DCHECK(contextInfo != NULL); | |
| 501 if (returnCode) { | |
| 502 CreditCard* creditCard = static_cast<CreditCard*>(contextInfo); | |
| 503 [creditCardSheetController copyModelToCreditCard:creditCard]; | |
| 504 | |
| 505 if (creditCard->IsEmpty()) | |
| 506 [tableView_ deselectAll:self]; | |
| 507 creditCards_.erase( | |
| 508 std::remove_if( | |
| 509 creditCards_.begin(), creditCards_.end(), | |
| 510 std::mem_fun_ref(&CreditCard::IsEmpty)), | |
| 511 creditCards_.end()); | |
| 512 | |
| 513 // Saving will save to the PDM and the table will refresh when PDM sends | |
| 514 // notification that the underlying model has changed. | |
| 515 [self save]; | |
| 516 } | |
| 517 [sheet orderOut:self]; | |
| 518 creditCardSheetController.reset(nil); | |
| 519 } | |
| 520 | |
| 521 // NSTableView Delegate method. | |
| 522 - (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row { | |
| 523 if ([self isProfileGroupRow:row] || [self isCreditCardGroupRow:row]) | |
| 524 return YES; | |
| 525 return NO; | |
| 526 } | |
| 527 | |
| 528 // NSTableView Delegate method. | |
| 529 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row { | |
| 530 return [self isProfileRow:row] || [self isCreditCardRow:row]; | |
| 531 } | |
| 532 | |
| 533 // NSTableView Delegate method. | |
| 534 - (id)tableView:(NSTableView *)tableView | |
| 535 objectValueForTableColumn:(NSTableColumn *)tableColumn | |
| 536 row:(NSInteger)row { | |
| 537 if ([[tableColumn identifier] isEqualToString:@"Spacer"]) | |
| 538 return @""; | |
| 539 | |
| 540 // Check that we're initialized before supplying data. | |
| 541 if (tableView != tableView_) | |
| 542 return @""; | |
| 543 | |
| 544 // Section label. | |
| 545 if ([self isProfileGroupRow:row]) { | |
| 546 if ([[tableColumn identifier] isEqualToString:@"Summary"]) | |
| 547 return l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES_GROUP_NAME); | |
| 548 else | |
| 549 return @""; | |
| 550 } | |
| 551 | |
| 552 if (row < 0) | |
| 553 return @""; | |
| 554 | |
| 555 // Data row. | |
| 556 if ([self isProfileRow:row]) { | |
| 557 if ([[tableColumn identifier] isEqualToString:@"Summary"]) { | |
| 558 return SysUTF16ToNSString( | |
| 559 profiles_[[self profileIndexFromRow:row]].Label()); | |
| 560 } | |
| 561 | |
| 562 return @""; | |
| 563 } | |
| 564 | |
| 565 // Section label. | |
| 566 if ([self isCreditCardGroupRow:row]) { | |
| 567 if ([[tableColumn identifier] isEqualToString:@"Summary"]) | |
| 568 return l10n_util::GetNSString(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME); | |
| 569 else | |
| 570 return @""; | |
| 571 } | |
| 572 | |
| 573 // Data row. | |
| 574 if ([self isCreditCardRow:row]) { | |
| 575 if ([[tableColumn identifier] isEqualToString:@"Summary"]) { | |
| 576 return SysUTF16ToNSString( | |
| 577 creditCards_[ | |
| 578 [self creditCardIndexFromRow:row]].PreviewSummary()); | |
| 579 } | |
| 580 | |
| 581 return @""; | |
| 582 } | |
| 583 | |
| 584 return @""; | |
| 585 } | |
| 586 | |
| 587 // We implement this delegate method to update our |itemIsSelected| and | |
| 588 // |multipleSelected| properties. | |
| 589 // The "Edit..." and "Remove" buttons' enabled state depends on having a | |
| 590 // valid selection in the table. The "Edit..." button depends on having | |
| 591 // exactly one item selected. | |
| 592 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { | |
| 593 if ([tableView_ selectedRow] >= 0) | |
| 594 [self setItemIsSelected:YES]; | |
| 595 else | |
| 596 [self setItemIsSelected:NO]; | |
| 597 | |
| 598 [self setMultipleSelected:([[tableView_ selectedRowIndexes] count] > 1UL)]; | |
| 599 } | |
| 600 | |
| 601 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { | |
| 602 if (tableView == tableView_) { | |
| 603 // 1 section header, the profiles, 1 section header, the credit cards. | |
| 604 return 1 + profiles_.size() + 1 + creditCards_.size(); | |
| 605 } | |
| 606 | |
| 607 return 0; | |
| 608 } | |
| 609 | |
| 610 // Accessor for |autoFillEnabled| preference state. Note: a checkbox in Nib | |
| 611 // is bound to this via KVO. | |
| 612 - (BOOL)autoFillEnabled { | |
| 613 return autoFillEnabled_.GetValue(); | |
| 614 } | |
| 615 | |
| 616 // Setter for |autoFillEnabled| preference state. | |
| 617 - (void)setAutoFillEnabled:(BOOL)value { | |
| 618 autoFillEnabled_.SetValueIfNotManaged(value ? true : false); | |
| 619 } | |
| 620 | |
| 621 // Accessor for |auxiliaryEnabled| preference state. Note: a checkbox in Nib | |
| 622 // is bound to this via KVO. | |
| 623 - (BOOL)auxiliaryEnabled { | |
| 624 return auxiliaryEnabled_.GetValue(); | |
| 625 } | |
| 626 | |
| 627 // Setter for |auxiliaryEnabled| preference state. | |
| 628 - (void)setAuxiliaryEnabled:(BOOL)value { | |
| 629 if ([self autoFillEnabled]) | |
| 630 auxiliaryEnabled_.SetValueIfNotManaged(value ? true : false); | |
| 631 } | |
| 632 | |
| 633 @end | |
| 634 | |
| 635 @implementation AutoFillDialogController (ExposedForUnitTests) | |
| 636 | |
| 637 + (AutoFillDialogController*) | |
| 638 controllerWithObserver:(AutoFillDialogObserver*)observer | |
| 639 profile:(Profile*)profile { | |
| 640 profile = profile->GetOriginalProfile(); | |
| 641 | |
| 642 ProfileControllerMap* map = g_profile_controller_map.Pointer(); | |
| 643 DCHECK(map != NULL); | |
| 644 ProfileControllerMap::iterator it = map->find(profile); | |
| 645 if (it == map->end()) { | |
| 646 // We should have exactly 1 or 0 entry in the map, no more. That is, | |
| 647 // only one profile can have the AutoFill dialog up at a time. | |
| 648 DCHECK_EQ(map->size(), 0U); | |
| 649 | |
| 650 // Deallocation is done upon window close. See |windowWillClose:|. | |
| 651 AutoFillDialogController* controller = | |
| 652 [[self alloc] initWithObserver:observer profile:profile]; | |
| 653 it = map->insert(std::make_pair(profile, controller)).first; | |
| 654 } | |
| 655 | |
| 656 return it->second; | |
| 657 } | |
| 658 | |
| 659 | |
| 660 // This is the designated initializer for this class. | |
| 661 // |profiles| are non-retained immutable list of AutoFill profiles. | |
| 662 // |creditCards| are non-retained immutable list of credit card info. | |
| 663 - (id)initWithObserver:(AutoFillDialogObserver*)observer | |
| 664 profile:(Profile*)profile { | |
| 665 DCHECK(profile); | |
| 666 // Use initWithWindowNibPath: instead of initWithWindowNibName: so we | |
| 667 // can override it in a unit test. | |
| 668 NSString* nibpath = [base::mac::MainAppBundle() | |
| 669 pathForResource:@"AutoFillDialog" | |
| 670 ofType:@"nib"]; | |
| 671 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { | |
| 672 // Initialize member variables based on input. | |
| 673 observer_ = observer; | |
| 674 profile_ = profile; | |
| 675 | |
| 676 // Initialize the preference observer and watch kAutoFillEnabled. | |
| 677 preferenceObserver_.reset( | |
| 678 new AutoFillDialogControllerInternal::PreferenceObserver(self)); | |
| 679 autoFillEnabled_.Init(prefs::kAutoFillEnabled, profile_->GetPrefs(), | |
| 680 preferenceObserver_.get()); | |
| 681 | |
| 682 // Call |preferenceDidChange| in order to initialize UI state of the | |
| 683 // checkbox. | |
| 684 [self preferenceDidChange:prefs::kAutoFillEnabled]; | |
| 685 | |
| 686 // Initialize the preference observer and watch | |
| 687 // kAutoFillAuxiliaryProfilesEnabled. | |
| 688 auxiliaryEnabled_.Init(prefs::kAutoFillAuxiliaryProfilesEnabled, | |
| 689 profile_->GetPrefs(), | |
| 690 preferenceObserver_.get()); | |
| 691 | |
| 692 // Call |preferenceDidChange| in order to initialize UI state of the | |
| 693 // checkbox. | |
| 694 [self preferenceDidChange:prefs::kAutoFillAuxiliaryProfilesEnabled]; | |
| 695 | |
| 696 // Do not use [NSMutableArray array] here; we need predictable destruction | |
| 697 // which will be prevented by having a reference held by an autorelease | |
| 698 // pool. | |
| 699 } | |
| 700 return self; | |
| 701 } | |
| 702 | |
| 703 // Run modeless. | |
| 704 - (void)runModelessDialog { | |
| 705 // Use stored window geometry if it exists. | |
| 706 if (g_browser_process && g_browser_process->local_state()) { | |
| 707 sizeSaver_.reset([[WindowSizeAutosaver alloc] | |
| 708 initWithWindow:[self window] | |
| 709 prefService:g_browser_process->local_state() | |
| 710 path:prefs::kAutoFillDialogPlacement]); | |
| 711 } | |
| 712 | |
| 713 [self showWindow:nil]; | |
| 714 } | |
| 715 | |
| 716 // Close the dialog. | |
| 717 - (void)closeDialog { | |
| 718 [[self window] performClose:self]; | |
| 719 } | |
| 720 | |
| 721 - (AutoFillAddressSheetController*)addressSheetController { | |
| 722 return addressSheetController.get(); | |
| 723 } | |
| 724 | |
| 725 - (AutoFillCreditCardSheetController*)creditCardSheetController { | |
| 726 return creditCardSheetController.get(); | |
| 727 } | |
| 728 | |
| 729 - (void)selectAddressAtIndex:(size_t)i { | |
| 730 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
| 731 [self rowFromProfileIndex:i]] | |
| 732 byExtendingSelection:NO]; | |
| 733 } | |
| 734 | |
| 735 - (void)selectCreditCardAtIndex:(size_t)i { | |
| 736 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
| 737 [self rowFromCreditCardIndex:i]] | |
| 738 byExtendingSelection:NO]; | |
| 739 } | |
| 740 | |
| 741 - (void)addSelectedAddressAtIndex:(size_t)i { | |
| 742 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
| 743 [self rowFromProfileIndex:i]] | |
| 744 byExtendingSelection:YES]; | |
| 745 } | |
| 746 | |
| 747 - (void)addSelectedCreditCardAtIndex:(size_t)i { | |
| 748 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
| 749 [self rowFromCreditCardIndex:i]] | |
| 750 byExtendingSelection:YES]; | |
| 751 } | |
| 752 | |
| 753 - (BOOL)editButtonEnabled { | |
| 754 return [editButton_ isEnabled]; | |
| 755 } | |
| 756 | |
| 757 - (std::vector<AutoFillProfile>&)profiles { | |
| 758 return profiles_; | |
| 759 } | |
| 760 | |
| 761 - (std::vector<CreditCard>&)creditCards { | |
| 762 return creditCards_; | |
| 763 } | |
| 764 | |
| 765 @end | |
| 766 | |
| 767 @implementation AutoFillDialogController (PrivateMethods) | |
| 768 | |
| 769 // Called when the user modifies the profiles or credit card information. | |
| 770 - (void)save { | |
| 771 // If we have an |observer_| then communicate the changes back, unless | |
| 772 // AutoFill has been disabled through policy in the mean time. | |
| 773 if (observer_ && !autoFillManagedAndDisabled_) { | |
| 774 // Make a working copy of profiles. |OnAutoFillDialogApply| can mutate | |
| 775 // |profiles_|. | |
| 776 std::vector<AutoFillProfile> profiles = profiles_; | |
| 777 | |
| 778 // Make a working copy of credit cards. |OnAutoFillDialogApply| can mutate | |
| 779 // |creditCards_|. | |
| 780 std::vector<CreditCard> creditCards = creditCards_; | |
| 781 | |
| 782 observer_->OnAutoFillDialogApply(&profiles, &creditCards); | |
| 783 } | |
| 784 } | |
| 785 | |
| 786 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles | |
| 787 creditCards:(const std::vector<CreditCard*>&)creditCards { | |
| 788 [self onPersonalDataChanged:profiles creditCards:creditCards]; | |
| 789 } | |
| 790 | |
| 791 - (void)onPersonalDataChanged:(const std::vector<AutoFillProfile*>&)profiles | |
| 792 creditCards:(const std::vector<CreditCard*>&)creditCards { | |
| 793 // Make local copy of |profiles|. | |
| 794 profiles_.clear(); | |
| 795 for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); | |
| 796 iter != profiles.end(); ++iter) | |
| 797 profiles_.push_back(**iter); | |
| 798 | |
| 799 // Make local copy of |creditCards|. | |
| 800 creditCards_.clear(); | |
| 801 for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin(); | |
| 802 iter != creditCards.end(); ++iter) | |
| 803 creditCards_.push_back(**iter); | |
| 804 | |
| 805 [self adjustSelectionOnReload:[tableView_ selectedRow]]; | |
| 806 [tableView_ reloadData]; | |
| 807 } | |
| 808 | |
| 809 - (void)preferenceDidChange:(const std::string&)preferenceName { | |
| 810 if (preferenceName == prefs::kAutoFillEnabled) { | |
| 811 [self setAutoFillEnabled:autoFillEnabled_.GetValue()]; | |
| 812 [self setAutoFillManaged:autoFillEnabled_.IsManaged()]; | |
| 813 [self setAutoFillManagedAndDisabled: | |
| 814 autoFillEnabled_.IsManaged() && !autoFillEnabled_.GetValue()]; | |
| 815 } else if (preferenceName == prefs::kAutoFillAuxiliaryProfilesEnabled) { | |
| 816 [self setAuxiliaryEnabled:auxiliaryEnabled_.GetValue()]; | |
| 817 } else { | |
| 818 NOTREACHED(); | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 - (void) adjustSelectionOnDelete:(NSInteger)selectedRow { | |
| 823 if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) { | |
| 824 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1] | |
| 825 byExtendingSelection:NO]; | |
| 826 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) { | |
| 827 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow] | |
| 828 byExtendingSelection:NO]; | |
| 829 } else { | |
| 830 [tableView_ deselectAll:self]; | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 - (void) adjustSelectionOnReload:(NSInteger)selectedRow { | |
| 835 if ([self tableView:tableView_ shouldSelectRow:selectedRow]) { | |
| 836 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow] | |
| 837 byExtendingSelection:NO]; | |
| 838 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) { | |
| 839 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1] | |
| 840 byExtendingSelection:NO]; | |
| 841 } else { | |
| 842 [tableView_ deselectAll:self]; | |
| 843 } | |
| 844 } | |
| 845 | |
| 846 - (BOOL)isProfileRow:(NSInteger)row { | |
| 847 if (row > 0 && static_cast<size_t>(row) <= profiles_.size()) | |
| 848 return YES; | |
| 849 return NO; | |
| 850 } | |
| 851 | |
| 852 - (BOOL)isProfileGroupRow:(NSInteger)row { | |
| 853 if (row == 0) | |
| 854 return YES; | |
| 855 return NO; | |
| 856 } | |
| 857 | |
| 858 - (BOOL)isCreditCardRow:(NSInteger)row { | |
| 859 if (row > 0 && | |
| 860 static_cast<size_t>(row) >= profiles_.size() + 2 && | |
| 861 static_cast<size_t>(row) <= profiles_.size() + creditCards_.size() + 1) | |
| 862 return YES; | |
| 863 return NO; | |
| 864 } | |
| 865 | |
| 866 - (BOOL)isCreditCardGroupRow:(NSInteger)row { | |
| 867 if (row > 0 && static_cast<size_t>(row) == profiles_.size() + 1) | |
| 868 return YES; | |
| 869 return NO; | |
| 870 } | |
| 871 | |
| 872 - (size_t)profileIndexFromRow:(NSInteger)row { | |
| 873 DCHECK([self isProfileRow:row]); | |
| 874 return static_cast<size_t>(row) - 1; | |
| 875 } | |
| 876 | |
| 877 - (size_t)creditCardIndexFromRow:(NSInteger)row { | |
| 878 DCHECK([self isCreditCardRow:row]); | |
| 879 return static_cast<size_t>(row) - (profiles_.size() + 2); | |
| 880 } | |
| 881 | |
| 882 - (NSInteger)rowFromProfileIndex:(size_t)i { | |
| 883 return 1 + i; | |
| 884 } | |
| 885 | |
| 886 - (NSInteger)rowFromCreditCardIndex:(size_t)i { | |
| 887 return 1 + profiles_.size() + 1 + i; | |
| 888 } | |
| 889 | |
| 890 @end | |
| 891 | |
| 892 // An NSValueTransformer subclass for use in validation of phone number | |
| 893 // fields. Transforms an invalid phone number string into a warning image. | |
| 894 // This data transformer is used in the credit card sheet for invalid phone and | |
| 895 // fax numbers. | |
| 896 @interface InvalidPhoneTransformer : NSValueTransformer { | |
| 897 } | |
| 898 @end | |
| 899 | |
| 900 @implementation InvalidPhoneTransformer | |
| 901 + (Class)transformedValueClass { | |
| 902 return [NSImage class]; | |
| 903 } | |
| 904 | |
| 905 + (BOOL)allowsReverseTransformation { | |
| 906 return NO; | |
| 907 } | |
| 908 | |
| 909 - (id)transformedValue:(id)string { | |
| 910 NSImage* image = nil; | |
| 911 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 912 | |
| 913 // We display no validation icon when input has not yet been entered. | |
| 914 if (string == nil || [string length] == 0) | |
| 915 return nil; | |
| 916 | |
| 917 // If we have input then display alert icon if we have an invalid number. | |
| 918 if (string != nil && [string length] != 0) { | |
| 919 // TODO(dhollowa): Using SetInfo() call to validate phone number. Should | |
| 920 // have explicit validation method. More robust validation is needed as | |
| 921 // well eventually. | |
| 922 AutoFillProfile profile; | |
| 923 profile.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), | |
| 924 base::SysNSStringToUTF16(string)); | |
| 925 if (profile.GetFieldText(AutofillType(PHONE_HOME_WHOLE_NUMBER)).empty()) { | |
| 926 image = rb.GetNativeImageNamed(IDR_INPUT_ALERT); | |
| 927 DCHECK(image); | |
| 928 return image; | |
| 929 } | |
| 930 } | |
| 931 | |
| 932 // No alert icon, so must be valid input. | |
| 933 if (!image) { | |
| 934 image = rb.GetNativeImageNamed(IDR_INPUT_GOOD); | |
| 935 DCHECK(image); | |
| 936 return image; | |
| 937 } | |
| 938 | |
| 939 return nil; | |
| 940 } | |
| 941 | |
| 942 @end | |
| OLD | NEW |