summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/autofill/autofill_address_model_mac.h77
-rw-r--r--chrome/browser/autofill/autofill_address_model_mac.mm181
-rw-r--r--chrome/browser/autofill/autofill_address_view_controller_mac.h37
-rw-r--r--chrome/browser/autofill/autofill_address_view_controller_mac.mm40
-rw-r--r--chrome/browser/autofill/autofill_credit_card_model_mac.h58
-rw-r--r--chrome/browser/autofill/autofill_credit_card_model_mac.mm86
-rw-r--r--chrome/browser/autofill/autofill_credit_card_view_controller_mac.h41
-rw-r--r--chrome/browser/autofill/autofill_credit_card_view_controller_mac.mm51
-rw-r--r--chrome/browser/autofill/autofill_dialog_controller_mac.h82
-rw-r--r--chrome/browser/autofill/autofill_dialog_controller_mac.mm162
-rw-r--r--chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm205
-rw-r--r--chrome/browser/autofill/autofill_dialog_mac.mm (renamed from chrome/browser/autofill/autofill_dialog.cc)12
-rw-r--r--chrome/browser/autofill/autofill_profile.cc4
-rw-r--r--chrome/browser/autofill/autofill_profile.h1
-rw-r--r--chrome/browser/autofill/credit_card.cc5
-rw-r--r--chrome/browser/autofill/credit_card.h1
-rw-r--r--chrome/browser/cocoa/disclosure_view_controller.h30
-rw-r--r--chrome/browser/cocoa/disclosure_view_controller.mm189
-rw-r--r--chrome/browser/cocoa/preferences_window_controller.h1
-rw-r--r--chrome/browser/cocoa/preferences_window_controller.mm25
-rw-r--r--chrome/browser/cocoa/section_separator_view.h32
-rw-r--r--chrome/browser/cocoa/section_separator_view.mm106
-rw-r--r--chrome/browser/cocoa/vertical_layout_view.h22
-rw-r--r--chrome/browser/cocoa/vertical_layout_view.mm73
24 files changed, 1516 insertions, 5 deletions
diff --git a/chrome/browser/autofill/autofill_address_model_mac.h b/chrome/browser/autofill/autofill_address_model_mac.h
new file mode 100644
index 0000000..92d02ae
--- /dev/null
+++ b/chrome/browser/autofill/autofill_address_model_mac.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_MODEL_MAC_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_MODEL_MAC_
+
+#import <Cocoa/Cocoa.h>
+
+class AutoFillProfile;
+
+// A "model" class used with bindings mechanism and the
+// |AutoFillAddressViewController| to achieve the form-like view
+// of autofill data in the Chrome options UI.
+// Note that |summary| is a derived property.
+// Model objects are initialized from a given profile using the designated
+// initializer |initWithProfile:|.
+// Users of this class must be prepared to handle nil string return values.
+// The KVO/bindings mechanisms expect this and deal with nil string values
+// appropriately.
+@interface AutoFillAddressModel : NSObject {
+ @private
+ // These are not scoped_nsobjects because we use them via KVO/bindings.
+ NSString* label_;
+ NSString* firstName_;
+ NSString* middleName_;
+ NSString* lastName_;
+ NSString* email_;
+ NSString* companyName_;
+ NSString* addressLine1_;
+ NSString* addressLine2_;
+ NSString* city_;
+ NSString* state_;
+ NSString* zip_;
+ NSString* country_;
+ NSString* phoneCountryCode_;
+ NSString* phoneAreaCode_;
+ NSString* phoneNumber_;
+ NSString* faxCountryCode_;
+ NSString* faxAreaCode_;
+ NSString* faxNumber_;
+}
+
+// |summary| is a derived property based on |firstName|, |lastName| and
+// |addressLine1|. KVO observers receive change notifications for |summary|
+// when any of these properties change.
+@property (readonly) NSString* summary;
+@property (nonatomic, copy) NSString* label;
+@property (nonatomic, copy) NSString* firstName;
+@property (nonatomic, copy) NSString* middleName;
+@property (nonatomic, copy) NSString* lastName;
+@property (nonatomic, copy) NSString* email;
+@property (nonatomic, copy) NSString* companyName;
+@property (nonatomic, copy) NSString* addressLine1;
+@property (nonatomic, copy) NSString* addressLine2;
+@property (nonatomic, copy) NSString* city;
+@property (nonatomic, copy) NSString* state;
+@property (nonatomic, copy) NSString* zip;
+@property (nonatomic, copy) NSString* country;
+@property (nonatomic, copy) NSString* phoneCountryCode;
+@property (nonatomic, copy) NSString* phoneAreaCode;
+@property (nonatomic, copy) NSString* phoneNumber;
+@property (nonatomic, copy) NSString* faxCountryCode;
+@property (nonatomic, copy) NSString* faxAreaCode;
+@property (nonatomic, copy) NSString* faxNumber;
+
+// The designated initializer. Initializes the property strings to values
+// retrieved from the |profile|.
+- (id)initWithProfile:(const AutoFillProfile&)profile;
+
+// This method copies internal NSString property values into the
+// |profile| object's fields as appropriate. |profile| should never be NULL.
+- (void)copyModelToProfile:(AutoFillProfile*)profile;
+
+@end
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_MODEL_MAC_
diff --git a/chrome/browser/autofill/autofill_address_model_mac.mm b/chrome/browser/autofill/autofill_address_model_mac.mm
new file mode 100644
index 0000000..265b055
--- /dev/null
+++ b/chrome/browser/autofill/autofill_address_model_mac.mm
@@ -0,0 +1,181 @@
+// 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_address_model_mac.h"
+#include "app/l10n_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "grit/generated_resources.h"
+
+@implementation AutoFillAddressModel
+
+@dynamic summary;
+@synthesize label = label_;
+@synthesize firstName = firstName_;
+@synthesize middleName = middleName_;
+@synthesize lastName = lastName_;
+@synthesize email = email_;
+@synthesize companyName = companyName_;
+@synthesize addressLine1 = addressLine1_;
+@synthesize addressLine2 = addressLine2_;
+@synthesize city = city_;
+@synthesize state = state_;
+@synthesize zip = zip_;
+@synthesize country = country_;
+@synthesize phoneCountryCode = phoneCountryCode_;
+@synthesize phoneAreaCode = phoneAreaCode_;
+@synthesize phoneNumber = phoneNumber_;
+@synthesize faxCountryCode = faxCountryCode_;
+@synthesize faxAreaCode = faxAreaCode_;
+@synthesize faxNumber = faxNumber_;
+
+// Sets up the KVO dependency between "summary" and dependent fields.
++ (NSSet*)keyPathsForValuesAffectingValueForKey:(NSString*)key {
+ NSSet* keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
+
+ if ([key isEqualToString:@"summary"]) {
+ NSSet* affectingKeys =
+ [NSSet setWithObjects:@"firstName", @"lastName", @"addressLine1", nil];
+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
+ }
+ return keyPaths;
+}
+
+- (id)initWithProfile:(const AutoFillProfile&)profile {
+ if ((self = [super init])) {
+ [self setLabel:SysUTF16ToNSString(profile.Label())];
+ [self setFirstName:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(NAME_FIRST)))];
+ [self setMiddleName:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(NAME_MIDDLE)))];
+ [self setLastName:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(NAME_LAST)))];
+ [self setEmail:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(EMAIL_ADDRESS)))];
+ [self setCompanyName:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(COMPANY_NAME)))];
+ [self setAddressLine1:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)))];
+ [self setAddressLine2:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)))];
+ [self setCity:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY)))];
+ [self setState:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE)))];
+ [self setZip:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP)))];
+ [self setCountry:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY)))];
+ [self setPhoneCountryCode:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_HOME_COUNTRY_CODE)))];
+ [self setPhoneAreaCode:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_HOME_CITY_CODE)))];
+ [self setPhoneNumber:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_HOME_NUMBER)))];
+ [self setFaxCountryCode:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_FAX_COUNTRY_CODE)))];
+ [self setFaxAreaCode:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_FAX_CITY_CODE)))];
+ [self setFaxNumber:SysUTF16ToNSString(
+ profile.GetFieldText(AutoFillType(PHONE_FAX_NUMBER)))];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [label_ release];
+ [firstName_ release];
+ [middleName_ release];
+ [lastName_ release];
+ [email_ release];
+ [companyName_ release];
+ [addressLine1_ release];
+ [addressLine2_ release];
+ [city_ release];
+ [state_ release];
+ [zip_ release];
+ [country_ release];
+ [phoneCountryCode_ release];
+ [phoneAreaCode_ release];
+ [phoneNumber_ release];
+ [faxCountryCode_ release];
+ [faxAreaCode_ release];
+ [faxNumber_ release];
+ [super dealloc];
+}
+
+- (NSString*)summary {
+ // Bindings may set these to nil. We normalize here to @"".
+ if (firstName_ == nil)
+ firstName_ = @"";
+ if (lastName_ == nil)
+ lastName_ = @"";
+ if (addressLine1_ == nil)
+ addressLine1_ = @"";
+
+ BOOL haveFirstName = [firstName_ length] > 0;
+ BOOL haveLastName = [lastName_ length] > 0;
+ BOOL haveAddress = [addressLine1_ length] > 0;
+
+ NSString* nameSeparator = (haveFirstName && haveLastName) ?
+ l10n_util::GetNSString(IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR) :
+ @"";
+ NSString* nameFormat =
+ l10n_util::GetNSStringF(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT,
+ base::SysNSStringToUTF16(firstName_),
+ base::SysNSStringToUTF16(nameSeparator),
+ base::SysNSStringToUTF16(lastName_));
+ NSString* summarySeparator = (haveFirstName || haveLastName) && haveAddress ?
+ l10n_util::GetNSString(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR) :
+ @"";
+ NSString* summaryFormat =
+ l10n_util::GetNSStringF(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT,
+ base::SysNSStringToUTF16(nameFormat),
+ base::SysNSStringToUTF16(summarySeparator),
+ base::SysNSStringToUTF16(addressLine1_));
+
+ return summaryFormat;
+}
+
+- (void)copyModelToProfile:(AutoFillProfile*)profile {
+ DCHECK(profile);
+ profile->set_label(base::SysNSStringToUTF16([self label]));
+
+ profile->SetInfo(AutoFillType(NAME_FIRST),
+ base::SysNSStringToUTF16([self firstName]));
+ profile->SetInfo(AutoFillType(NAME_MIDDLE),
+ base::SysNSStringToUTF16([self middleName]));
+ profile->SetInfo(AutoFillType(NAME_LAST),
+ base::SysNSStringToUTF16([self lastName]));
+ profile->SetInfo(AutoFillType(EMAIL_ADDRESS),
+ base::SysNSStringToUTF16([self email]));
+ profile->SetInfo(AutoFillType(COMPANY_NAME),
+ base::SysNSStringToUTF16([self companyName]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1),
+ base::SysNSStringToUTF16([self addressLine1]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2),
+ base::SysNSStringToUTF16([self addressLine2]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY),
+ base::SysNSStringToUTF16([self city]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE),
+ base::SysNSStringToUTF16([self state]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP),
+ base::SysNSStringToUTF16([self zip]));
+ profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY),
+ base::SysNSStringToUTF16([self country]));
+ profile->SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE),
+ base::SysNSStringToUTF16([self phoneCountryCode]));
+ profile->SetInfo(AutoFillType(PHONE_HOME_CITY_CODE),
+ base::SysNSStringToUTF16([self phoneAreaCode]));
+ profile->SetInfo(AutoFillType(PHONE_HOME_NUMBER),
+ base::SysNSStringToUTF16([self phoneNumber]));
+ profile->SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE),
+ base::SysNSStringToUTF16([self faxCountryCode]));
+ profile->SetInfo(AutoFillType(PHONE_FAX_CITY_CODE),
+ base::SysNSStringToUTF16([self faxAreaCode]));
+ profile->SetInfo(AutoFillType(PHONE_FAX_NUMBER),
+ base::SysNSStringToUTF16([self faxNumber]));
+}
+
+@end
diff --git a/chrome/browser/autofill/autofill_address_view_controller_mac.h b/chrome/browser/autofill/autofill_address_view_controller_mac.h
new file mode 100644
index 0000000..40448d5
--- /dev/null
+++ b/chrome/browser/autofill/autofill_address_view_controller_mac.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_VIEW_CONTROLLER_MAC_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_VIEW_CONTROLLER_MAC_
+
+#import <Cocoa/Cocoa.h>
+#import "chrome/browser/cocoa/disclosure_view_controller.h"
+
+@class AutoFillAddressModel;
+class AutoFillProfile;
+
+// A class that coordinates the |addressModel| and the associated view
+// held in AutoFillAddressFormView.xib.
+// |initWithProfile:| is the designated initializer. It takes |profile|
+// and transcribes it to |addressModel| to which the view is bound.
+@interface AutoFillAddressViewController : DisclosureViewController {
+ @private
+ // The primary model for this controller. The model is instantiated
+ // from within |initWithProfile:|. We do not hold it as a scoped_nsobject
+ // because it is exposed as a KVO compliant property.
+ AutoFillAddressModel* addressModel_; // strong reference
+}
+
+@property (nonatomic, retain) AutoFillAddressModel* addressModel;
+
+// Designated initializer. Takes a copy of the data in |profile|,
+// it is not held as a reference.
+- (id)initWithProfile:(const AutoFillProfile&)profile;
+
+// Copy data from internal model to |profile|.
+- (void)copyModelToProfile:(AutoFillProfile*)profile;
+
+@end
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_ADDRESS_VIEW_CONTROLLER_MAC_
diff --git a/chrome/browser/autofill/autofill_address_view_controller_mac.mm b/chrome/browser/autofill/autofill_address_view_controller_mac.mm
new file mode 100644
index 0000000..2862ad9
--- /dev/null
+++ b/chrome/browser/autofill/autofill_address_view_controller_mac.mm
@@ -0,0 +1,40 @@
+// 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_address_view_controller_mac.h"
+#include "base/mac_util.h"
+#include "base/sys_string_conversions.h"
+#import "chrome/browser/autofill/autofill_address_model_mac.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+
+@implementation AutoFillAddressViewController
+
+@synthesize addressModel = addressModel_;
+
+- (id)initWithProfile:(const AutoFillProfile&)profile {
+ self = [super initWithNibName:@"AutoFillAddressFormView"
+ bundle:mac_util::MainAppBundle()];
+ if (self) {
+ // Pull in the view for initialization.
+ [self view];
+
+ // Create the model.
+ [self setAddressModel:[[[AutoFillAddressModel alloc]
+ initWithProfile:profile] autorelease]];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [addressModel_ release];
+ [super dealloc];
+}
+
+- (void)copyModelToProfile:(AutoFillProfile*)profile {
+ [addressModel_ copyModelToProfile:profile];
+}
+
+@end
+
+
diff --git a/chrome/browser/autofill/autofill_credit_card_model_mac.h b/chrome/browser/autofill/autofill_credit_card_model_mac.h
new file mode 100644
index 0000000..97dfb67
--- /dev/null
+++ b/chrome/browser/autofill/autofill_credit_card_model_mac.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_MODEL_MAC_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_MODEL_MAC_
+
+#import <Cocoa/Cocoa.h>
+
+class CreditCard;
+
+// A "model" class used with bindings mechanism and the
+// |AutoFillCreditCardViewController| to achieve the form-like view
+// of autofill data in the Chrome options UI.
+// Note that |summary| is a derived property.
+// Model objects are initialized from the given |creditCard| using the
+// designated initializer |initWithCreditCard:|.
+// Users of this class must be prepared to handle nil string return values.
+// The KVO/bindings mechanisms expect this and deal with nil string values
+// appropriately.
+@interface AutoFillCreditCardModel : NSObject {
+ @private
+ // These are not scoped_nsobjects because we use them via KVO/bindings.
+ NSString* label_;
+ NSString* nameOnCard_;
+ NSString* creditCardNumber_;
+ NSString* expirationMonth_;
+ NSString* expirationYear_;
+ NSString* cvcCode_;
+ NSString* billingAddress_;
+ NSString* shippingAddress_;
+}
+
+// |summary| is a derived property based on |creditCardNumber|,
+// |expirationMonth| and |expirationYear|. KVO observers receive change
+// notifications for |summary| when any of these properties change.
+@property (readonly) NSString* summary;
+@property (nonatomic, copy) NSString* label;
+@property (nonatomic, copy) NSString* nameOnCard;
+@property (nonatomic, copy) NSString* creditCardNumber;
+@property (nonatomic, copy) NSString* expirationMonth;
+@property (nonatomic, copy) NSString* expirationYear;
+@property (nonatomic, copy) NSString* cvcCode;
+@property (nonatomic, copy) NSString* billingAddress;
+@property (nonatomic, copy) NSString* shippingAddress;
+
+// Designated initializer. Initializes the property strings to values retrieved
+// from the |creditCard| object.
+- (id)initWithCreditCard:(const CreditCard&)creditCard;
+
+// This method copies internal NSString property values into the
+// |creditCard| object's fields as appropriate. |creditCard| should never
+// be NULL.
+- (void)copyModelToCreditCard:(CreditCard*)creditCard;
+
+@end
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_MODEL_MAC_
diff --git a/chrome/browser/autofill/autofill_credit_card_model_mac.mm b/chrome/browser/autofill/autofill_credit_card_model_mac.mm
new file mode 100644
index 0000000..f59cff9
--- /dev/null
+++ b/chrome/browser/autofill/autofill_credit_card_model_mac.mm
@@ -0,0 +1,86 @@
+// 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_credit_card_model_mac.h"
+#include "app/l10n_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/autofill/credit_card.h"
+#include "grit/generated_resources.h"
+
+
+@implementation AutoFillCreditCardModel
+
+@dynamic summary;
+@synthesize label = label_;
+@synthesize nameOnCard = nameOnCard_;
+@synthesize creditCardNumber = creditCardNumber_;
+@synthesize expirationMonth = expirationMonth_;
+@synthesize expirationYear = expirationYear_;
+@synthesize cvcCode = cvcCode_;
+@synthesize billingAddress = billingAddress_;
+@synthesize shippingAddress = shippingAddress_;
+
+// Sets up the KVO dependency between "summary" and dependent fields.
++ (NSSet*)keyPathsForValuesAffectingValueForKey:(NSString*)key {
+ NSSet* keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
+
+ if ([key isEqualToString:@"summary"]) {
+ NSSet* affectingKeys = [NSSet setWithObjects:@"creditCardNumber",
+ @"expirationMonth", @"expirationYear", nil];
+ keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
+ }
+ return keyPaths;
+}
+
+- (id)initWithCreditCard:(const CreditCard&)creditCard {
+ if ((self = [super init])) {
+ [self setLabel:SysUTF16ToNSString(creditCard.Label())];
+ [self setNameOnCard:SysUTF16ToNSString(
+ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_NAME)))];
+ [self setCreditCardNumber:SysUTF16ToNSString(
+ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)))];
+ [self setExpirationMonth:SysUTF16ToNSString(
+ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)))];
+ [self setExpirationYear:SysUTF16ToNSString(
+ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)))];
+ [self setCvcCode:SysUTF16ToNSString(
+ creditCard.GetFieldText(AutoFillType(CREDIT_CARD_VERIFICATION_CODE)))];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [label_ release];
+ [nameOnCard_ release];
+ [creditCardNumber_ release];
+ [expirationMonth_ release];
+ [expirationYear_ release];
+ [cvcCode_ release];
+ [billingAddress_ release];
+ [shippingAddress_ release];
+ [super dealloc];
+}
+
+- (NSString*)summary {
+ // TODO(dhollowa): This has been pulled into cross platform code.
+ // Will hook up in separate CL. See http://crbug.com/33029.
+ return @"";
+}
+
+- (void)copyModelToCreditCard:(CreditCard*)creditCard {
+ DCHECK(creditCard);
+ creditCard->set_label(base::SysNSStringToUTF16([self label]));
+ creditCard->SetInfo(AutoFillType(CREDIT_CARD_NAME),
+ base::SysNSStringToUTF16([self nameOnCard]));
+ creditCard->SetInfo(AutoFillType(CREDIT_CARD_NUMBER),
+ base::SysNSStringToUTF16([self creditCardNumber]));
+ creditCard->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH),
+ base::SysNSStringToUTF16([self expirationMonth]));
+ creditCard->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR),
+ base::SysNSStringToUTF16([self expirationYear]));
+ creditCard->SetInfo(AutoFillType(CREDIT_CARD_VERIFICATION_CODE),
+ base::SysNSStringToUTF16([self cvcCode]));
+}
+
+@end
diff --git a/chrome/browser/autofill/autofill_credit_card_view_controller_mac.h b/chrome/browser/autofill/autofill_credit_card_view_controller_mac.h
new file mode 100644
index 0000000..05320c5
--- /dev/null
+++ b/chrome/browser/autofill/autofill_credit_card_view_controller_mac.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_VIEW_CONTROLLER_MAC_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_VIEW_CONTROLLER_MAC_
+
+#import <Cocoa/Cocoa.h>
+#import "chrome/browser/cocoa/disclosure_view_controller.h"
+
+@class AutoFillCreditCardModel;
+class CreditCard;
+
+// A class that coordinates the |creditCardModel| and the associated view
+// held in AutoFillCreditCardFormView.xib.
+// |initWithCreditCard:| is the designated initializer. It takes |creditCard|
+// and transcribes it to |creditCardModel| to which the view is bound.
+@interface AutoFillCreditCardViewController : DisclosureViewController {
+ @private
+ // TODO(dhollowa): temporary to disable until implementend.
+ // See http://crbug.com/33029.
+ IBOutlet NSTextField* billingAddressLabel_;
+ IBOutlet NSPopUpButton* billingAddressPopup_;
+ IBOutlet NSTextField* shippingAddressLabel_;
+ IBOutlet NSPopUpButton* shippingAddressPopup_;
+
+ // The primary model for this controller. The model is instantiated
+ // from within |initWithCreditCard:|. We do not hold it as a scoped_nsobject
+ // because it is exposed as a KVO compliant property.
+ AutoFillCreditCardModel* creditCardModel_;
+}
+
+@property (nonatomic, retain) AutoFillCreditCardModel* creditCardModel;
+
+// Designated initializer. Takes a copy of the data in |creditCard|,
+// it is not held as a reference.
+- (id)initWithCreditCard:(const CreditCard&)creditCard;
+
+@end
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_CREDIT_CARD_VIEW_CONTROLLER_MAC_
diff --git a/chrome/browser/autofill/autofill_credit_card_view_controller_mac.mm b/chrome/browser/autofill/autofill_credit_card_view_controller_mac.mm
new file mode 100644
index 0000000..36ea721
--- /dev/null
+++ b/chrome/browser/autofill/autofill_credit_card_view_controller_mac.mm
@@ -0,0 +1,51 @@
+// 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_credit_card_view_controller_mac.h"
+#include "base/mac_util.h"
+#include "base/sys_string_conversions.h"
+#import "chrome/browser/autofill/autofill_credit_card_model_mac.h"
+#include "chrome/browser/autofill/credit_card.h"
+
+@implementation AutoFillCreditCardViewController
+
+@synthesize creditCardModel = creditCardModel_;
+
+- (id)initWithCreditCard:(const CreditCard&)creditCard {
+ self = [super initWithNibName:@"AutoFillCreditCardFormView"
+ bundle:mac_util::MainAppBundle()];
+ if (self) {
+ // Pull in the view for initialization.
+ [self view];
+
+ // Create the model.
+ [self setCreditCardModel:[[[AutoFillCreditCardModel alloc]
+ initWithCreditCard:creditCard] autorelease]];
+
+ // Setup initial state.
+ // TODO(dhollowa): not yet implemented, disabling controls for now.
+ // See http://crbug.com/33029.
+ [billingAddressLabel_ setEnabled:FALSE];
+ [billingAddressLabel_ setTextColor:[NSColor secondarySelectedControlColor]];
+ [billingAddressPopup_ removeAllItems];
+ [billingAddressPopup_ setEnabled:FALSE];
+ [shippingAddressLabel_ setEnabled:FALSE];
+ [shippingAddressLabel_ setTextColor:
+ [NSColor secondarySelectedControlColor]];
+ [shippingAddressPopup_ removeAllItems];
+ [shippingAddressPopup_ setEnabled:FALSE];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [creditCardModel_ release];
+ [super dealloc];
+}
+
+- (void)copyModelToCreditCard:(CreditCard*)creditCard {
+ [creditCardModel_ copyModelToCreditCard:creditCard];
+}
+
+@end
diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac.h b/chrome/browser/autofill/autofill_dialog_controller_mac.h
new file mode 100644
index 0000000..6e1d8e3
--- /dev/null
+++ b/chrome/browser/autofill/autofill_dialog_controller_mac.h
@@ -0,0 +1,82 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_DIALOG_CONTROLLER_MAC_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_DIALOG_CONTROLLER_MAC_
+
+#import <Cocoa/Cocoa.h>
+#include <vector>
+#include "base/scoped_nsobject.h"
+#include "chrome/browser/autofill/autofill_dialog.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/autofill/credit_card.h"
+
+@class AutoFillAddressViewController;
+@class AutoFillCreditCardViewController;
+@class SectionSeparatorView;
+
+// A window controller for managing the autofill options dialog.
+// Application modally presents a dialog allowing the user to store
+// personal address and credit card information.
+@interface AutoFillDialogController : NSWindowController {
+ @private
+ IBOutlet NSView* childView_;
+ IBOutlet NSView* addressSection_;
+ IBOutlet SectionSeparatorView* addressSectionBox_;
+ IBOutlet NSView* creditCardSection_;
+
+ // TODO(dhollowa): one each of these for now. Will be n of each
+ // controller eventually, for n addresses and n credit cards.
+ // Note on ownership: the controllers are strongly owned by the dialog
+ // controller. Their views are inserted into the dialog's view hierarcy
+ // but are retained by these controllers as well.
+ // See http://crbug.com/33029.
+ scoped_nsobject<AutoFillAddressViewController>
+ addressFormViewController_;
+ scoped_nsobject<AutoFillCreditCardViewController>
+ creditCardFormViewController_;
+
+ AutoFillDialogObserver* observer_; // (weak) not retained
+ std::vector<AutoFillProfile> profiles_;
+ std::vector<CreditCard> creditCards_;
+}
+
+// Main interface for displaying an application modal autofill dialog on screen.
+// This class method creates a new |AutoFillDialogController| and runs it as a
+// modal dialog. The controller autoreleases itself when the dialog is closed.
+// |observer| can be NULL, but if it is, then no notification is sent during
+// call to |save|. If |observer| is non-NULL then its |OnAutoFillDialogApply|
+// method is invoked during |save| with the new address and credit card
+// information.
+// |profiles| and |creditCards| must have non-NULL entries (zero or more).
+// These provide the initial data that is presented to the user.
++ (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards;
+
+// IBActions for the dialog buttons.
+- (IBAction)save:(id)sender;
+- (IBAction)cancel:(id)sender;
+
+@end
+
+// Interface exposed for unit testing.
+@interface AutoFillDialogController (ExposedForUnitTests)
+// Returns an instance of AutoFillDialogController. See |-initWithObserver|
+// for details about arguments.
+// Note: controller is autoreleased when |-closeDialog| is called.
++ (AutoFillDialogController*)controllerWithObserver:
+ (AutoFillDialogObserver*)observer
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards;
+
+- (id)initWithObserver:(AutoFillDialogObserver*)observer
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards;
+- (AutoFillAddressViewController*)addressFormViewController;
+- (AutoFillCreditCardViewController*)creditCardFormViewController;
+- (void)closeDialog;
+@end
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_DIALOG_CONTROLLER_MAC_
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..4288296
--- /dev/null
+++ b/chrome/browser/autofill/autofill_dialog_controller_mac.mm
@@ -0,0 +1,162 @@
+// 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 "base/mac_util.h"
+#import "chrome/browser/autofill/autofill_address_model_mac.h"
+#import "chrome/browser/autofill/autofill_address_view_controller_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_model_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_view_controller_mac.h"
+#import "chrome/browser/cocoa/disclosure_view_controller.h"
+#import "chrome/browser/cocoa/section_separator_view.h"
+#include "chrome/browser/profile.h"
+
+@interface AutoFillDialogController (PrivateMethods)
+- (void)runModalDialog;
+- (void)installChildViews;
+@end
+
+@implementation AutoFillDialogController
+
++ (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards {
+ AutoFillDialogController* controller =
+ [AutoFillDialogController controllerWithObserver:observer
+ autoFillProfiles:profiles
+ creditCards:creditCards];
+
+ // Only run modal dialog if it is not already being shown.
+ if (![controller isWindowLoaded]) {
+ [controller runModalDialog];
+ }
+}
+
+- (void)awakeFromNib {
+ [addressSectionBox_ setShowTopLine:FALSE];
+ [self installChildViews];
+}
+
+// NSWindow Delegate callback. When the window closes the controller can
+// be released.
+- (void)windowWillClose:(NSNotification *)notification {
+ [self autorelease];
+}
+
+
+// Called when the user clicks the save button.
+- (IBAction)save:(id)sender {
+ if (observer_) {
+ [addressFormViewController_ copyModelToProfile:&profiles_[0]];
+ [creditCardFormViewController_ copyModelToCreditCard:&creditCards_[0]];
+ 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];
+}
+
+@end
+
+@implementation AutoFillDialogController (ExposedForUnitTests)
+
++ (AutoFillDialogController*)controllerWithObserver:
+ (AutoFillDialogObserver*)observer
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards {
+
+ // Deallocation is done upon window close. See |windowWillClose:|.
+ AutoFillDialogController* controller =
+ [[self alloc] initWithObserver:observer
+ autoFillProfiles:profiles
+ creditCards:creditCards];
+ 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
+ autoFillProfiles:(const std::vector<AutoFillProfile*>&)profiles
+ creditCards:(const std::vector<CreditCard*>&)creditCards {
+ // 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])) {
+ observer_ = observer;
+
+ // Make local copy of |profiles|.
+ std::vector<AutoFillProfile*>::const_iterator i;
+ for (i = profiles.begin(); i != profiles.end(); ++i)
+ profiles_.push_back(**i);
+
+ // Make local copy of |creditCards|.
+ std::vector<CreditCard*>::const_iterator j;
+ for (j = creditCards.begin(); j != creditCards.end(); ++j)
+ creditCards_.push_back(**j);
+ }
+ return self;
+}
+
+// Close the dialog.
+- (void)closeDialog {
+ [[self window] close];
+ [NSApp stopModal];
+}
+
+- (AutoFillAddressViewController*)addressFormViewController {
+ return addressFormViewController_.get();
+}
+
+- (AutoFillCreditCardViewController*)creditCardFormViewController {
+ return creditCardFormViewController_.get();
+}
+
+@end
+
+@implementation AutoFillDialogController (PrivateMethods)
+
+// Run application modal.
+- (void)runModalDialog {
+ [NSApp runModalForWindow:[self window]];
+}
+
+// Install controller and views for the address form and the credit card form.
+// They are installed into the appropriate sibling order so that they can be
+// arranged vertically by the VerticalLayoutView class. We insert the views
+// into the |childView_| but we hold onto the controllers and release them in
+// our dealloc once the dialog closes.
+- (void)installChildViews {
+ if (profiles_.size() > 0) {
+ AutoFillAddressViewController* autoFillAddressViewController =
+ [[AutoFillAddressViewController alloc] initWithProfile:profiles_[0]];
+ addressFormViewController_.reset(autoFillAddressViewController);
+
+ // Embed the child view into our (owned by us) target view.
+ [childView_ addSubview:[addressFormViewController_ view]
+ positioned:NSWindowBelow relativeTo:addressSection_];
+ [[addressFormViewController_ view] setFrameOrigin:NSMakePoint(0, 0)];
+ }
+
+ if (creditCards_.size() > 0) {
+ AutoFillCreditCardViewController* autoFillCreditCardViewController =
+ [[AutoFillCreditCardViewController alloc]
+ initWithCreditCard:creditCards_[0]];
+ creditCardFormViewController_.reset(autoFillCreditCardViewController);
+
+ // Embed the child view into our (owned by us) target view.
+ [childView_ addSubview:[creditCardFormViewController_ view]
+ positioned:NSWindowBelow relativeTo:creditCardSection_];
+ [[creditCardFormViewController_ view] setFrameOrigin:NSMakePoint(0, 0)];
+ }
+}
+
+@end
diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm b/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm
new file mode 100644
index 0000000..907e2dc
--- /dev/null
+++ b/chrome/browser/autofill/autofill_dialog_controller_mac_unittest.mm
@@ -0,0 +1,205 @@
+// Copyright (c) 2009 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_address_model_mac.h"
+#import "chrome/browser/autofill/autofill_address_view_controller_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_model_mac.h"
+#import "chrome/browser/autofill/autofill_credit_card_view_controller_mac.h"
+#import "chrome/browser/autofill/autofill_dialog_controller_mac.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/cocoa/browser_test_helper.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "chrome/browser/profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+class AutoFillDialogObserverTester : public AutoFillDialogObserver {
+ public:
+ AutoFillDialogObserverTester() : hit_(false) {}
+ virtual ~AutoFillDialogObserverTester() {}
+
+ virtual void OnAutoFillDialogApply(
+ std::vector<AutoFillProfile>* profiles,
+ std::vector<CreditCard>* credit_cards) {
+ hit_ = true;
+
+ std::vector<AutoFillProfile>::iterator i;
+ profiles_.clear();
+ for (i = profiles->begin(); i != profiles->end(); ++i)
+ profiles_.push_back(*i);
+
+ std::vector<CreditCard>::iterator j;
+ credit_cards_.clear();
+ for (j = credit_cards->begin(); j != credit_cards->end(); ++j)
+ credit_cards_.push_back(*j);
+ }
+
+ bool hit_;
+ std::vector<AutoFillProfile> profiles_;
+ std::vector<CreditCard> credit_cards_;
+};
+
+class AutoFillDialogControllerTest : public CocoaTest {
+ public:
+ AutoFillDialogControllerTest() {}
+
+ void LoadDialog() {
+ controller_ = [AutoFillDialogController
+ controllerWithObserver:&observer_
+ autoFillProfiles:profiles_
+ creditCards:credit_cards_];
+ [controller_ window];
+ }
+
+ BrowserTestHelper helper_;
+ AutoFillDialogObserverTester observer_;
+ AutoFillDialogController* controller_; // weak reference
+ std::vector<AutoFillProfile*> profiles_; // weak references within vector
+ std::vector<CreditCard*> credit_cards_; // weak references within vector
+};
+
+TEST_F(AutoFillDialogControllerTest, SaveButtonInformsObserver) {
+ LoadDialog();
+ [controller_ save:nil];
+ ASSERT_TRUE(observer_.hit_);
+}
+
+TEST_F(AutoFillDialogControllerTest, CancelButtonDoesNotInformObserver) {
+ LoadDialog();
+ [controller_ cancel:nil];
+ ASSERT_FALSE(observer_.hit_);
+}
+
+TEST_F(AutoFillDialogControllerTest, NoEditsGiveBackOriginalProfile) {
+ AutoFillProfile profile;
+ profiles_.push_back(&profile);
+ LoadDialog();
+ [controller_ save:nil];
+
+ // Should hit our observer.
+ ASSERT_TRUE(observer_.hit_);
+
+ // Sizes should match.
+ ASSERT_EQ(observer_.profiles_.size(), profiles_.size());
+
+ // Contents should match.
+ size_t i = 0;
+ size_t count = profiles_.size();
+ for (i = 0; i < count; i++)
+ ASSERT_EQ(observer_.profiles_[i], *profiles_[i]);
+
+ // Contents should not match a different profile.
+ AutoFillProfile differentProfile;
+ differentProfile.set_label(ASCIIToUTF16("different"));
+ differentProfile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("joe"));
+ for (i = 0; i < count; i++)
+ ASSERT_NE(observer_.profiles_[i], differentProfile);
+}
+
+TEST_F(AutoFillDialogControllerTest, NoEditsGiveBackOriginalCreditCard) {
+ CreditCard creditCard(ASCIIToUTF16("myCC"), 345);
+ credit_cards_.push_back(&creditCard);
+ LoadDialog();
+ [controller_ save:nil];
+
+ // Should hit our observer.
+ ASSERT_TRUE(observer_.hit_);
+
+ // Sizes should match.
+ ASSERT_EQ(observer_.credit_cards_.size(), credit_cards_.size());
+
+ // Contents should match.
+ size_t i = 0;
+ size_t count = credit_cards_.size();
+ for (i = 0; i < count; i++)
+ ASSERT_EQ(observer_.credit_cards_[i], *credit_cards_[i]);
+
+ // Contents should not match a different profile.
+ CreditCard differentCreditCard(ASCIIToUTF16("different"), 0);
+ differentCreditCard.SetInfo(
+ AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("1234"));
+ for (i = 0; i < count; i++)
+ ASSERT_NE(observer_.credit_cards_[i], differentCreditCard);
+}
+
+TEST_F(AutoFillDialogControllerTest, AutoFillDataMutation) {
+ AutoFillProfile profile(ASCIIToUTF16("Home"), 17);
+ profile.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("David"));
+ profile.SetInfo(AutoFillType(NAME_MIDDLE), ASCIIToUTF16("C"));
+ profile.SetInfo(AutoFillType(NAME_LAST), ASCIIToUTF16("Holloway"));
+ profile.SetInfo(AutoFillType(EMAIL_ADDRESS),
+ ASCIIToUTF16("dhollowa@chromium.org"));
+ profile.SetInfo(AutoFillType(COMPANY_NAME), ASCIIToUTF16("Google Inc."));
+ profile.SetInfo(
+ AutoFillType(ADDRESS_HOME_LINE1), ASCIIToUTF16("1122 Mountain View Road"));
+ profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE2), ASCIIToUTF16("Suite #1"));
+ profile.SetInfo(AutoFillType(ADDRESS_HOME_CITY),
+ ASCIIToUTF16("Mountain View"));
+ profile.SetInfo(AutoFillType(ADDRESS_HOME_STATE), ASCIIToUTF16("CA"));
+ profile.SetInfo(AutoFillType(ADDRESS_HOME_ZIP), ASCIIToUTF16("94111"));
+ profile.SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("USA"));
+ profile.SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE), ASCIIToUTF16("01"));
+ profile.SetInfo(AutoFillType(PHONE_HOME_CITY_CODE), ASCIIToUTF16("415"));
+ profile.SetInfo(AutoFillType(PHONE_HOME_NUMBER), ASCIIToUTF16("5552258"));
+ profile.SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE), ASCIIToUTF16("02"));
+ profile.SetInfo(AutoFillType(PHONE_FAX_CITY_CODE), ASCIIToUTF16("408"));
+ profile.SetInfo(AutoFillType(PHONE_FAX_NUMBER), ASCIIToUTF16("7172258"));
+ profiles_.push_back(&profile);
+
+ LoadDialog();
+
+ AutoFillAddressModel* am = [[controller_ addressFormViewController]
+ addressModel];
+ EXPECT_TRUE([[am firstName] isEqualToString:@"David"]);
+ EXPECT_TRUE([[am middleName] isEqualToString:@"C"]);
+ EXPECT_TRUE([[am lastName] isEqualToString:@"Holloway"]);
+ EXPECT_TRUE([[am email] isEqualToString:@"dhollowa@chromium.org"]);
+ EXPECT_TRUE([[am companyName] isEqualToString:@"Google Inc."]);
+ EXPECT_TRUE([[am addressLine1] isEqualToString:@"1122 Mountain View Road"]);
+ EXPECT_TRUE([[am addressLine2] isEqualToString:@"Suite #1"]);
+ EXPECT_TRUE([[am city] isEqualToString:@"Mountain View"]);
+ EXPECT_TRUE([[am state] isEqualToString:@"CA"]);
+ EXPECT_TRUE([[am zip] isEqualToString:@"94111"]);
+ EXPECT_TRUE([[am phoneCountryCode] isEqualToString:@"01"]);
+ EXPECT_TRUE([[am phoneAreaCode] isEqualToString:@"415"]);
+ EXPECT_TRUE([[am phoneNumber] isEqualToString:@"5552258"]);
+ EXPECT_TRUE([[am faxCountryCode] isEqualToString:@"02"]);
+ EXPECT_TRUE([[am faxAreaCode] isEqualToString:@"408"]);
+ EXPECT_TRUE([[am faxNumber] isEqualToString:@"7172258"]);
+
+ [controller_ save:nil];
+
+ ASSERT_TRUE(observer_.hit_);
+ ASSERT_TRUE(observer_.profiles_.size() == 1);
+}
+
+TEST_F(AutoFillDialogControllerTest, CreditCardDataMutation) {
+ CreditCard creditCard(ASCIIToUTF16("myCC"), 345);
+ creditCard.SetInfo(AutoFillType(CREDIT_CARD_NAME), ASCIIToUTF16("DCH"));
+ creditCard.SetInfo(
+ AutoFillType(CREDIT_CARD_NUMBER), ASCIIToUTF16("1234 5678 9101 1121"));
+ creditCard.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("01"));
+ creditCard.SetInfo(
+ AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), ASCIIToUTF16("2012"));
+ creditCard.SetInfo(
+ AutoFillType(CREDIT_CARD_VERIFICATION_CODE), ASCIIToUTF16("222"));
+ credit_cards_.push_back(&creditCard);
+
+ LoadDialog();
+
+ AutoFillCreditCardModel* cm = [[controller_ creditCardFormViewController]
+ creditCardModel];
+ EXPECT_TRUE([[cm nameOnCard] isEqualToString:@"DCH"]);
+ EXPECT_TRUE([[cm creditCardNumber] isEqualToString:@"1234 5678 9101 1121"]);
+ EXPECT_TRUE([[cm expirationMonth] isEqualToString:@"01"]);
+ EXPECT_TRUE([[cm expirationYear] isEqualToString:@"2012"]);
+ EXPECT_TRUE([[cm cvcCode] isEqualToString:@"222"]);
+
+ [controller_ save:nil];
+
+ ASSERT_TRUE(observer_.hit_);
+ ASSERT_TRUE(observer_.credit_cards_.size() == 1);
+}
+
+}
diff --git a/chrome/browser/autofill/autofill_dialog.cc b/chrome/browser/autofill/autofill_dialog_mac.mm
index 44df062..912ced4 100644
--- a/chrome/browser/autofill/autofill_dialog.cc
+++ b/chrome/browser/autofill/autofill_dialog_mac.mm
@@ -2,13 +2,17 @@
// 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 "chrome/browser/autofill/autofill_dialog.h"
-// TODO(dhollowa): Remove these as each platform implements this
-// function. The last one to implement the function should remove this file.
-#if defined(OS_MACOSX)
+// Mac implementation of |ShowAutoFillDialog| interface defined in
+// |chrome/browser/autofill/autofill_dialog.h|.
void ShowAutoFillDialog(AutoFillDialogObserver* observer,
const std::vector<AutoFillProfile*>& profiles,
const std::vector<CreditCard*>& credit_cards) {
+ [AutoFillDialogController
+ showAutoFillDialogWithObserver:observer
+ autoFillProfiles:profiles
+ creditCards:credit_cards];
}
-#endif // defined(OS_WIN) || defined(OS_MACOSX)
+
diff --git a/chrome/browser/autofill/autofill_profile.cc b/chrome/browser/autofill/autofill_profile.cc
index caf335a..fc3b30c 100644
--- a/chrome/browser/autofill/autofill_profile.cc
+++ b/chrome/browser/autofill/autofill_profile.cc
@@ -158,6 +158,10 @@ bool AutoFillProfile::operator==(const AutoFillProfile& profile) const {
return true;
}
+bool AutoFillProfile::operator!=(const AutoFillProfile& profile) const {
+ return !operator==(profile);
+}
+
void AutoFillProfile::set_use_billing_address(bool use) {
if (use_billing_address_ == use)
return;
diff --git a/chrome/browser/autofill/autofill_profile.h b/chrome/browser/autofill/autofill_profile.h
index 6142721..65182ec 100644
--- a/chrome/browser/autofill/autofill_profile.h
+++ b/chrome/browser/autofill/autofill_profile.h
@@ -56,6 +56,7 @@ class AutoFillProfile : public FormGroup {
// Used by tests.
// TODO(jhawkins): Move these to private and add the test as a friend.
bool operator==(const AutoFillProfile& profile) const;
+ bool operator!=(const AutoFillProfile& profile) const;
void set_label(const string16& label) { label_ = label; }
private:
diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc
index d4e5589..e32ccf1 100644
--- a/chrome/browser/autofill/credit_card.cc
+++ b/chrome/browser/autofill/credit_card.cc
@@ -42,7 +42,6 @@ CreditCard::CreditCard()
expiration_year_(0) {
}
-
FormGroup* CreditCard::Clone() const {
return new CreditCard(*this);
}
@@ -325,6 +324,10 @@ bool CreditCard::operator==(const CreditCard& creditcard) const {
return true;
}
+bool CreditCard::operator!=(const CreditCard& creditcard) const {
+ return !operator==(creditcard);
+}
+
bool CreditCard::FindInfoMatchesHelper(const AutoFillFieldType& field_type,
const string16& info,
string16* match) const {
diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h
index 1f99405..1c22d14 100644
--- a/chrome/browser/autofill/credit_card.h
+++ b/chrome/browser/autofill/credit_card.h
@@ -90,6 +90,7 @@ class CreditCard : public FormGroup {
// Used by tests.
bool operator==(const CreditCard& creditcard) const;
+ bool operator!=(const CreditCard& creditcard) const;
void set_label(const string16& label) { label_ = label; }
private:
diff --git a/chrome/browser/cocoa/disclosure_view_controller.h b/chrome/browser/cocoa/disclosure_view_controller.h
new file mode 100644
index 0000000..71a2c5f
--- /dev/null
+++ b/chrome/browser/cocoa/disclosure_view_controller.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef CHROME_BROWSER_COCOA_DISCLOSURE_VIEW_CONTROLLER_
+#define CHROME_BROWSER_COCOA_DISCLOSURE_VIEW_CONTROLLER_
+
+#import <Cocoa/Cocoa.h>
+
+@class DisclosureViewState;
+
+// A view class that provides a disclosure triangle that controls the size
+// of the view. Toggling the disclosure triangle animates the change in
+// size of the view. The |openHeight| is initialized from the initial size
+// of the view. |disclosureState| is initialized as |NSOnState| (of type
+// NSCellStateValue) which corresponds to "open".
+@interface DisclosureViewController : NSViewController {
+ @private
+ // The |disclosureState_| is instantiated from within |awakeFromNib|.
+ // We do not hold it as a scoped_nsobject because it is exposed as a KVO
+ // compliant property.
+ DisclosureViewState* disclosureState_; // strong reference
+ CGFloat openHeight_;
+}
+
+@property (nonatomic, retain) DisclosureViewState* disclosureState;
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_DISCLOSURE_VIEW_CONTROLLER_
diff --git a/chrome/browser/cocoa/disclosure_view_controller.mm b/chrome/browser/cocoa/disclosure_view_controller.mm
new file mode 100644
index 0000000..f41acd5
--- /dev/null
+++ b/chrome/browser/cocoa/disclosure_view_controller.mm
@@ -0,0 +1,189 @@
+// 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/cocoa/disclosure_view_controller.h"
+#include "base/logging.h"
+#include "base/scoped_nsobject.h"
+
+const NSCellStateValue kInitialDisclosureState = NSOffState;
+const NSInteger kClosedBoxHeight = 20;
+NSString* const kKVODisclosedKey = @"disclosed";
+
+// This class externalizes the state of the disclosure control. When the
+// disclosure control is pressed it changes the state of this object. In turn
+// the KVO machinery detects the change to |disclosed| and signals the
+// |observeValueForKeyPath| call in the |DisclosureViewController|.
+@interface DisclosureViewState : NSObject {
+ @private
+ NSCellStateValue disclosed;
+}
+@end
+
+@implementation DisclosureViewState
+@end
+
+@interface DisclosureViewController(PrivateMethods)
+
+- (void)initDisclosureState:(NSCellStateValue)state;
+- (NSRect)openStateFrameSize:(NSRect)startFrame;
+- (NSRect)closedStateFrameSize:(NSRect)startFrame;
+
+- (void)startAnimations:(NSView*)view
+ start:(NSRect)startFrame
+ end:(NSRect)endFrame;
+
+- (void)discloseDetails:(NSCellStateValue)state;
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context;
+
+@end
+
+@implementation DisclosureViewController
+
+@synthesize disclosureState = disclosureState_;
+
+- (void)awakeFromNib {
+ // Create the disclosure state.
+ [self setDisclosureState:[[[DisclosureViewState alloc] init] autorelease]];
+
+ // Set up the initial disclosure state before we install the observer.
+ // We don't want our animations firing before we're done initializing.
+ [disclosureState_ setValue:[NSNumber numberWithInt:kInitialDisclosureState]
+ forKey:kKVODisclosedKey];
+
+ // Pick up "open" height from the initial state of the view in the nib.
+ openHeight_ = [[self view] frame].size.height;
+
+ // Set frame size according to initial disclosure state.
+ [self initDisclosureState:kInitialDisclosureState];
+
+ // Setup observers so that when disclosure state changes we resize frame
+ // accordingly.
+ [disclosureState_ addObserver:self forKeyPath:kKVODisclosedKey
+ options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
+ context:nil];
+}
+
+- (void)dealloc {
+ [disclosureState_ removeObserver:self forKeyPath:kKVODisclosedKey];
+ [disclosureState_ release];
+ [super dealloc];
+}
+
+@end
+
+@implementation DisclosureViewController(PrivateMethods)
+
+// Initializes the view's frame geometry based on the input |state|.
+// If the |state| is NSOnState then the frame size corresponds to "open".
+// If the |state| is NSOffState then the frame size corresponds to "closed".
+// The |origin.x| and |size.width| remain unchanged, but the |origin.y| and
+// |size.height| may vary.
+- (void)initDisclosureState:(NSCellStateValue)state {
+ if (state == NSOnState) {
+ [[self view] setFrame:[self openStateFrameSize:[[self view] frame]]];
+ }
+ else if (state == NSOffState) {
+ [[self view] setFrame:[self closedStateFrameSize:[[self view] frame]]];
+ }
+ else {
+ NOTREACHED();
+ }
+}
+
+// Computes the frame geometry during the "open" state of the disclosure view.
+- (NSRect)openStateFrameSize:(NSRect)startFrame {
+ return NSMakeRect(startFrame.origin.x,
+ startFrame.size.height - openHeight_ +
+ startFrame.origin.y,
+ startFrame.size.width,
+ openHeight_);
+}
+
+// Computes the frame geometry during the "closed" state of the disclosure view.
+- (NSRect)closedStateFrameSize:(NSRect)startFrame {
+ return NSMakeRect(startFrame.origin.x,
+ startFrame.size.height - kClosedBoxHeight +
+ startFrame.origin.y,
+ startFrame.size.width,
+ kClosedBoxHeight);
+}
+
+// Animates the opening or closing of the disclosure view. The |startFrame|
+// specifies the frame geometry at the beginning of the animation and the
+// |endFrame| specifies the geometry at the end of the animation. The input
+// |view| is view managed by this controller.
+- (void)startAnimations:(NSView*)view
+ start:(NSRect)startFrame
+ end:(NSRect)endFrame
+{
+ // Setup dictionary describing animation.
+ // Create the attributes dictionary for the first view.
+ NSMutableDictionary* dictionary;
+ dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ // Specify which view to modify.
+ view, NSViewAnimationTargetKey,
+ // Specify the starting position of the view.
+ [NSValue valueWithRect:startFrame], NSViewAnimationStartFrameKey,
+ // Change the ending position of the view.
+ [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey,
+ nil];
+
+ // Create the view animation object.
+ scoped_nsobject<NSViewAnimation> animation;
+ animation.reset([[NSViewAnimation alloc] initWithViewAnimations:
+ [NSArray arrayWithObject:dictionary]]);
+
+ // Set some additional attributes for the animation.
+ [animation.get() setDuration:.2];
+ [animation.get() setAnimationCurve:NSAnimationEaseIn];
+
+ // Run the animation.
+ [animation.get() startAnimation];
+}
+
+// This method is invoked when the disclosure state changes. It computes
+// the appropriate view frame geometry and then initiates the animation to
+// change that geometry.
+- (void)discloseDetails:(NSCellStateValue)state {
+ NSRect startFrame = [[self view] frame];
+ NSRect endFrame = startFrame;
+
+ if (state == NSOnState) {
+ endFrame = [self openStateFrameSize:startFrame];
+ } else if (state == NSOffState) {
+ endFrame = [self closedStateFrameSize:startFrame];
+ } else {
+ NOTREACHED();
+ return;
+ }
+
+ [self startAnimations:[self view] start:startFrame end:endFrame];
+}
+
+// The |DisclosureViewController| is an observer of an instance of a
+// |DisclosureViewState| object. This object lives within the controller's
+// nib file. When the KVO machinery detects a change to the state
+// it triggers this call and we initiate the change in frame geometry of the
+// view.
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ if ([keyPath isEqualToString:kKVODisclosedKey]) {
+ NSCellStateValue newValue =
+ [[change objectForKey:NSKeyValueChangeNewKey] intValue];
+ NSCellStateValue oldValue =
+ [[change objectForKey:NSKeyValueChangeOldKey] intValue];
+
+ if (newValue != oldValue) {
+ [self discloseDetails:newValue];
+ }
+ }
+}
+
+@end
diff --git a/chrome/browser/cocoa/preferences_window_controller.h b/chrome/browser/cocoa/preferences_window_controller.h
index e29047e..352f6d8 100644
--- a/chrome/browser/cocoa/preferences_window_controller.h
+++ b/chrome/browser/cocoa/preferences_window_controller.h
@@ -126,6 +126,7 @@ class ProfileSyncService;
// User Data panel
- (IBAction)showSavedPasswords:(id)sender;
+- (IBAction)showAutoFillSettings:(id)sender;
- (IBAction)importData:(id)sender;
- (IBAction)clearData:(id)sender;
- (IBAction)resetThemeToDefault:(id)sender;
diff --git a/chrome/browser/cocoa/preferences_window_controller.mm b/chrome/browser/cocoa/preferences_window_controller.mm
index 56e369f..357824d 100644
--- a/chrome/browser/cocoa/preferences_window_controller.mm
+++ b/chrome/browser/cocoa/preferences_window_controller.mm
@@ -12,6 +12,9 @@
#include "base/string16.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
+#include "chrome/browser/autofill/autofill_dialog.h"
+#include "chrome/browser/autofill/autofill_type.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
@@ -1145,6 +1148,28 @@ const int kDisabledIndex = 1;
[self launchKeychainAccess];
}
+// Called to show the Auto Fill Settings dialog.
+- (IBAction)showAutoFillSettings:(id)sender {
+ [self recordUserAction:"Options_ShowAutoFillSettings"];
+
+ // TODO(dhollowa): Need "n" of these. Create single entry for now.
+ // See http://crbug.com/33029.
+ std::vector<AutoFillProfile*> profiles;
+ AutoFillProfile profile(ASCIIToUTF16(""), 0);
+ profiles.push_back(&profile);
+
+ // TODO(dhollowa): Need "n" of these. Create single entry for now.
+ // See http://crbug.com/33029.
+ std::vector<CreditCard*> creditCards;
+ CreditCard creditCard(ASCIIToUTF16(""), 0);
+ creditCards.push_back(&creditCard);
+
+ // TODO(dhollowa): There are outstanding assertions in autofill back end.
+ // Hooking up with UI only until those issues are resolved.
+ // See http://crbug.com/33029.
+ ShowAutoFillDialog(NULL, profiles, creditCards);
+}
+
// Called to import data from other browsers (Safari, Firefox, etc).
- (IBAction)importData:(id)sender {
UserMetrics::RecordAction("Import_ShowDlg", profile_);
diff --git a/chrome/browser/cocoa/section_separator_view.h b/chrome/browser/cocoa/section_separator_view.h
new file mode 100644
index 0000000..22a9412
--- /dev/null
+++ b/chrome/browser/cocoa/section_separator_view.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_COCOA_SECTION_SEPARATOR_VIEW_
+#define CHROME_BROWSER_COCOA_SECTION_SEPARATOR_VIEW_
+
+#import <Cocoa/Cocoa.h>
+
+// A view class that renders a gradient "section" separator. The visual
+// style is modelled similarly to iPhone table view separators. This view
+// paints a simple top-to-bottom gradient in its bounds of fixed gray values.
+// Optionally, it also paints a "topline" and "baseline". Default is to
+// draw both topline and baseline, but these can be overridden.
+// The user of the class can override the color of the base line and top line
+// using the |baselineSeparatorColor| and |toplineSeparatorColor| properties.
+@interface SectionSeparatorView : NSView {
+ @private
+ BOOL showBaseLine_;
+ NSColor* baselineSeparatorColor_;
+ BOOL showTopLine_;
+ NSColor* toplineSeparatorColor_;
+}
+
+@property (nonatomic, assign) BOOL showBaseLine;
+@property (nonatomic, retain) NSColor* baselineSeparatorColor;
+@property (nonatomic, assign) BOOL showTopLine;
+@property (nonatomic, retain) NSColor* toplineSeparatorColor;
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_SECTION_SEPARATOR_VIEW_
diff --git a/chrome/browser/cocoa/section_separator_view.mm b/chrome/browser/cocoa/section_separator_view.mm
new file mode 100644
index 0000000..a7968bc
--- /dev/null
+++ b/chrome/browser/cocoa/section_separator_view.mm
@@ -0,0 +1,106 @@
+// 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/cocoa/section_separator_view.h"
+
+@interface SectionSeparatorView (PrivateMethods)
+- (void)drawGradientRect:(NSRect)rect;
+- (void)drawBaseLineRect:(NSRect)rect;
+- (void)drawTopLineRect:(NSRect)rect;
+@end
+
+@implementation SectionSeparatorView
+
+@synthesize showBaseLine = showBaseLine_;
+@synthesize baselineSeparatorColor = baselineSeparatorColor_;
+@synthesize showTopLine = showTopLine_;
+@synthesize toplineSeparatorColor = toplineSeparatorColor_;
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ [self setShowBaseLine:YES];
+ [self setBaselineSeparatorColor:[NSColor grayColor]];
+ [self setShowTopLine:YES];
+ [self setToplineSeparatorColor:[NSColor lightGrayColor]];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [baselineSeparatorColor_ release];
+ [toplineSeparatorColor_ release];
+ [super dealloc];
+}
+
+- (void)drawRect:(NSRect)rect {
+ NSRect gradientBounds = [self bounds];
+ NSRect baselineRect = gradientBounds;
+ NSRect toplineRect = gradientBounds;
+ gradientBounds.size.height -= 1;
+ gradientBounds.origin.y += 1;
+ baselineRect.size.height = 1;
+ baselineRect.origin.y = 0;
+ toplineRect.size.height = 1;
+ toplineRect.origin.y = gradientBounds.size.height;
+ [self drawGradientRect:gradientBounds];
+ if ([self showBaseLine])
+ [self drawBaseLineRect:baselineRect];
+ if ([self showTopLine])
+ [self drawTopLineRect:toplineRect];
+}
+
+@end
+
+@implementation SectionSeparatorView (PrivateMethods)
+
+// This method draws the gradient fill of the "separator" bar. The input
+// |rect| designates the bounds that will be filled with the the gradient.
+// The gradient has two stops, lighter gray blending to
+// darker gray, descending from the top of the |rect| to the bottom.
+- (void)drawGradientRect:(NSRect)rect {
+ // Compute start and end points where to draw the gradient.
+ CGPoint startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));
+ CGPoint endPoint = CGPointMake(NSMinX(rect), NSMaxY(rect));
+
+ // Setup the context and colorspace.
+ CGContextRef context =
+ (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState(context);
+ CGColorSpaceRef colorspace =
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+
+ // Create the gradient.
+ const size_t stopCount = 2;
+ CGFloat stops[stopCount] = { 0.0, 1.0 };
+ CGFloat components[8] = {
+ 0.75, 0.75, 0.75, 1.0, // start color
+ 0.95, 0.95, 0.95, 1.0 }; // end color
+
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(
+ colorspace, components, stops, stopCount);
+
+ CGContextClipToRect(context, *(CGRect*)&rect);
+ CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
+
+ CGGradientRelease(gradient);
+ CGColorSpaceRelease(colorspace);
+ CGContextRestoreGState(context);
+}
+
+// Draws the base line of the separator bar using the |baselineSeparatorColor_|
+// designated color.
+- (void)drawBaseLineRect:(NSRect)rect {
+ [baselineSeparatorColor_ set];
+ NSFrameRect(rect);
+}
+
+// Draws the top line of the separator bar using the |toplineSeparatorColor_|
+// designated color.
+- (void)drawTopLineRect:(NSRect)rect {
+ [toplineSeparatorColor_ set];
+ NSFrameRect(rect);
+}
+
+@end
diff --git a/chrome/browser/cocoa/vertical_layout_view.h b/chrome/browser/cocoa/vertical_layout_view.h
new file mode 100644
index 0000000..56e9954
--- /dev/null
+++ b/chrome/browser/cocoa/vertical_layout_view.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef CHROME_BROWSER_COCOA_VERTICAL_LAYOUT_VIEW_
+#define CHROME_BROWSER_COCOA_VERTICAL_LAYOUT_VIEW_
+
+#import <Cocoa/Cocoa.h>
+
+// A view class that automatically performs layout of child views based
+// on paint order of the children in the view hierarchy. The children are
+// arranged top-to-bottom (in y-order) based on each child's height.
+// Horizontal (x) positions are left as specified. Layout is performed when
+// children are added, removed, or have their frames changed. Layout is also
+// performed when this view (|self|) has its frame changed.
+// Autoresizing is disabled for |VerticalLayoutView|s.
+@interface VerticalLayoutView : NSView {
+}
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_VERTICAL_LAYOUT_VIEW_
diff --git a/chrome/browser/cocoa/vertical_layout_view.mm b/chrome/browser/cocoa/vertical_layout_view.mm
new file mode 100644
index 0000000..b4e6a5e
--- /dev/null
+++ b/chrome/browser/cocoa/vertical_layout_view.mm
@@ -0,0 +1,73 @@
+// 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/cocoa/vertical_layout_view.h"
+
+@interface VerticalLayoutView(PrivateMethods)
+- (void)layoutChildren;
+@end
+
+@implementation VerticalLayoutView
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ // Turn auto resizing off, we'll be laying out our children programatically.
+ [self setAutoresizesSubviews:NO];
+ [self setAutoresizingMask:NSViewNotSizable];
+ }
+
+ return self;
+}
+
+// Flip the coordinate system to arrange child views from top to bottom
+// with top at 0, increasing down. This simplifies the logic and plays
+// well with containing scroll views.
+- (BOOL)isFlipped {
+ return YES;
+}
+
+// Override the default |viewWillDraw| to indicate to drawing machinery proper
+// arrangement of subvies.
+- (void)viewWillDraw {
+ // Reposition child views prior to super's descent into its |viewWillDraw|
+ // pass.
+ [self layoutChildren];
+
+ // Default descent into subviews.
+ [super viewWillDraw];
+
+ // Adjust children again to account for any modifications made during the
+ // prior descent. Most importantly we resize our own frame to properly
+ // adjust any containing scroll view.
+ [self layoutChildren];
+}
+
+@end
+
+@implementation VerticalLayoutView(PrivateMethods)
+
+// This method traverses the immediate subviews measuring their height and
+// adjusting their frames so they are arranged vertically ordered relative
+// to their sibling views. Note the dependency here on the |isFlipped|
+// state. This code assumes |isFlipped| is YES.
+- (void)layoutChildren {
+ NSArray* children = [self subviews];
+ int childCount = [children count];
+
+ CGFloat yPosition = 0.0;
+ for (int i = childCount-1; i >= 0; --i) {
+ NSView* child = [children objectAtIndex:i];
+ [child setFrameOrigin:NSMakePoint([child frame].origin.x, yPosition)];
+ yPosition += [child frame].size.height;
+ }
+
+ // Resize self to reflect vertical extent of children.
+ [self setFrame:NSMakeRect([self frame].origin.x,
+ [self frame].origin.y,
+ [self frame].size.width,
+ yPosition)];
+}
+
+@end