diff options
author | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-24 08:32:55 +0000 |
---|---|---|
committer | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-24 08:32:55 +0000 |
commit | 2609bc107a31083c6c88e207d13eaa029d594235 (patch) | |
tree | 8331e468eb82a2a9611f388349da4f3565ee3201 /chrome/browser | |
parent | 51552aae65a96d19e1adcbf6fe760f102b53a8bf (diff) | |
download | chromium_src-2609bc107a31083c6c88e207d13eaa029d594235.zip chromium_src-2609bc107a31083c6c88e207d13eaa029d594235.tar.gz chromium_src-2609bc107a31083c6c88e207d13eaa029d594235.tar.bz2 |
Add the ability to save and remove AutoFill profiles from the AutoFillDialog.
BUG=18201
TEST=PersonalDataManagerTest.SetProfiles
Review URL: http://codereview.chromium.org/545175
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36978 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/autofill/autofill_dialog.cc | 4 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_dialog.h | 15 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_dialog_gtk.cc | 172 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 37 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 14 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_profile.cc | 39 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_profile.h | 4 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.cc | 150 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.h | 76 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager_unittest.cc | 176 | ||||
-rw-r--r-- | chrome/browser/profile.cc | 2 | ||||
-rw-r--r-- | chrome/browser/webdata/web_data_service.cc | 49 | ||||
-rw-r--r-- | chrome/browser/webdata/web_data_service.h | 41 | ||||
-rw-r--r-- | chrome/browser/webdata/web_database.cc | 90 | ||||
-rw-r--r-- | chrome/browser/webdata/web_database.h | 8 | ||||
-rw-r--r-- | chrome/browser/webdata/web_database_unittest.cc | 36 |
16 files changed, 760 insertions, 153 deletions
diff --git a/chrome/browser/autofill/autofill_dialog.cc b/chrome/browser/autofill/autofill_dialog.cc index d15cca4..f574987 100644 --- a/chrome/browser/autofill/autofill_dialog.cc +++ b/chrome/browser/autofill/autofill_dialog.cc @@ -8,7 +8,7 @@ // function. The last one to implement the function should remove this file. #if defined(OS_WIN) || defined(OS_MACOSX) void ShowAutoFillDialog(AutoFillDialogObserver* observer, - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards) { + const std::vector<AutoFillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards) { } #endif // defined(OS_WIN) || defined(OS_MACOSX) diff --git a/chrome/browser/autofill/autofill_dialog.h b/chrome/browser/autofill/autofill_dialog.h index 34006b4..7a5c966 100644 --- a/chrome/browser/autofill/autofill_dialog.h +++ b/chrome/browser/autofill/autofill_dialog.h @@ -14,10 +14,11 @@ // the user has applied changes to the AutoFill profile data. class AutoFillDialogObserver { public: - // The user has confirmed changes by clicking "Apply" or "OK". + // The user has confirmed changes by clicking "Apply" or "OK". Any of the + // parameters may be NULL. virtual void OnAutoFillDialogApply( - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards) = 0; + std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards) = 0; protected: virtual ~AutoFillDialogObserver() {} @@ -29,8 +30,12 @@ class AutoFillDialogObserver { // changes made to the profile information through the dialog should be // transferred back into |profiles| and |credit_cards|. |observer| will be // notified by OnAutoFillDialogAccept when the user has applied changes. +// +// The PersonalDataManager owns the contents of these vectors. The lifetime of +// the contents is until the PersonalDataManager replaces them with new data +// whenever the web database is updated. void ShowAutoFillDialog(AutoFillDialogObserver* observer, - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards); + const std::vector<AutoFillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards); #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_DIALOG_H_ diff --git a/chrome/browser/autofill/autofill_dialog_gtk.cc b/chrome/browser/autofill/autofill_dialog_gtk.cc index 6432f59..33a30a5 100644 --- a/chrome/browser/autofill/autofill_dialog_gtk.cc +++ b/chrome/browser/autofill/autofill_dialog_gtk.cc @@ -26,6 +26,10 @@ namespace { // Style for dialog group titles. const char kDialogGroupTitleMarkup[] = "<span weight='bold'>%s</span>"; +// The name of the object property used to store an entry widget pointer on +// another widget. +const char kButtonDataKey[] = "label-entry"; + // How far we indent dialog widgets, in pixels. const int kAutoFillDialogIndent = 5; @@ -73,6 +77,23 @@ void SetWhiteBackground(GtkWidget* widget) { gtk_widget_destroy(entry); } +string16 GetEntryText(GtkWidget* entry) { + return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(entry))); +} + +void SetEntryText(GtkWidget* entry, const string16& text) { + gtk_entry_set_text(GTK_ENTRY(entry), UTF16ToUTF8(text).c_str()); +} + +void SetButtonData(GtkWidget* widget, GtkWidget* entry) { + g_object_set_data(G_OBJECT(widget), kButtonDataKey, entry); +} + +GtkWidget* GetButtonData(GtkWidget* widget) { + return static_cast<GtkWidget*>( + g_object_get_data(G_OBJECT(widget), kButtonDataKey)); +} + //////////////////////////////////////////////////////////////////////////////// // Form Table helpers. // @@ -191,8 +212,8 @@ GtkWidget* FormTableAddLabelEntry( class AutoFillDialog { public: AutoFillDialog(AutoFillDialogObserver* observer, - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards); + const std::vector<AutoFillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards); ~AutoFillDialog() {} // Shows the AutoFill dialog. @@ -216,6 +237,13 @@ class AutoFillDialog { // 'clicked' signal handler. We add a new credit card. static void OnAddCreditCardClicked(GtkButton* button, AutoFillDialog* dialog); + // 'clicked' signal handler. We delete the associated address. + static void OnDeleteAddressClicked(GtkButton* button, AutoFillDialog* dialog); + + // 'clicked' signal handler. We delete the associated credit card. + static void OnDeleteCreditCardClicked(GtkButton* button, + AutoFillDialog* dialog); + // 'changed' signal handler. We update the title of the expander widget with // the contents of the label entry widget. static void OnLabelChanged(GtkEntry* label, GtkWidget* expander); @@ -236,10 +264,14 @@ class AutoFillDialog { // Returns a GtkExpander that is added to the appropriate vbox. Each method // adds the necessary widgets and layout required to fill out information - // for either an address or a credit card. - GtkWidget* AddNewAddress(); + // for either an address or a credit card. The expander will be expanded by + // default if |expand| is true. + GtkWidget* AddNewAddress(bool expand); GtkWidget* AddNewCreditCard(); + // Adds a new address filled out with information from |profile|. + void AddAddress(const AutoFillProfile& profile); + // The list of current AutoFill profiles. std::vector<AutoFillProfile> profiles_; @@ -268,13 +300,18 @@ class AutoFillDialog { static AutoFillDialog* dialog = NULL; AutoFillDialog::AutoFillDialog(AutoFillDialogObserver* observer, - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards) - : profiles_(profiles), - credit_cards_(credit_cards), - observer_(observer) { + const std::vector<AutoFillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards) + : observer_(observer) { DCHECK(observer); + // Copy the profiles. + std::vector<AutoFillProfile*>::const_iterator profile; + for (profile = profiles.begin(); profile != profiles.end(); ++profile) + profiles_.push_back(**profile); + + // TODO(jhawkins): Copy the credit cards. + dialog_ = gtk_dialog_new_with_buttons( l10n_util::GetStringUTF8(IDS_AUTOFILL_DIALOG_TITLE).c_str(), // AutoFill dialog is shared between all browser windows. @@ -328,7 +365,9 @@ AutoFillDialog::AutoFillDialog(AutoFillDialogObserver* observer, G_CALLBACK(OnAddAddressClicked)); gtk_box_pack_start_defaults(GTK_BOX(outer_vbox), addresses_vbox_); - // TODO(jhawkins): Add addresses from |profiles|. + std::vector<AutoFillProfile>::const_iterator iter; + for (iter = profiles_.begin(); iter != profiles_.end(); ++iter) + AddAddress(*iter); creditcards_vbox_ = InitGroup(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME, IDS_AUTOFILL_ADD_CREDITCARD_BUTTON, @@ -352,10 +391,6 @@ void AutoFillDialog::OnDestroy(GtkWidget* widget, MessageLoop::current()->DeleteSoon(FROM_HERE, autofill_dialog); } -static string16 GetEntryText(GtkWidget* entry) { - return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(entry))); -} - static AutoFillProfile AutoFillProfileFromWidgetValues( const AddressWidgets& widgets) { // TODO(jhawkins): unique id? @@ -411,7 +446,8 @@ void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id, } autofill_dialog->observer_->OnAutoFillDialogApply( - autofill_dialog->profiles_, autofill_dialog->credit_cards_); + &autofill_dialog->profiles_, + &autofill_dialog->credit_cards_); } if (response_id == GTK_RESPONSE_OK || response_id == GTK_RESPONSE_CANCEL) { @@ -422,7 +458,7 @@ void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id, // static void AutoFillDialog::OnAddAddressClicked(GtkButton* button, AutoFillDialog* dialog) { - GtkWidget* new_address = dialog->AddNewAddress(); + GtkWidget* new_address = dialog->AddNewAddress(true); gtk_box_pack_start(GTK_BOX(dialog->addresses_vbox_), new_address, FALSE, FALSE, 0); gtk_widget_show_all(new_address); @@ -438,6 +474,50 @@ void AutoFillDialog::OnAddCreditCardClicked(GtkButton* button, } // static +void AutoFillDialog::OnDeleteAddressClicked(GtkButton* button, + AutoFillDialog* dialog) { + GtkWidget* entry = GetButtonData(GTK_WIDGET(button)); + string16 label = GetEntryText(entry); + + // TODO(jhawkins): Base this on ID. + + // Remove the profile. + for (std::vector<AutoFillProfile>::iterator iter = dialog->profiles_.begin(); + iter != dialog->profiles_.end(); + ++iter) { + if (iter->Label() == label) { + dialog->profiles_.erase(iter); + break; + } + } + + // Remove the set of address widgets. + for (std::vector<AddressWidgets>::iterator iter = + dialog->address_widgets_.begin(); + iter != dialog->address_widgets_.end(); + ++iter) { + if (iter->label == entry) { + dialog->address_widgets_.erase(iter); + break; + } + } + + // Get back to the expander widget. + GtkWidget* expander = gtk_widget_get_ancestor(GTK_WIDGET(button), + GTK_TYPE_EXPANDER); + DCHECK(expander); + + // Destroying the widget will also remove it from the parent container. + gtk_widget_destroy(expander); +} + +// static +void AutoFillDialog::OnDeleteCreditCardClicked(GtkButton* button, + AutoFillDialog* dialog) { + // TODO(jhawkins): Remove the associated credit card. +} + +// static void AutoFillDialog::OnLabelChanged(GtkEntry* label, GtkWidget* expander) { gtk_expander_set_label(GTK_EXPANDER(expander), gtk_entry_get_text(label)); } @@ -492,18 +572,17 @@ GtkWidget* AutoFillDialog::InitGroupContentArea(int name_id, gtk_container_add(GTK_CONTAINER(vbox_alignment), vbox); gtk_container_add(GTK_CONTAINER(frame), vbox_alignment); - // Make it expand by default. - gtk_expander_set_expanded(GTK_EXPANDER(expander), true); - *content_vbox = vbox; return expander; } -GtkWidget* AutoFillDialog::AddNewAddress() { +GtkWidget* AutoFillDialog::AddNewAddress(bool expand) { AddressWidgets widgets = {0}; GtkWidget* vbox; GtkWidget* address = InitGroupContentArea(IDS_AUTOFILL_NEW_ADDRESS, &vbox); + gtk_expander_set_expanded(GTK_EXPANDER(address), expand); + GtkWidget* table = InitFormTable(5, 3); gtk_box_pack_start_defaults(GTK_BOX(vbox), table); @@ -558,6 +637,8 @@ GtkWidget* AutoFillDialog::AddNewAddress() { GtkWidget* button = gtk_button_new_with_label( l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str()); + g_signal_connect(button, "clicked", G_CALLBACK(OnDeleteAddressClicked), this); + SetButtonData(button, widgets.label); GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0); gtk_container_add(GTK_CONTAINER(alignment), button); gtk_box_pack_start_defaults(GTK_BOX(vbox), alignment); @@ -637,12 +718,59 @@ GtkWidget* AutoFillDialog::AddNewCreditCard() { return credit_card; } +void AutoFillDialog::AddAddress(const AutoFillProfile& profile) { + GtkWidget* address = AddNewAddress(false); + gtk_expander_set_label(GTK_EXPANDER(address), + UTF16ToUTF8(profile.Label()).c_str()); + + // We just pushed the widgets to the back of the vector. + const AddressWidgets& widgets = address_widgets_.back(); + SetEntryText(widgets.label, profile.Label()); + SetEntryText(widgets.first_name, + profile.GetFieldText(AutoFillType(NAME_FIRST))); + SetEntryText(widgets.middle_name, + profile.GetFieldText(AutoFillType(NAME_MIDDLE))); + SetEntryText(widgets.last_name, + profile.GetFieldText(AutoFillType(NAME_LAST))); + SetEntryText(widgets.email, + profile.GetFieldText(AutoFillType(EMAIL_ADDRESS))); + SetEntryText(widgets.company_name, + profile.GetFieldText(AutoFillType(COMPANY_NAME))); + SetEntryText(widgets.address_line1, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))); + SetEntryText(widgets.address_line2, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))); + SetEntryText(widgets.city, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY))); + SetEntryText(widgets.state, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE))); + SetEntryText(widgets.zipcode, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP))); + SetEntryText(widgets.country, + profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY))); + SetEntryText(widgets.phone1, + profile.GetFieldText(AutoFillType(PHONE_HOME_COUNTRY_CODE))); + SetEntryText(widgets.phone2, + profile.GetFieldText(AutoFillType(PHONE_HOME_CITY_CODE))); + SetEntryText(widgets.phone3, + profile.GetFieldText(AutoFillType(PHONE_HOME_NUMBER))); + SetEntryText(widgets.fax1, + profile.GetFieldText(AutoFillType(PHONE_FAX_COUNTRY_CODE))); + SetEntryText(widgets.fax2, + profile.GetFieldText(AutoFillType(PHONE_FAX_CITY_CODE))); + SetEntryText(widgets.fax3, + profile.GetFieldText(AutoFillType(PHONE_FAX_NUMBER))); + + gtk_box_pack_start(GTK_BOX(addresses_vbox_), address, FALSE, FALSE, 0); + gtk_widget_show_all(address); +} + /////////////////////////////////////////////////////////////////////////////// // Factory/finder method: void ShowAutoFillDialog(AutoFillDialogObserver* observer, - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards) { + const std::vector<AutoFillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards) { if (!dialog) { dialog = new AutoFillDialog(observer, profiles, credit_cards); } diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 1fa94d8..2e067a9 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -10,7 +10,6 @@ #include "chrome/browser/autofill/autofill_dialog.h" #include "chrome/browser/autofill/autofill_infobar_delegate.h" #include "chrome/browser/autofill/form_structure.h" -#include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/chrome_switches.h" @@ -61,9 +60,24 @@ void AutoFillManager::FormFieldValuesSubmitted( } void AutoFillManager::OnAutoFillDialogApply( - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards) { - // TODO(jhawkins): Pass the profile data onto the PersonalDataManager. + std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards) { + // Save the personal data. + personal_data_->SetProfiles(profiles); + // TODO(jhawkins): Set the credit cards. + + HandleSubmit(); +} + +void AutoFillManager::OnPersonalDataLoaded() { + // We might have been alerted that the PersonalDataManager has loaded, so + // remove ourselves as observer. + personal_data_->RemoveObserver(this); + + // TODO(jhawkins): Actually send in the real credit cards from the personal + // data manager. + std::vector<CreditCard*> credit_cards; + ShowAutoFillDialog(this, personal_data_->profiles(), credit_cards); } void AutoFillManager::DeterminePossibleFieldTypes( @@ -93,13 +107,12 @@ void AutoFillManager::OnInfoBarAccepted() { PrefService* prefs = tab_contents_->profile()->GetPrefs(); prefs->SetBoolean(prefs::kAutoFillEnabled, true); - // TODO(jhawkins): Actually send in the real profiles and credit cards from - // the personal data manager. - std::vector<AutoFillProfile> profiles; - std::vector<CreditCard> credit_cards; - ShowAutoFillDialog(this, profiles, credit_cards); - - HandleSubmit(); + // If the personal data manager has not loaded the data yet, set ourselves as + // its observer so that we can listen for the OnPersonalDataLoaded signal. + if (!personal_data_->IsDataLoaded()) + personal_data_->SetObserver(this); + else + OnPersonalDataLoaded(); } void AutoFillManager::SaveFormData() { diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index eb53517..85905c8 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -9,6 +9,7 @@ #include "base/scoped_ptr.h" #include "chrome/browser/autofill/autofill_dialog.h" +#include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" namespace webkit_glue { @@ -19,14 +20,14 @@ class AutoFillInfoBarDelegate; class AutoFillProfile; class CreditCard; class FormStructure; -class PersonalDataManager; class PrefService; class TabContents; // Manages saving and restoring the user's personal information entered into web // forms. class AutoFillManager : public RenderViewHostDelegate::AutoFill, - AutoFillDialogObserver { + public AutoFillDialogObserver, + public PersonalDataManager::Observer { public: explicit AutoFillManager(TabContents* tab_contents); virtual ~AutoFillManager(); @@ -40,8 +41,11 @@ class AutoFillManager : public RenderViewHostDelegate::AutoFill, // AutoFillDialogObserver implementation: virtual void OnAutoFillDialogApply( - const std::vector<AutoFillProfile>& profiles, - const std::vector<CreditCard>& credit_cards); + std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards); + + // PersonalDataManager::Observer implementation: + virtual void OnPersonalDataLoaded(); // Uses heuristics and existing personal data to determine the possible field // types. diff --git a/chrome/browser/autofill/autofill_profile.cc b/chrome/browser/autofill/autofill_profile.cc index a292a41..bfc4fef 100644 --- a/chrome/browser/autofill/autofill_profile.cc +++ b/chrome/browser/autofill/autofill_profile.cc @@ -144,8 +144,9 @@ bool AutoFillProfile::operator==(const AutoFillProfile& profile) const { if (label_ != profile.label_ || unique_id_ != profile.unique_id_ || - use_billing_address_ != profile.use_billing_address_) + use_billing_address_ != profile.use_billing_address_) { return false; + } for (size_t index = 0; index < arraysize(types); ++index) { if (GetFieldText(AutoFillType(types[index])) != @@ -183,3 +184,39 @@ Address* AutoFillProfile::GetBillingAddress() { Address* AutoFillProfile::GetHomeAddress() { return static_cast<Address*>(personal_info_[AutoFillType::ADDRESS_HOME]); } + +// So we can compare AutoFillProfiles with EXPECT_EQ(). +std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile) { + return os + << UTF16ToASCII(profile.Label()) + << " " + << profile.unique_id() + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_MIDDLE))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(EMAIL_ADDRESS))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(COMPANY_NAME))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType( + PHONE_HOME_WHOLE_NUMBER))) + << " " + << UTF16ToUTF8(profile.GetFieldText(AutoFillType( + PHONE_FAX_WHOLE_NUMBER))); +} diff --git a/chrome/browser/autofill/autofill_profile.h b/chrome/browser/autofill/autofill_profile.h index a6ed0d3..acc7527 100644 --- a/chrome/browser/autofill/autofill_profile.h +++ b/chrome/browser/autofill/autofill_profile.h @@ -53,6 +53,7 @@ class AutoFillProfile : public FormGroup { void operator=(const AutoFillProfile&); // Used by tests. + // TODO(jhawkins): Move these to private and add the test as a friend. bool operator==(const AutoFillProfile& profile) const; void set_label(const string16& label) { label_ = label; } void set_unique_id(int id) { unique_id_ = id; } @@ -75,4 +76,7 @@ class AutoFillProfile : public FormGroup { FormGroupMap personal_info_; }; +// So we can compare AutoFillProfiles with EXPECT_EQ(). +std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile); + #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_PROFILE_H_ diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index c60aa6e..e2b2150 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -1,12 +1,18 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. #include "chrome/browser/autofill/personal_data_manager.h" +#include <algorithm> +#include <iterator> + +#include "base/logging.h" #include "chrome/browser/autofill/autofill_manager.h" #include "chrome/browser/autofill/autofill_field.h" #include "chrome/browser/autofill/form_structure.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/webdata/web_data_service.h" // The minimum number of fields that must contain user data and have known types // before autofill will attempt to import the data into a profile. @@ -19,6 +25,45 @@ static const int kPhoneNumberLength = 7; static const int kPhoneCityCodeLength = 3; PersonalDataManager::~PersonalDataManager() { + CancelPendingQuery(); +} + +void PersonalDataManager::OnWebDataServiceRequestDone( + WebDataService::Handle h, + const WDTypedResult* result) { + // Error from the web database. + if (!result) + return; + + DCHECK(pending_query_handle_); + DCHECK(result->GetType() == AUTOFILL_PROFILES_RESULT); + pending_query_handle_ = 0; + + unique_ids_.clear(); + profiles_.reset(); + const WDResult<std::vector<AutoFillProfile*> >* r = + static_cast<const WDResult<std::vector<AutoFillProfile*> >*>(result); + std::vector<AutoFillProfile*> profiles = r->GetValue(); + for (std::vector<AutoFillProfile*>::iterator iter = profiles.begin(); + iter != profiles.end(); ++iter) { + unique_ids_.insert((*iter)->unique_id()); + profiles_.push_back(*iter); + } + + is_data_loaded_ = true; + if (observer_) + observer_->OnPersonalDataLoaded(); +} + +void PersonalDataManager::SetObserver(PersonalDataManager::Observer* observer) { + DCHECK(observer_ == NULL); + observer_ = observer; +} + +void PersonalDataManager::RemoveObserver( + PersonalDataManager::Observer* observer) { + if (observer_ == observer) + observer_ = NULL; } bool PersonalDataManager::ImportFormData( @@ -31,7 +76,7 @@ bool PersonalDataManager::ImportFormData( int importable_fields = 0; int importable_credit_card_fields = 0; imported_profile_.reset(new AutoFillProfile(string16(), - CreateNextUniqueId())); + CreateNextUniqueID())); imported_credit_card_.reset(new CreditCard(string16())); bool billing_address_info = false; @@ -104,6 +149,63 @@ bool PersonalDataManager::ImportFormData( return true; } +void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) { + if (profile_->IsOffTheRecord()) + return; + + WebDataService* wds = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); + if (!wds) + return; + + // Remove the unique IDs of the new set of profiles from the unique ID set. + for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); + iter != profiles->end(); ++iter) { + if (iter->unique_id() != 0) + unique_ids_.erase(iter->unique_id()); + } + + // Any remaining IDs are not in the new profile list and should be removed + // from the web database. + for (std::set<int>::iterator iter = unique_ids_.begin(); + iter != unique_ids_.end(); ++iter) { + wds->RemoveAutoFillProfile(*iter); + } + + // Clear the unique IDs. The set of unique IDs is updated for each profile + // added to |profile_| below. + unique_ids_.clear(); + + // Update the web database with the existing profiles. We need to handle + // these first so that |unique_ids_| is reset with the IDs of the existing + // profiles; otherwise, new profiles added before older profiles can take + // their unique ID. + for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); + iter != profiles->end(); ++iter) { + if (iter->unique_id() != 0) { + unique_ids_.insert(iter->unique_id()); + wds->UpdateAutoFillProfile(*iter); + } + } + + // Add the new profiles to the web database. + for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); + iter != profiles->end(); ++iter) { + // Profile was added by the AutoFill dialog, so we need to set the unique + // ID. This also means we need to add this profile to the web DB. + if (iter->unique_id() == 0) { + iter->set_unique_id(CreateNextUniqueID()); + wds->AddAutoFillProfile(*iter); + } + } + + profiles_.reset(); + for (std::vector<AutoFillProfile>::iterator iter = profiles->begin(); + iter != profiles->end(); ++iter) { + profiles_.push_back(new AutoFillProfile(*iter)); + } +} + + void PersonalDataManager::GetPossibleFieldTypes(const string16& text, FieldTypeSet* possible_types) { InitializeIfNeeded(); @@ -115,8 +217,8 @@ void PersonalDataManager::GetPossibleFieldTypes(const string16& text, return; } - ScopedVector<FormGroup>::const_iterator iter; - for (iter = profiles_.begin(); iter != profiles_.end(); ++iter) { + for (ScopedVector<AutoFillProfile>::iterator iter = profiles_.begin(); + iter != profiles_.end(); ++iter) { const FormGroup* profile = *iter; if (!profile) { DLOG(ERROR) << "NULL information in profiles list"; @@ -125,7 +227,8 @@ void PersonalDataManager::GetPossibleFieldTypes(const string16& text, profile->GetPossibleFieldTypes(clean_info, possible_types); } - for (iter = credit_cards_.begin(); iter != credit_cards_.end(); ++iter) { + for (ScopedVector<FormGroup>::iterator iter = credit_cards_.begin(); + iter != credit_cards_.end(); ++iter) { const FormGroup* credit_card = *iter; if (!credit_card) { DLOG(ERROR) << "NULL information in credit cards list"; @@ -143,8 +246,13 @@ bool PersonalDataManager::HasPassword() { return !password_hash_.empty(); } -PersonalDataManager::PersonalDataManager() - : is_initialized_(false) { +PersonalDataManager::PersonalDataManager(Profile* profile) + : profile_(profile), + is_initialized_(false), + is_data_loaded_(false), + pending_query_handle_(0), + observer_(NULL) { + LoadProfiles(); } void PersonalDataManager::InitializeIfNeeded() { @@ -155,7 +263,7 @@ void PersonalDataManager::InitializeIfNeeded() { // TODO(jhawkins): Load data. } -int PersonalDataManager::CreateNextUniqueId() { +int PersonalDataManager::CreateNextUniqueID() { // Profile IDs MUST start at 1 to allow 0 as an error value when reading // the ID from the WebDB (see LoadData()). int id = 1; @@ -190,3 +298,29 @@ void PersonalDataManager::ParsePhoneNumber( // Treat any remaining digits as the country code. profile->SetInfo(AutoFillType(country_code), *value); } + +void PersonalDataManager::LoadProfiles() { + WebDataService* web_data_service = + profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); + if (!web_data_service) { + NOTREACHED(); + return; + } + + CancelPendingQuery(); + + pending_query_handle_ = web_data_service->GetAutoFillProfiles(this); +} + +void PersonalDataManager::CancelPendingQuery() { + if (pending_query_handle_) { + WebDataService* web_data_service = + profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); + if (!web_data_service) { + NOTREACHED(); + return; + } + web_data_service->CancelRequest(pending_query_handle_); + } + pending_query_handle_ = 0; +} diff --git a/chrome/browser/autofill/personal_data_manager.h b/chrome/browser/autofill/personal_data_manager.h index c28c24b..5821a2a 100644 --- a/chrome/browser/autofill/personal_data_manager.h +++ b/chrome/browser/autofill/personal_data_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -14,17 +14,41 @@ #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/credit_card.h" #include "chrome/browser/autofill/field_types.h" +#include "chrome/browser/webdata/web_data_service.h" class AutoFillManager; class FormStructure; +class Profile; // Handles loading and saving AutoFill profile information to the web database. // This class also stores the profiles loaded from the database for use during // AutoFill. -class PersonalDataManager { +class PersonalDataManager : public WebDataServiceConsumer { public: + // An interface the PersonalDataManager uses to notify its clients (observers) + // when it has finished loading personal data from the web database. Register + // the observer via PersonalDataManager::SetObserver. + class Observer { + public: + // Notifies the observer that the PersonalDataManager has finished loading. + virtual void OnPersonalDataLoaded() = 0; + + protected: + virtual ~Observer() {} + }; + virtual ~PersonalDataManager(); + // WebDataServiceConsumer implementation: + virtual void OnWebDataServiceRequestDone(WebDataService::Handle h, + const WDTypedResult* result); + + // Sets the listener to be notified of PersonalDataManager events. + void SetObserver(PersonalDataManager::Observer* observer); + + // Removes |observer| as the observer of this PersonalDataManager. + void RemoveObserver(PersonalDataManager::Observer* observer); + // If AutoFill is able to determine the field types of a significant number // of field types that contain information in the FormStructures and the user // has not previously been prompted, the user will be asked if he would like @@ -33,6 +57,10 @@ class PersonalDataManager { bool ImportFormData(const std::vector<FormStructure*>& form_structures, AutoFillManager* autofill_manager); + // Sets |profiles_| to the contents of |profiles| and updates the web database + // by adding, updating and removing profiles. + void SetProfiles(std::vector<AutoFillProfile>* profiles); + // Gets the possible field types for the given text, determined by matching // the text with all known personal information and returning matching types. void GetPossibleFieldTypes(const string16& text, @@ -41,19 +69,31 @@ class PersonalDataManager { // Returns true if the credit card information is stored with a password. bool HasPassword(); + // Returns whether the personal data has been loaded from the web database. + bool IsDataLoaded() const { return is_data_loaded_; } + + // This PersonalDataManager owns these profiles. Their lifetime is until the + // web database is updated with new profile information. + const std::vector<AutoFillProfile*>& profiles() { return profiles_.get(); } + private: - // Make sure that only Profile can create an instance of PersonalDataManager. + // Make sure that only Profile and the PersonalDataManager tests can create an + // instance of PersonalDataManager. friend class ProfileImpl; + friend class PersonalDataManagerTest; + + explicit PersonalDataManager(Profile* profile); - PersonalDataManager(); + // Returns the profile of the tab contents. + Profile* profile(); // Initializes the object if needed. This should be called at the beginning // of all the public functions to make sure that the object has been properly // initialized before use. void InitializeIfNeeded(); - // This will create and reserve a new unique id for a profile. - int CreateNextUniqueId(); + // This will create and reserve a new unique ID for a profile. + int CreateNextUniqueID(); // Parses value to extract the components of a phone number and adds them to // profile. @@ -65,14 +105,26 @@ class PersonalDataManager { AutoFillFieldType city_code, AutoFillFieldType country_code) const; + // Loads the saved profiles from the web database. + void LoadProfiles(); + + // Cancels a pending query to the web database. + void CancelPendingQuery(); + + // The profile hosting this PersonalDataManager. + Profile* profile_; + // True if PersonalDataManager is initialized. bool is_initialized_; + // True if personal data has been loaded from the web database. + bool is_data_loaded_; + // The set of already created unique IDs, used to create a new unique ID. std::set<int> unique_ids_; // The loaded profiles. - ScopedVector<FormGroup> profiles_; + ScopedVector<AutoFillProfile> profiles_; // The loaded credit cards. ScopedVector<FormGroup> credit_cards_; @@ -86,6 +138,16 @@ class PersonalDataManager { // The hash of the password used to store the credit card. This is empty if // no password exists. string16 password_hash_; + + // When the manager makes a request from WebDataService, the database + // is queried on another thread, we record the query handle until we + // get called back. + WebDataService::Handle pending_query_handle_; + + // The observer. This can be NULL. + Observer* observer_; + + DISALLOW_COPY_AND_ASSIGN(PersonalDataManager); }; #endif // CHROME_BROWSER_AUTOFILL_PERSONAL_DATA_MANAGER_H_ diff --git a/chrome/browser/autofill/personal_data_manager_unittest.cc b/chrome/browser/autofill/personal_data_manager_unittest.cc new file mode 100644 index 0000000..5bcbb08 --- /dev/null +++ b/chrome/browser/autofill/personal_data_manager_unittest.cc @@ -0,0 +1,176 @@ +// 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. + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/autofill/autofill_profile.h" +#include "chrome/browser/autofill/personal_data_manager.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer_mock.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/test/testing_profile.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +ACTION(QuitUIMessageLoop) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + MessageLoop::current()->Quit(); +} + +class PersonalDataLoadedObserverMock : public PersonalDataManager::Observer { + public: + PersonalDataLoadedObserverMock() {} + virtual ~PersonalDataLoadedObserverMock() {} + + MOCK_METHOD0(OnPersonalDataLoaded, void()); +}; + +class PersonalDataManagerTest : public testing::Test { + protected: + PersonalDataManagerTest() + : ui_thread_(ChromeThread::UI, &message_loop_), + db_thread_(ChromeThread::DB) { + } + + virtual void SetUp() { + db_thread_.Start(); + + profile_.reset(new TestingProfile); + profile_->CreateWebDataService(false); + + ResetPersonalDataManager(); + } + + virtual void TearDown() { + if (profile_.get()) + profile_.reset(NULL); + + db_thread_.Stop(); + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); + MessageLoop::current()->Run(); + } + + void ResetPersonalDataManager() { + personal_data_.reset(new PersonalDataManager(profile_.get())); + personal_data_->SetObserver(&personal_data_observer_); + } + + static void SetProfileInfo(AutoFillProfile* profile, + const char* label, const char* first_name, const char* middle_name, + const char* last_name, const char* email, const char* company, + const char* address1, const char* address2, const char* city, + const char* state, const char* zipcode, const char* country, + const char* phone, const char* fax) { + profile->set_label(ASCIIToUTF16(label)); + profile->SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16(first_name)); + profile->SetInfo(AutoFillType(NAME_MIDDLE), ASCIIToUTF16(middle_name)); + profile->SetInfo(AutoFillType(NAME_LAST), ASCIIToUTF16(last_name)); + profile->SetInfo(AutoFillType(EMAIL_ADDRESS), ASCIIToUTF16(email)); + profile->SetInfo(AutoFillType(COMPANY_NAME), ASCIIToUTF16(company)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), ASCIIToUTF16(address1)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), ASCIIToUTF16(address2)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), ASCIIToUTF16(city)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), ASCIIToUTF16(state)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), ASCIIToUTF16(zipcode)); + profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16(country)); + profile->SetInfo(AutoFillType(PHONE_HOME_NUMBER), ASCIIToUTF16(phone)); + profile->SetInfo(AutoFillType(PHONE_FAX_NUMBER), ASCIIToUTF16(fax)); + } + + MessageLoopForUI message_loop_; + ChromeThread ui_thread_; + ChromeThread db_thread_; + scoped_ptr<TestingProfile> profile_; + scoped_ptr<PersonalDataManager> personal_data_; + NotificationRegistrar registrar_; + NotificationObserverMock observer_; + PersonalDataLoadedObserverMock personal_data_observer_; +}; + +// TODO(jhawkins): Test SaveProfiles w/out a WebDataService in the profile. +TEST_F(PersonalDataManagerTest, SetProfiles) { + AutoFillProfile profile0(string16(), 0); + SetProfileInfo(&profile0, "Billing", "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910", "01987654321"); + + AutoFillProfile profile1(string16(), 0); + SetProfileInfo(&profile1, "Home", "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801", + "US", "19482937549", "13502849239"); + + AutoFillProfile profile2(string16(), 0); + SetProfileInfo(&profile2, "Work", "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", + "32801", "US", "19482937549", "13502849239"); + + // This will verify that the web database has been loaded and the notification + // sent out. + EXPECT_CALL(personal_data_observer_, + OnPersonalDataLoaded()).WillOnce(QuitUIMessageLoop()); + + // The message loop will exit when the mock observer is notified. + MessageLoop::current()->Run(); + + // Add the three test profiles to the database. + std::vector<AutoFillProfile> update; + update.push_back(profile0); + update.push_back(profile1); + personal_data_->SetProfiles(&update); + + // The PersonalDataManager will update the unique IDs when saving the + // profiles, so we have to update the expectations. + profile0.set_unique_id(update[0].unique_id()); + profile1.set_unique_id(update[1].unique_id()); + + const std::vector<AutoFillProfile*>& results1 = personal_data_->profiles(); + ASSERT_EQ(2U, results1.size()); + EXPECT_EQ(profile0, *results1.at(0)); + EXPECT_EQ(profile1, *results1.at(1)); + + // Three operations in one: + // - Update profile0 + // - Remove profile1 + // - Add profile2 + profile0.SetInfo(AutoFillType(NAME_FIRST), ASCIIToUTF16("John")); + update.clear(); + update.push_back(profile0); + update.push_back(profile2); + personal_data_->SetProfiles(&update); + + // Set the expected unique ID for profile2. + profile2.set_unique_id(update[1].unique_id()); + + // AutoFillProfile IDs are re-used, so the third profile to be added will have + // a unique ID that matches the old unique ID of the removed profile1, even + // though that ID has already been used. + const std::vector<AutoFillProfile*>& results2 = personal_data_->profiles(); + ASSERT_EQ(2U, results2.size()); + EXPECT_EQ(profile0, *results2.at(0)); + EXPECT_EQ(profile2, *results2.at(1)); + EXPECT_EQ(profile2.unique_id(), profile1.unique_id()); + + // Reset the PersonalDataManager. This tests that the personal data was saved + // to the web database, and that we can load the profiles from the web + // database. + ResetPersonalDataManager(); + + // This will verify that the web database has been loaded and the notification + // sent out. + EXPECT_CALL(personal_data_observer_, + OnPersonalDataLoaded()).WillOnce(QuitUIMessageLoop()); + + // The message loop will exit when the PersonalDataLoadedObserver is notified. + MessageLoop::current()->Run(); + + // Verify that we've loaded the profiles from the web database. + const std::vector<AutoFillProfile*>& results3 = personal_data_->profiles(); + ASSERT_EQ(2U, results3.size()); + EXPECT_EQ(profile0, *results3.at(0)); + EXPECT_EQ(profile2, *results3.at(1)); +} diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 8c9e9c7..a6861e9 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -1095,7 +1095,7 @@ bool ProfileImpl::HasCreatedDownloadManager() const { PersonalDataManager* ProfileImpl::GetPersonalDataManager() { if (!personal_data_manager_.get()) { - personal_data_manager_.reset(new PersonalDataManager); + personal_data_manager_.reset(new PersonalDataManager(this)); } return personal_data_manager_.get(); } diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc index 3601b8b..b630a91 100644 --- a/chrome/browser/webdata/web_data_service.cc +++ b/chrome/browser/webdata/web_data_service.cc @@ -160,10 +160,10 @@ void WebDataService::UpdateAutoFillProfile(const AutoFillProfile& profile) { request)); } -void WebDataService::RemoveAutoFillProfile(const AutoFillProfile& profile) { - GenericRequest<AutoFillProfile>* request = - new GenericRequest<AutoFillProfile>( - this, GetNextRequestHandle(), NULL, profile); +void WebDataService::RemoveAutoFillProfile(int profile_id) { + GenericRequest<int>* request = + new GenericRequest<int>( + this, GetNextRequestHandle(), NULL, profile_id); RegisterRequest(request); ScheduleTask(NewRunnableMethod(this, &WebDataService::RemoveAutoFillProfileImpl, @@ -183,6 +183,18 @@ WebDataService::Handle WebDataService::GetAutoFillProfileForLabel( return request->GetHandle(); } +WebDataService::Handle WebDataService::GetAutoFillProfiles( + WebDataServiceConsumer* consumer) { + WebDataRequest* request = + new WebDataRequest(this, GetNextRequestHandle(), consumer); + RegisterRequest(request); + ScheduleTask( + NewRunnableMethod(this, + &WebDataService::GetAutoFillProfilesImpl, + request)); + return request->GetHandle(); +} + void WebDataService::RequestCompleted(Handle h) { pending_lock_.Acquire(); RequestMap::iterator i = pending_requests_.find(h); @@ -472,10 +484,21 @@ void WebDataService::InitializeDatabaseIfNecessary() { return; } + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &WebDataService::NotifyDatabaseLoadedOnUIThread)); + db_ = db; db_->BeginTransaction(); } +void WebDataService::NotifyDatabaseLoadedOnUIThread() { + // Notify that the database has been initialized. + NotificationService::current()->Notify(NotificationType::WEB_DATABASE_LOADED, + NotificationService::AllSources(), + NotificationService::NoDetails()); +} + void WebDataService::ShutdownDatabase() { should_commit_ = false; @@ -734,11 +757,11 @@ void WebDataService::UpdateAutoFillProfileImpl( } void WebDataService::RemoveAutoFillProfileImpl( - GenericRequest<AutoFillProfile>* request) { + GenericRequest<int>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - const AutoFillProfile& profile = request->GetArgument(); - if (!db_->RemoveAutoFillProfile(profile)) + int profile_id = request->GetArgument(); + if (!db_->RemoveAutoFillProfile(profile_id)) NOTREACHED(); ScheduleCommit(); } @@ -758,6 +781,18 @@ void WebDataService::GetAutoFillProfileForLabelImpl(WebDataRequest* request, request->RequestComplete(); } +void WebDataService::GetAutoFillProfilesImpl(WebDataRequest* request) { + InitializeDatabaseIfNecessary(); + if (db_ && !request->IsCancelled()) { + std::vector<AutoFillProfile*> profiles; + db_->GetAutoFillProfiles(&profiles); + request->SetResult( + new WDResult<std::vector<AutoFillProfile*> >(AUTOFILL_PROFILES_RESULT, + profiles)); + } + request->RequestComplete(); +} + //////////////////////////////////////////////////////////////////////////////// // // Web Apps implementation. diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h index 9ab3c01..ff356e0 100644 --- a/chrome/browser/webdata/web_data_service.h +++ b/chrome/browser/webdata/web_data_service.h @@ -54,17 +54,18 @@ struct PasswordForm; // Result types // typedef enum { - BOOL_RESULT = 1, // WDResult<bool> - KEYWORDS_RESULT, // WDResult<WDKeywordsResult> - INT64_RESULT, // WDResult<int64> - PASSWORD_RESULT, // WDResult<std::vector<PasswordForm*>> + BOOL_RESULT = 1, // WDResult<bool> + KEYWORDS_RESULT, // WDResult<WDKeywordsResult> + INT64_RESULT, // WDResult<int64> + PASSWORD_RESULT, // WDResult<std::vector<PasswordForm*>> #if defined(OS_WIN) - PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo> + PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo> #endif - WEB_APP_IMAGES, // WDResult<WDAppImagesResult> - AUTOFILL_VALUE_RESULT, // WDResult<std::vector<string16>> - AUTOFILL_CHANGES, // WDResult<std::vector<AutofillChange>> - AUTOFILL_PROFILE_RESULT // WDResult<AutoFillProfile> + WEB_APP_IMAGES, // WDResult<WDAppImagesResult> + AUTOFILL_VALUE_RESULT, // WDResult<std::vector<string16>> + AUTOFILL_CHANGES, // WDResult<std::vector<AutofillChange>> + AUTOFILL_PROFILE_RESULT, // WDResult<AutoFillProfile> + AUTOFILL_PROFILES_RESULT // WDResult<std::vector<AutoFillProfile*>> } WDResultType; typedef std::vector<AutofillChange> AutofillChangeList; @@ -85,7 +86,7 @@ struct WDKeywordsResult { // Identifies the ID of the TemplateURL that is the default search. A value of // 0 indicates there is no default search provider. int64 default_search_provider_id; - // Version of the builin keywords. A value of 0 indicates a first run. + // Version of the built-in keywords. A value of 0 indicates a first run. int builtin_keyword_version; }; @@ -414,14 +415,21 @@ class WebDataService void UpdateAutoFillProfile(const AutoFillProfile& profile); // Schedules a task to remove an AutoFill profile from the web database. - void RemoveAutoFillProfile(const AutoFillProfile& profile); + // |profile_id| is the unique ID of the profile to remove. + void RemoveAutoFillProfile(int profile_id); - // Initiates the request for an AutoFill profile with label |label. The + // Initiates the request for an AutoFill profile with label |label|. The // method OnWebDataServiceRequestDone of |consumer| gets called back when the // request is finished, with the profile included in the argument |result|. Handle GetAutoFillProfileForLabel(const string16& label, WebDataServiceConsumer* consumer); + // Initiates the request for all AutoFill profiles. The method + // OnWebDataServiceRequestDone of |consumer| gets called when the request is + // finished, with the profiles included in the argument |result|. The + // consumer owns the profiles. + Handle GetAutoFillProfiles(WebDataServiceConsumer* consumer); + // Testing #ifdef UNIT_TEST void set_failed_init(bool value) { failed_init_ = value; } @@ -464,6 +472,9 @@ class WebDataService // Initialize the database, if it hasn't already been initialized. void InitializeDatabaseIfNecessary(); + // The notification method. + void NotifyDatabaseLoadedOnUIThread(); + // Commit any pending transaction and deletes the database. void ShutdownDatabase(); @@ -516,9 +527,10 @@ class WebDataService GenericRequest2<string16, string16>* request); void AddAutoFillProfileImpl(GenericRequest<AutoFillProfile>* request); void UpdateAutoFillProfileImpl(GenericRequest<AutoFillProfile>* request); - void RemoveAutoFillProfileImpl(GenericRequest<AutoFillProfile>* request); + void RemoveAutoFillProfileImpl(GenericRequest<int>* request); void GetAutoFillProfileForLabelImpl(WebDataRequest* request, const string16& label); + void GetAutoFillProfilesImpl(WebDataRequest* request); ////////////////////////////////////////////////////////////////////////////// // @@ -591,6 +603,9 @@ class WebDataServiceConsumer { // not be opened. The result object is destroyed after this call. virtual void OnWebDataServiceRequestDone(WebDataService::Handle h, const WDTypedResult* result) = 0; + + protected: + virtual ~WebDataServiceConsumer() {} }; #endif // CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__ diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc index 58068e8..019c7d3 100644 --- a/chrome/browser/webdata/web_database.cc +++ b/chrome/browser/webdata/web_database.cc @@ -1227,11 +1227,45 @@ bool WebDatabase::AddAutoFillProfile(const AutoFillProfile& profile) { return false; } - return true; + return s.Succeeded(); +} + +static AutoFillProfile* AutoFillProfileFromStatement(const sql::Statement& s) { + AutoFillProfile* profile = new AutoFillProfile( + ASCIIToUTF16(s.ColumnString(0)), s.ColumnInt(1)); + profile->SetInfo(AutoFillType(NAME_FIRST), + ASCIIToUTF16(s.ColumnString(2))); + profile->SetInfo(AutoFillType(NAME_MIDDLE), + ASCIIToUTF16(s.ColumnString(3))); + profile->SetInfo(AutoFillType(NAME_LAST), + ASCIIToUTF16(s.ColumnString(4))); + profile->SetInfo(AutoFillType(EMAIL_ADDRESS), + ASCIIToUTF16(s.ColumnString(5))); + profile->SetInfo(AutoFillType(COMPANY_NAME), + ASCIIToUTF16(s.ColumnString(6))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), + ASCIIToUTF16(s.ColumnString(7))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), + ASCIIToUTF16(s.ColumnString(8))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), + ASCIIToUTF16(s.ColumnString(9))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), + ASCIIToUTF16(s.ColumnString(10))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), + ASCIIToUTF16(s.ColumnString(11))); + profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), + ASCIIToUTF16(s.ColumnString(12))); + profile->SetInfo(AutoFillType(PHONE_HOME_NUMBER), + ASCIIToUTF16(s.ColumnString(13))); + profile->SetInfo(AutoFillType(PHONE_FAX_NUMBER), + ASCIIToUTF16(s.ColumnString(14))); + + return profile; } bool WebDatabase::GetAutoFillProfileForLabel(const string16& label, AutoFillProfile** profile) { + DCHECK(profile); sql::Statement s(db_.GetUniqueStatement( "SELECT * FROM autofill_profiles " "WHERE label = ?")); @@ -1244,36 +1278,26 @@ bool WebDatabase::GetAutoFillProfileForLabel(const string16& label, if (!s.Step()) return false; - *profile = new AutoFillProfile(label, s.ColumnInt(1)); - AutoFillProfile* profile_ptr = *profile; - profile_ptr->SetInfo(AutoFillType(NAME_FIRST), - ASCIIToUTF16(s.ColumnString(2))); - profile_ptr->SetInfo(AutoFillType(NAME_MIDDLE), - ASCIIToUTF16(s.ColumnString(3))); - profile_ptr->SetInfo(AutoFillType(NAME_LAST), - ASCIIToUTF16(s.ColumnString(4))); - profile_ptr->SetInfo(AutoFillType(EMAIL_ADDRESS), - ASCIIToUTF16(s.ColumnString(5))); - profile_ptr->SetInfo(AutoFillType(COMPANY_NAME), - ASCIIToUTF16(s.ColumnString(6))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), - ASCIIToUTF16(s.ColumnString(7))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), - ASCIIToUTF16(s.ColumnString(8))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_CITY), - ASCIIToUTF16(s.ColumnString(9))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_STATE), - ASCIIToUTF16(s.ColumnString(10))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), - ASCIIToUTF16(s.ColumnString(11))); - profile_ptr->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), - ASCIIToUTF16(s.ColumnString(12))); - profile_ptr->SetInfo(AutoFillType(PHONE_HOME_NUMBER), - ASCIIToUTF16(s.ColumnString(13))); - profile_ptr->SetInfo(AutoFillType(PHONE_FAX_NUMBER), - ASCIIToUTF16(s.ColumnString(14))); + *profile = AutoFillProfileFromStatement(s); - return true; + return s.Succeeded(); +} + +bool WebDatabase::GetAutoFillProfiles( + std::vector<AutoFillProfile*>* profiles) { + DCHECK(profiles); + profiles->clear(); + + sql::Statement s(db_.GetUniqueStatement("SELECT * FROM autofill_profiles")); + if (!s) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + while (s.Step()) + profiles->push_back(AutoFillProfileFromStatement(s)); + + return s.Succeeded(); } bool WebDatabase::UpdateAutoFillProfile(const AutoFillProfile& profile) { @@ -1294,8 +1318,8 @@ bool WebDatabase::UpdateAutoFillProfile(const AutoFillProfile& profile) { return s.Run(); } -bool WebDatabase::RemoveAutoFillProfile(const AutoFillProfile& profile) { - DCHECK(profile.unique_id()); +bool WebDatabase::RemoveAutoFillProfile(int profile_id) { + DCHECK_NE(0, profile_id); sql::Statement s(db_.GetUniqueStatement( "DELETE FROM autofill_profiles WHERE unique_id = ?")); if (!s) { @@ -1303,7 +1327,7 @@ bool WebDatabase::RemoveAutoFillProfile(const AutoFillProfile& profile) { return false; } - s.BindInt(0, profile.unique_id()); + s.BindInt(0, profile_id); return s.Run(); } diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h index 70eb235..2820beb1 100644 --- a/chrome/browser/webdata/web_database.h +++ b/chrome/browser/webdata/web_database.h @@ -204,13 +204,17 @@ class WebDatabase { // Updates the database values for the specified profile. bool UpdateAutoFillProfile(const AutoFillProfile& profile); - // Removes a row from the autofill_profiles table. - bool RemoveAutoFillProfile(const AutoFillProfile& profile); + // Removes a row from the autofill_profiles table. |profile_id| is the + // unique ID of the profile to remove. + bool RemoveAutoFillProfile(int profile_id); // Retrieves a profile with label |label|. The caller owns |profile|. bool GetAutoFillProfileForLabel(const string16& label, AutoFillProfile** profile); + // Retrieves all profiles in the database. Caller owns the returned profiles. + bool GetAutoFillProfiles(std::vector<AutoFillProfile*>* profiles); + ////////////////////////////////////////////////////////////////////////////// // // Web Apps diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc index d086b1c..c0f340d 100644 --- a/chrome/browser/webdata/web_database_unittest.cc +++ b/chrome/browser/webdata/web_database_unittest.cc @@ -52,40 +52,6 @@ std::ostream& operator<<(std::ostream& os, const AutofillChange& change) { return os << " " << change.key(); } -// So we can compare AutoFillProfiles with EXPECT_EQ(). -std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile) { - return os - << UTF16ToASCII(profile.Label()) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_MIDDLE))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(EMAIL_ADDRESS))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(COMPANY_NAME))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType( - PHONE_HOME_WHOLE_NUMBER))) - << " " - << UTF16ToUTF8(profile.GetFieldText(AutoFillType( - PHONE_FAX_WHOLE_NUMBER))); -} - class WebDatabaseTest : public testing::Test { protected: typedef std::vector<AutofillChange> AutofillChangeList; @@ -963,7 +929,7 @@ TEST_F(WebDatabaseTest, AutoFillProfile) { delete db_profile; // Remove the 'Billing' profile. - EXPECT_TRUE(db.RemoveAutoFillProfile(billing_profile)); + EXPECT_TRUE(db.RemoveAutoFillProfile(billing_profile.unique_id())); EXPECT_FALSE(db.GetAutoFillProfileForLabel(ASCIIToUTF16("Billing"), &db_profile)); } |