summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill/autofill_dialog_controller_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/autofill/autofill_dialog_controller_mac.mm')
-rw-r--r--chrome/browser/autofill/autofill_dialog_controller_mac.mm837
1 files changed, 837 insertions, 0 deletions
diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac.mm b/chrome/browser/autofill/autofill_dialog_controller_mac.mm
new file mode 100644
index 0000000..594808b
--- /dev/null
+++ b/chrome/browser/autofill/autofill_dialog_controller_mac.mm
@@ -0,0 +1,837 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/autofill/autofill_dialog_controller_mac.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/mac_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#import "chrome/browser/autofill/autofill_address_model_mac.h"
+#import "chrome/browser/autofill/autofill_address_sheet_controller_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_model_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h"
+#import "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/browser_process.h"
+#import "chrome/browser/cocoa/window_size_autosaver.h"
+#include "chrome/browser/pref_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+#include "grit/generated_resources.h"
+#include "grit/app_resources.h"
+#include "grit/theme_resources.h"
+
+namespace {
+
+// Update profile labels passed as |input|. When profile data changes as a
+// result of adding new profiles, edititing existing profiles, or deleting a
+// profile, then the list of profiles need to have their derived labels
+// recomputed.
+void UpdateProfileLabels(std::vector<AutoFillProfile>* input) {
+ DCHECK(input);
+ std::vector<AutoFillProfile*> profiles;
+ profiles.resize(input->size());
+ for (size_t i = 0; i < input->size(); ++i) {
+ profiles[i] = &(*input)[i];
+ }
+ AutoFillProfile::AdjustInferredLabels(&profiles);
+}
+
+} // namespace
+
+// Delegate protocol that needs to be in place for the AutoFillTableView's
+// handling of delete and backspace keys.
+@protocol DeleteKeyDelegate
+- (IBAction)deleteSelection:(id)sender;
+@end
+
+// A subclass of NSTableView that allows for deleting selected elements using
+// the delete or backspace keys.
+@interface AutoFillTableView : NSTableView {
+}
+@end
+
+@implementation AutoFillTableView
+
+// We override the keyDown method to dispatch the |deleteSelection:| action
+// when the user presses the delete or backspace keys. Note a delegate must
+// be present that conforms to the DeleteKeyDelegate protocol.
+- (void)keyDown:(NSEvent *)event {
+ id object = [self delegate];
+ unichar c = [[event characters] characterAtIndex: 0];
+
+ // If the user pressed delete and the delegate supports deleteSelection:
+ if ((c == NSDeleteFunctionKey ||
+ c == NSDeleteCharFunctionKey ||
+ c == NSDeleteCharacter) &&
+ [object respondsToSelector:@selector(deleteSelection:)]) {
+ id <DeleteKeyDelegate> delegate = (id <DeleteKeyDelegate>) object;
+
+ [delegate deleteSelection:self];
+ } else {
+ [super keyDown:event];
+ }
+}
+
+@end
+
+// Private interface.
+@interface AutoFillDialogController (PrivateMethods)
+// Asyncronous handler for when PersonalDataManager data loads. The
+// personal data manager notifies the dialog with this method when the
+// data loading is complete and ready to be used.
+- (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards;
+
+// Returns true if |row| is an index to a valid profile in |tableView_|, and
+// false otherwise.
+- (BOOL)isProfileRow:(NSInteger)row;
+
+// Returns true if |row| is an index to the profile group row in |tableView_|,
+// and false otherwise.
+- (BOOL)isProfileGroupRow:(NSInteger)row;
+
+// Returns true if |row| is an index to a valid credit card in |tableView_|, and
+// false otherwise.
+- (BOOL)isCreditCardRow:(NSInteger)row;
+
+// Returns true if |row| is the index to the credit card group row in
+// |tableView_|, and false otherwise.
+- (BOOL)isCreditCardGroupRow:(NSInteger)row;
+
+// Returns the index to |profiles_| of the corresponding |row| in |tableView_|.
+- (size_t)profileIndexFromRow:(NSInteger)row;
+
+// Returns the index to |creditCards_| of the corresponding |row| in
+// |tableView_|.
+- (size_t)creditCardIndexFromRow:(NSInteger)row;
+
+// Returns the |row| in |tableView_| that corresponds to the index |i| into
+// |profiles_|.
+- (NSInteger)rowFromProfileIndex:(size_t)i;
+
+// Returns the |row| in |tableView_| that corresponds to the index |i| into
+// |creditCards_|.
+- (NSInteger)rowFromCreditCardIndex:(size_t)row;
+
+// Invokes the modal dialog.
+- (void)runModalDialog;
+
+@end
+
+namespace AutoFillDialogControllerInternal {
+
+// PersonalDataManagerObserver facilitates asynchronous loading of
+// PersonalDataManager data before showing the AutoFill settings data to the
+// user. It acts as a C++-based delegate for the |AutoFillDialogController|.
+class PersonalDataManagerObserver : public PersonalDataManager::Observer {
+ public:
+ explicit PersonalDataManagerObserver(
+ AutoFillDialogController* controller,
+ PersonalDataManager* personal_data_manager,
+ Profile* profile)
+ : controller_(controller),
+ personal_data_manager_(personal_data_manager),
+ profile_(profile) {
+ }
+
+ virtual ~PersonalDataManagerObserver();
+
+ // Notifies the observer that the PersonalDataManager has finished loading.
+ virtual void OnPersonalDataLoaded();
+
+ private:
+ // Utility method to remove |this| from |personal_data_manager_| as an
+ // observer.
+ void RemoveObserver();
+
+ // The dialog controller to be notified when the data loading completes.
+ // Weak reference.
+ AutoFillDialogController* controller_;
+
+ // The object in which we are registered as an observer. We hold on to
+ // it to facilitate un-registering ourself in the destructor and in the
+ // |OnPersonalDataLoaded| method. This may be NULL.
+ // Weak reference.
+ PersonalDataManager* personal_data_manager_;
+
+ // Profile of caller. Held as weak reference. May not be NULL.
+ Profile* profile_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerObserver);
+};
+
+// During destruction ensure that we are removed from the
+// |personal_data_manager_| as an observer.
+PersonalDataManagerObserver::~PersonalDataManagerObserver() {
+ RemoveObserver();
+}
+
+void PersonalDataManagerObserver::RemoveObserver() {
+ if (personal_data_manager_) {
+ personal_data_manager_->RemoveObserver(this);
+ }
+}
+
+// The data is ready so display our data. Notify the dialog controller that
+// the data is ready. Once done we clear the observer.
+void PersonalDataManagerObserver::OnPersonalDataLoaded() {
+ RemoveObserver();
+ [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles()
+ creditCards:personal_data_manager_->credit_cards()];
+}
+
+} // namespace AutoFillDialogControllerInternal
+
+@implementation AutoFillDialogController
+
+@synthesize autoFillEnabled = autoFillEnabled_;
+@synthesize auxiliaryEnabled = auxiliaryEnabled_;
+@synthesize itemIsSelected = itemIsSelected_;
+@synthesize multipleSelected = multipleSelected_;
+
++ (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer
+ profile:(Profile*)profile
+ importedProfile:(AutoFillProfile*) importedProfile
+ importedCreditCard:(CreditCard*) importedCreditCard {
+ AutoFillDialogController* controller =
+ [AutoFillDialogController controllerWithObserver:observer
+ profile:profile
+ importedProfile:importedProfile
+ importedCreditCard:importedCreditCard];
+
+ // Only run modal dialog if it is not already being shown.
+ if (![controller isWindowLoaded]) {
+ [controller runModalDialog];
+ }
+}
+
+- (void)awakeFromNib {
+ PersonalDataManager* personal_data_manager =
+ profile_->GetPersonalDataManager();
+ DCHECK(personal_data_manager);
+
+ if (personal_data_manager->IsDataLoaded()) {
+ // |personalDataManager| data is loaded, we can proceed with the contents.
+ [self onPersonalDataLoaded:personal_data_manager->web_profiles()
+ creditCards:personal_data_manager->credit_cards()];
+ } else {
+ // |personalDataManager| data is NOT loaded, so we load it here, installing
+ // our observer.
+ personalDataManagerObserver_.reset(
+ new AutoFillDialogControllerInternal::PersonalDataManagerObserver(
+ self, personal_data_manager, profile_));
+ personal_data_manager->SetObserver(personalDataManagerObserver_.get());
+ }
+
+ // Explicitly load the data in the table before window displays to avoid
+ // nasty flicker as tables update.
+ [tableView_ reloadData];
+
+ // Set up edit when double-clicking on a table row.
+ [tableView_ setDoubleAction:@selector(editSelection:)];
+}
+
+// NSWindow Delegate callback. When the window closes the controller can
+// be released.
+- (void)windowWillClose:(NSNotification *)notification {
+ [tableView_ setDataSource:nil];
+ [tableView_ setDelegate:nil];
+ [self autorelease];
+}
+
+// Called when the user clicks the save button.
+- (IBAction)save:(id)sender {
+ // If we have an |observer_| then communicate the changes back.
+ if (observer_) {
+ profile_->GetPrefs()->SetBoolean(prefs::kAutoFillEnabled, autoFillEnabled_);
+ profile_->GetPrefs()->SetBoolean(prefs::kAutoFillAuxiliaryProfilesEnabled,
+ auxiliaryEnabled_);
+ observer_->OnAutoFillDialogApply(&profiles_, &creditCards_);
+ }
+ [self closeDialog];
+}
+
+// Called when the user clicks the cancel button. All we need to do is stop
+// the modal session.
+- (IBAction)cancel:(id)sender {
+ [self closeDialog];
+}
+
+// Invokes the "Add" sheet for address information. If user saves then the new
+// information is added to |profiles_| in |addressAddDidEnd:| method.
+- (IBAction)addNewAddress:(id)sender {
+ DCHECK(!addressSheetController.get());
+
+ // Create a new default address.
+ string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_ADDRESS);
+ AutoFillProfile newAddress(newName, 0);
+
+ // Create a new address sheet controller in "Add" mode.
+ addressSheetController.reset(
+ [[AutoFillAddressSheetController alloc]
+ initWithProfile:newAddress
+ mode:kAutoFillAddressAddMode]);
+
+ // Show the sheet.
+ [NSApp beginSheet:[addressSheetController window]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(addressAddDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+}
+
+// Invokes the "Add" sheet for credit card information. If user saves then the
+// new information is added to |creditCards_| in |creditCardAddDidEnd:| method.
+- (IBAction)addNewCreditCard:(id)sender {
+ DCHECK(!creditCardSheetController.get());
+
+ // Create a new default credit card.
+ string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_CREDITCARD);
+ CreditCard newCreditCard(newName, 0);
+
+ // Create a new address sheet controller in "Add" mode.
+ creditCardSheetController.reset(
+ [[AutoFillCreditCardSheetController alloc]
+ initWithCreditCard:newCreditCard
+ mode:kAutoFillCreditCardAddMode
+ controller:self]);
+
+ // Show the sheet.
+ [NSApp beginSheet:[creditCardSheetController window]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(creditCardAddDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+}
+
+// Add address sheet was dismissed. Non-zero |returnCode| indicates a save.
+- (void)addressAddDidEnd:(NSWindow*)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void*)contextInfo {
+ DCHECK(contextInfo == NULL);
+
+ if (returnCode) {
+ // Create a new address and save it to the |profiles_| list.
+ AutoFillProfile newAddress(string16(), 0);
+ [addressSheetController copyModelToProfile:&newAddress];
+ if (!newAddress.IsEmpty()) {
+ profiles_.push_back(newAddress);
+
+ // Refresh the view based on new data.
+ UpdateProfileLabels(&profiles_);
+ [tableView_ reloadData];
+
+ // Update the selection to the newly added item.
+ NSInteger row = [self rowFromProfileIndex:profiles_.size() - 1];
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
+ byExtendingSelection:NO];
+ }
+ }
+ [sheet orderOut:self];
+ addressSheetController.reset(nil);
+}
+
+// Add credit card sheet was dismissed. Non-zero |returnCode| indicates a save.
+- (void)creditCardAddDidEnd:(NSWindow *)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void *)contextInfo {
+ DCHECK(contextInfo == NULL);
+
+ if (returnCode) {
+ // Create a new credit card and save it to the |creditCards_| list.
+ CreditCard newCreditCard(string16(), 0);
+ [creditCardSheetController copyModelToCreditCard:&newCreditCard];
+ if (!newCreditCard.IsEmpty()) {
+ creditCards_.push_back(newCreditCard);
+
+ // Refresh the view based on new data.
+ [tableView_ reloadData];
+
+ // Update the selection to the newly added item.
+ NSInteger row = [self rowFromCreditCardIndex:creditCards_.size() - 1];
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
+ byExtendingSelection:NO];
+ }
+ }
+ [sheet orderOut:self];
+ creditCardSheetController.reset(nil);
+}
+
+// Deletes selected items; either addresses, credit cards, or a mixture of the
+// two depending on the items selected.
+- (IBAction)deleteSelection:(id)sender {
+ NSIndexSet* selection = [tableView_ selectedRowIndexes];
+ NSInteger selectedRow = [tableView_ selectedRow];
+
+ // Loop through from last to first deleting selected items as we go.
+ for (NSUInteger i = [selection lastIndex];
+ i != NSNotFound;
+ i = [selection indexLessThanIndex:i]) {
+ // We keep track of the "top most" selection in the list so we know where
+ // to set new selection below.
+ selectedRow = i;
+
+ if ([self isProfileRow:i]) {
+ profiles_.erase(
+ profiles_.begin() + [self profileIndexFromRow:i]);
+ } else if ([self isCreditCardRow:i]) {
+ creditCards_.erase(
+ creditCards_.begin() + [self creditCardIndexFromRow:i]);
+ }
+ }
+
+ // Select the previous row if possible, else current row, else deselect all.
+ if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1]
+ byExtendingSelection:NO];
+ } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow]
+ byExtendingSelection:NO];
+ } else {
+ [tableView_ deselectAll:self];
+ }
+
+ UpdateProfileLabels(&profiles_);
+ [tableView_ reloadData];
+}
+
+// Edits the selected item, either address or credit card depending on the item
+// selected.
+- (IBAction)editSelection:(id)sender {
+ NSInteger selectedRow = [tableView_ selectedRow];
+ if ([self isProfileRow:selectedRow]) {
+ if (!addressSheetController.get()) {
+ int i = [self profileIndexFromRow:selectedRow];
+
+ // Create a new address sheet controller in "Edit" mode.
+ addressSheetController.reset(
+ [[AutoFillAddressSheetController alloc]
+ initWithProfile:profiles_[i]
+ mode:kAutoFillAddressEditMode]);
+
+ // Show the sheet.
+ [NSApp beginSheet:[addressSheetController window]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(addressEditDidEnd:returnCode:contextInfo:)
+ contextInfo:&profiles_[i]];
+ }
+ } else if ([self isCreditCardRow:selectedRow]) {
+ if (!creditCardSheetController.get()) {
+ int i = [self creditCardIndexFromRow:selectedRow];
+
+ // Create a new credit card sheet controller in "Edit" mode.
+ creditCardSheetController.reset(
+ [[AutoFillCreditCardSheetController alloc]
+ initWithCreditCard:creditCards_[i]
+ mode:kAutoFillCreditCardEditMode
+ controller:self]);
+
+ // Show the sheet.
+ [NSApp beginSheet:[creditCardSheetController window]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(creditCardEditDidEnd:returnCode:contextInfo:)
+ contextInfo:&creditCards_[i]];
+ }
+ }
+}
+
+// Navigates to the AutoFill help url.
+- (IBAction)openHelp:(id)sender {
+ Browser* browser = BrowserList::GetLastActive();
+
+ if (!browser || !browser->GetSelectedTabContents())
+ browser = Browser::Create(profile_);
+ browser->OpenAutoFillHelpTabAndActivate();
+}
+
+// Edit address sheet was dismissed. Non-zero |returnCode| indicates a save.
+- (void)addressEditDidEnd:(NSWindow *)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void *)contextInfo {
+ DCHECK(contextInfo != NULL);
+ if (returnCode) {
+ AutoFillProfile* profile = static_cast<AutoFillProfile*>(contextInfo);
+ [addressSheetController copyModelToProfile:profile];
+
+ if (profile->IsEmpty())
+ [tableView_ deselectAll:self];
+ profiles_.erase(
+ std::remove_if(profiles_.begin(), profiles_.end(),
+ std::mem_fun_ref(&AutoFillProfile::IsEmpty)),
+ profiles_.end());
+
+ UpdateProfileLabels(&profiles_);
+ [tableView_ reloadData];
+ }
+ [sheet orderOut:self];
+ addressSheetController.reset(nil);
+}
+
+// Edit credit card sheet was dismissed. Non-zero |returnCode| indicates a
+// save.
+- (void)creditCardEditDidEnd:(NSWindow *)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void *)contextInfo {
+ DCHECK(contextInfo != NULL);
+ if (returnCode) {
+ CreditCard* creditCard = static_cast<CreditCard*>(contextInfo);
+ [creditCardSheetController copyModelToCreditCard:creditCard];
+
+ if (creditCard->IsEmpty())
+ [tableView_ deselectAll:self];
+ creditCards_.erase(
+ std::remove_if(
+ creditCards_.begin(), creditCards_.end(),
+ std::mem_fun_ref(&CreditCard::IsEmpty)),
+ creditCards_.end());
+ [tableView_ reloadData];
+ }
+ [sheet orderOut:self];
+ creditCardSheetController.reset(nil);
+}
+
+// NSTableView Delegate method.
+- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row {
+ if ([self isProfileGroupRow:row] || [self isCreditCardGroupRow:row])
+ return YES;
+ return NO;
+}
+
+// NSTableView Delegate method.
+- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row {
+ return ![self tableView:tableView isGroupRow:row];
+}
+
+// NSTableView Delegate method.
+- (id)tableView:(NSTableView *)tableView
+ objectValueForTableColumn:(NSTableColumn *)tableColumn
+ row:(NSInteger)row {
+ if ([[tableColumn identifier] isEqualToString:@"Spacer"])
+ return @"";
+
+ // Check that we're initialized before supplying data.
+ if (tableView != tableView_)
+ return @"";
+
+ // Section label.
+ if ([self isProfileGroupRow:row]) {
+ if ([[tableColumn identifier] isEqualToString:@"Summary"])
+ return l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES_GROUP_NAME);
+ else
+ return @"";
+ }
+
+ if (row < 0)
+ return @"";
+
+ // Data row.
+ if ([self isProfileRow:row]) {
+ if ([[tableColumn identifier] isEqualToString:@"Summary"]) {
+ return SysUTF16ToNSString(
+ profiles_[[self profileIndexFromRow:row]].Label());
+ }
+
+ return @"";
+ }
+
+ // Section label.
+ if ([self isCreditCardGroupRow:row]) {
+ if ([[tableColumn identifier] isEqualToString:@"Summary"])
+ return l10n_util::GetNSString(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME);
+ else
+ return @"";
+ }
+
+ // Data row.
+ if ([self isCreditCardRow:row]) {
+ if ([[tableColumn identifier] isEqualToString:@"Summary"]) {
+ return SysUTF16ToNSString(
+ creditCards_[
+ [self creditCardIndexFromRow:row]].PreviewSummary());
+ }
+
+ return @"";
+ }
+
+ return @"";
+}
+
+// We implement this delegate method to update our |itemIsSelected| and
+// |multipleSelected| properties.
+// The "Edit..." and "Remove" buttons' enabled state depends on having a
+// valid selection in the table. The "Edit..." button depends on having
+// exactly one item selected.
+- (void)tableViewSelectionDidChange:(NSNotification *)aNotification {
+ if ([tableView_ selectedRow] >= 0)
+ [self setItemIsSelected:YES];
+ else
+ [self setItemIsSelected:NO];
+
+ [self setMultipleSelected:([[tableView_ selectedRowIndexes] count] > 1UL)];
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
+ if (tableView == tableView_) {
+ // 1 section header, the profiles, 1 section header, the credit cards.
+ return 1 + profiles_.size() + 1 + creditCards_.size();
+ }
+
+ return 0;
+}
+
+- (NSArray*)addressLabels {
+ NSUInteger capacity = profiles_.size();
+ NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity];
+
+ std::vector<AutoFillProfile>::iterator i;
+ for (i = profiles_.begin(); i != profiles_.end(); ++i) {
+ [array addObject:SysUTF16ToNSString(i->Label())];
+ }
+
+ return array;
+}
+
+@end
+
+@implementation AutoFillDialogController (ExposedForUnitTests)
+
++ (AutoFillDialogController*)controllerWithObserver:
+ (AutoFillDialogObserver*)observer
+ profile:(Profile*)profile
+ importedProfile:(AutoFillProfile*)importedProfile
+ importedCreditCard:(CreditCard*)importedCreditCard {
+
+ // Deallocation is done upon window close. See |windowWillClose:|.
+ AutoFillDialogController* controller =
+ [[self alloc] initWithObserver:observer
+ profile:profile
+ importedProfile:importedProfile
+ importedCreditCard:importedCreditCard];
+ return controller;
+}
+
+
+// This is the designated initializer for this class.
+// |profiles| are non-retained immutable list of autofill profiles.
+// |creditCards| are non-retained immutable list of credit card info.
+- (id)initWithObserver:(AutoFillDialogObserver*)observer
+ profile:(Profile*)profile
+ importedProfile:(AutoFillProfile*)importedProfile
+ importedCreditCard:(CreditCard*)importedCreditCard {
+ DCHECK(profile);
+ // Use initWithWindowNibPath: instead of initWithWindowNibName: so we
+ // can override it in a unit test.
+ NSString* nibpath = [mac_util::MainAppBundle()
+ pathForResource:@"AutoFillDialog"
+ ofType:@"nib"];
+ if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
+ // Initialize member variables based on input.
+ observer_ = observer;
+ profile_ = profile;
+ importedProfile_ = importedProfile;
+ importedCreditCard_ = importedCreditCard;
+
+ // Use property here to trigger KVO binding.
+ [self setAutoFillEnabled:profile_->GetPrefs()->GetBoolean(
+ prefs::kAutoFillEnabled)];
+
+ // Use property here to trigger KVO binding.
+ [self setAuxiliaryEnabled:profile_->GetPrefs()->GetBoolean(
+ prefs::kAutoFillAuxiliaryProfilesEnabled)];
+
+ // Do not use [NSMutableArray array] here; we need predictable destruction
+ // which will be prevented by having a reference held by an autorelease
+ // pool.
+ }
+ return self;
+}
+
+// Close the dialog.
+- (void)closeDialog {
+ [[self window] close];
+ [NSApp stopModal];
+}
+
+- (AutoFillAddressSheetController*)addressSheetController {
+ return addressSheetController.get();
+}
+
+- (AutoFillCreditCardSheetController*)creditCardSheetController {
+ return creditCardSheetController.get();
+}
+
+- (void)selectAddressAtIndex:(size_t)i {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
+ [self rowFromProfileIndex:i]]
+ byExtendingSelection:NO];
+}
+
+- (void)selectCreditCardAtIndex:(size_t)i {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
+ [self rowFromCreditCardIndex:i]]
+ byExtendingSelection:NO];
+}
+
+- (void)addSelectedAddressAtIndex:(size_t)i {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
+ [self rowFromProfileIndex:i]]
+ byExtendingSelection:YES];
+}
+
+- (void)addSelectedCreditCardAtIndex:(size_t)i {
+ [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
+ [self rowFromCreditCardIndex:i]]
+ byExtendingSelection:YES];
+}
+
+- (BOOL)editButtonEnabled {
+ return [editButton_ isEnabled];
+}
+
+@end
+
+@implementation AutoFillDialogController (PrivateMethods)
+
+// Run application modal.
+- (void)runModalDialog {
+ // Use stored window geometry if it exists.
+ if (g_browser_process && g_browser_process->local_state()) {
+ sizeSaver_.reset([[WindowSizeAutosaver alloc]
+ initWithWindow:[self window]
+ prefService:g_browser_process->local_state()
+ path:prefs::kAutoFillDialogPlacement
+ state:kSaveWindowPos]);
+ }
+
+ [NSApp runModalForWindow:[self window]];
+}
+
+- (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards {
+ if (importedProfile_) {
+ profiles_.push_back(*importedProfile_);
+ }
+
+ if (importedCreditCard_) {
+ creditCards_.push_back(*importedCreditCard_);
+ }
+
+ // If we're not using imported data then use the data fetch from the web db.
+ if (!importedProfile_ && !importedCreditCard_) {
+ // Make local copy of |profiles|.
+ for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin();
+ iter != profiles.end(); ++iter)
+ profiles_.push_back(**iter);
+
+ // Make local copy of |creditCards|.
+ for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin();
+ iter != creditCards.end(); ++iter)
+ creditCards_.push_back(**iter);
+ }
+
+ UpdateProfileLabels(&profiles_);
+}
+
+- (BOOL)isProfileRow:(NSInteger)row {
+ if (row > 0 && static_cast<size_t>(row) <= profiles_.size())
+ return YES;
+ return NO;
+}
+
+- (BOOL)isProfileGroupRow:(NSInteger)row {
+ if (row == 0)
+ return YES;
+ return NO;
+}
+
+- (BOOL)isCreditCardRow:(NSInteger)row {
+ if (row > 0 &&
+ static_cast<size_t>(row) >= profiles_.size() + 2 &&
+ static_cast<size_t>(row) <= profiles_.size() + creditCards_.size() + 1)
+ return YES;
+ return NO;
+}
+
+- (BOOL)isCreditCardGroupRow:(NSInteger)row {
+ if (row > 0 && static_cast<size_t>(row) == profiles_.size() + 1)
+ return YES;
+ return NO;
+}
+
+- (size_t)profileIndexFromRow:(NSInteger)row {
+ DCHECK([self isProfileRow:row]);
+ return static_cast<size_t>(row) - 1;
+}
+
+- (size_t)creditCardIndexFromRow:(NSInteger)row {
+ DCHECK([self isCreditCardRow:row]);
+ return static_cast<size_t>(row) - (profiles_.size() + 2);
+}
+
+- (NSInteger)rowFromProfileIndex:(size_t)i {
+ return 1 + i;
+}
+
+- (NSInteger)rowFromCreditCardIndex:(size_t)i {
+ return 1 + profiles_.size() + 1 + i;
+}
+
+@end
+
+// An NSValueTransformer subclass for use in validation of phone number
+// fields. Transforms an invalid phone number string into a warning image.
+// This data transformer is used in the credit card sheet for invalid phone and
+// fax numbers.
+@interface InvalidPhoneTransformer : NSValueTransformer {
+}
+@end
+
+@implementation InvalidPhoneTransformer
++ (Class)transformedValueClass {
+ return [NSImage class];
+}
+
++ (BOOL)allowsReverseTransformation {
+ return NO;
+}
+
+- (id)transformedValue:(id)string {
+ NSImage* image = nil;
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ // We display no validation icon when input has not yet been entered.
+ if (string == nil || [string length] == 0)
+ return nil;
+
+ // If we have input then display alert icon if we have an invalid number.
+ if (string != nil && [string length] != 0) {
+ // TODO(dhollowa): Using SetInfo() call to validate phone number. Should
+ // have explicit validation method. More robust validation is needed as
+ // well eventually.
+ AutoFillProfile profile(string16(), 0);
+ profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER),
+ base::SysNSStringToUTF16(string));
+ if (profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)).empty()) {
+ image = rb.GetNSImageNamed(IDR_INPUT_ALERT);
+ DCHECK(image);
+ return image;
+ }
+ }
+
+ // No alert icon, so must be valid input.
+ if (!image) {
+ image = rb.GetNSImageNamed(IDR_INPUT_GOOD);
+ DCHECK(image);
+ return image;
+ }
+
+ return nil;
+}
+
+@end