summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/autofill')
-rw-r--r--chrome/browser/autofill/autofill_dialog.cc4
-rw-r--r--chrome/browser/autofill/autofill_dialog.h15
-rw-r--r--chrome/browser/autofill/autofill_dialog_gtk.cc172
-rw-r--r--chrome/browser/autofill/autofill_manager.cc37
-rw-r--r--chrome/browser/autofill/autofill_manager.h14
-rw-r--r--chrome/browser/autofill/autofill_profile.cc39
-rw-r--r--chrome/browser/autofill/autofill_profile.h4
-rw-r--r--chrome/browser/autofill/personal_data_manager.cc150
-rw-r--r--chrome/browser/autofill/personal_data_manager.h76
-rw-r--r--chrome/browser/autofill/personal_data_manager_unittest.cc176
10 files changed, 625 insertions, 62 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));
+}