diff options
author | alyssad@chromium.org <alyssad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-09 18:22:56 +0000 |
---|---|---|
committer | alyssad@chromium.org <alyssad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-09 18:22:56 +0000 |
commit | 55846ad8432f07ebc6fce2fafadffe60a5bab246 (patch) | |
tree | 502750d292fb9b9b184dc7671ad2ff4bd563302a | |
parent | 756ef54543f64b1295ac6c93df4c7fb9e62ab435 (diff) | |
download | chromium_src-55846ad8432f07ebc6fce2fafadffe60a5bab246.zip chromium_src-55846ad8432f07ebc6fce2fafadffe60a5bab246.tar.gz chromium_src-55846ad8432f07ebc6fce2fafadffe60a5bab246.tar.bz2 |
New pyauto hook to set and get autofill profiles
Review URL: http://codereview.chromium.org/2836046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51984 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 259 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 40 | ||||
-rw-r--r-- | chrome/test/functional/PYAUTO_TESTS | 1 | ||||
-rw-r--r-- | chrome/test/functional/autofill.py | 53 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 106 |
5 files changed, 458 insertions, 1 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 7edc7c3..cc9aecd 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -29,6 +29,7 @@ #include "chrome/app/chrome_version_info.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/app_modal_dialog_queue.h" +#include "chrome/browser/autofill/autofill_manager.h" #include "chrome/browser/automation/automation_extension_tracker.h" #include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/automation/automation_provider_observers.h" @@ -2345,6 +2346,260 @@ void AutomationProvider::SaveTabContents(Browser* browser, Send(reply_message); } +// Sample json input: +// { "command": "GetAutoFillProfile" } +// Refer to GetAutoFillProfile() in chrome/test/pyautolib/pyauto.py for sample +// json output. +void AutomationProvider::GetAutoFillProfile(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + std::string json_return; + bool reply_return = true; + + // Get the AutoFillProfiles currently in the database. + int tab_index = 0; + args->GetInteger(L"tab_index", &tab_index); + TabContents* tab_contents = browser->GetTabContentsAt(tab_index); + + if (tab_contents) { + PersonalDataManager* pdm = tab_contents->profile()->GetOriginalProfile() + ->GetPersonalDataManager(); + if (pdm) { + std::vector<AutoFillProfile*> autofill_profiles = pdm->profiles(); + std::vector<CreditCard*> credit_cards = pdm->credit_cards(); + + ListValue* profiles = GetListFromAutoFillProfiles(autofill_profiles); + ListValue* cards = GetListFromCreditCards(credit_cards); + + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + + return_value->Set(L"profiles", profiles); + return_value->Set(L"credit_cards", cards); + + base::JSONWriter::Write(return_value.get(), false, &json_return); + } else { + json_return = JSONErrorString("No PersonalDataManager."); + reply_return = false; + } + } else { + json_return = JSONErrorString("No tab at that index."); + reply_return = false; + } + + AutomationMsg_SendJSONRequest::WriteReplyParams(reply_message, json_return, + reply_return); + Send(reply_message); +} + +// Refer to FillAutoFillProfile() in chrome/test/pyautolib/pyauto.py for sample +// json input. +// Sample json output: {} +void AutomationProvider::FillAutoFillProfile(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + std::string json_return = "{}"; + bool reply_return = true; + + ListValue* profiles = NULL; + ListValue* cards = NULL; + args->GetList(L"profiles", &profiles); + args->GetList(L"credit_cards", &cards); + + std::vector<AutoFillProfile> autofill_profiles; + std::vector<CreditCard> credit_cards; + // Create an AutoFillProfile for each of the dictionary profiles. + if (profiles) { + autofill_profiles = GetAutoFillProfilesFromList(*profiles, &json_return); + } + // Create a CreditCard for each of the dictionary values. + if (cards) { + credit_cards = GetCreditCardsFromList(*cards, &json_return); + } + + // Save the AutoFillProfiles. + int tab_index = 0; + args->GetInteger(L"tab_index", &tab_index); + TabContents* tab_contents = browser->GetTabContentsAt(tab_index); + + if (tab_contents) { + PersonalDataManager* pdm = tab_contents->profile()->GetOriginalProfile() + ->GetPersonalDataManager(); + if (pdm) { + pdm->OnAutoFillDialogApply(profiles? &autofill_profiles : NULL, + cards? &credit_cards : NULL); + } else { + json_return = JSONErrorString("No PersonalDataManager."); + reply_return = false; + } + } else { + json_return = JSONErrorString("No tab at that index."); + reply_return = false; + } + AutomationMsg_SendJSONRequest::WriteReplyParams( + reply_message, json_return, reply_return); + Send(reply_message); +} + +/* static */ +ListValue* AutomationProvider::GetListFromAutoFillProfiles( + std::vector<AutoFillProfile*> autofill_profiles) { + ListValue* profiles = new ListValue; + + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string + = GetAutoFillFieldToStringMap(); + + // For each AutoFillProfile, transform it to a dictionary object to return. + for (std::vector<AutoFillProfile*>::iterator it = autofill_profiles.begin(); + it != autofill_profiles.end(); ++it) { + AutoFillProfile* profile = *it; + DictionaryValue* profile_info = new DictionaryValue; + profile_info->SetStringFromUTF16(L"label", profile->Label()); + // For each of the types, if it has a value, add it to the dictionary. + for (std::map<AutoFillFieldType, std::wstring>::iterator + type_it = autofill_type_to_string.begin(); + type_it != autofill_type_to_string.end(); ++type_it) { + string16 value = profile->GetFieldText(AutoFillType(type_it->first)); + if (value.length()) { // If there was something stored for that value. + profile_info->SetStringFromUTF16(type_it->second, value); + } + } + profiles->Append(profile_info); + } + return profiles; +} + +/* static */ +ListValue* AutomationProvider::GetListFromCreditCards( + std::vector<CreditCard*> credit_cards) { + ListValue* cards = new ListValue; + + std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string = + GetCreditCardFieldToStringMap(); + + // For each AutoFillProfile, transform it to a dictionary object to return. + for (std::vector<CreditCard*>::iterator it = credit_cards.begin(); + it != credit_cards.end(); ++it) { + CreditCard* card = *it; + DictionaryValue* card_info = new DictionaryValue; + card_info->SetStringFromUTF16(L"label", card->Label()); + // For each of the types, if it has a value, add it to the dictionary. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + credit_card_type_to_string.begin(); + type_it != credit_card_type_to_string.end(); ++type_it) { + string16 value = card->GetFieldText(AutoFillType(type_it->first)); + // If there was something stored for that value. + if (value.length()) { + card_info->SetStringFromUTF16(type_it->second, value); + } + } + cards->Append(card_info); + } + return cards; +} + +/* static */ +std::vector<AutoFillProfile> AutomationProvider::GetAutoFillProfilesFromList( + const ListValue& profiles, std::string* json_return) { + std::vector<AutoFillProfile> autofill_profiles; + DictionaryValue* profile_info = NULL; + string16 profile_label; + string16 current_value; + + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string = + GetAutoFillFieldToStringMap(); + + int num_profiles = profiles.GetSize(); + for (int i = 0; i < num_profiles; i++) { + profiles.GetDictionary(i, &profile_info); + profile_info->GetString("label", &profile_label); + // Choose an id of 0 so that a unique id will be created. + AutoFillProfile profile(profile_label, 0); + // Loop through the possible profile types and add those provided. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + autofill_type_to_string.begin(); + type_it != autofill_type_to_string.end(); ++type_it) { + if (profile_info->HasKey(type_it->second)) { + if (profile_info->GetStringAsUTF16(type_it->second, ¤t_value)) { + profile.SetInfo(AutoFillType(type_it->first), current_value); + } else { + *json_return = JSONErrorString("All values must be strings"); + break; + } + } + } + autofill_profiles.push_back(profile); + } + return autofill_profiles; +} + +/* static */ +std::vector<CreditCard> AutomationProvider::GetCreditCardsFromList( + const ListValue& cards, std::string* json_return) { + std::vector<CreditCard> credit_cards; + DictionaryValue* card_info = NULL; + string16 card_label; + string16 current_value; + + std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string = + GetCreditCardFieldToStringMap(); + + int num_credit_cards = cards.GetSize(); + for (int i = 0; i < num_credit_cards; i++) { + cards.GetDictionary(i, &card_info); + card_info->GetString("label", &card_label); + CreditCard card(card_label, 0); + // Loop through the possible credit card fields and add those provided. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + credit_card_type_to_string.begin(); + type_it != credit_card_type_to_string.end(); ++type_it) { + if (card_info->HasKey(type_it->second)) { + if (card_info->GetStringAsUTF16(type_it->second, ¤t_value)) { + card.SetInfo(AutoFillType(type_it->first), current_value); + } else { + *json_return = JSONErrorString("All values must be strings"); + break; + } + } + } + credit_cards.push_back(card); + } + return credit_cards; +} + +/* static */ +std::map<AutoFillFieldType, std::wstring> + AutomationProvider::GetAutoFillFieldToStringMap() { + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string; + autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST"; + autofill_type_to_string[NAME_MIDDLE] = L"NAME_MIDDLE"; + autofill_type_to_string[NAME_LAST] = L"NAME_LAST"; + autofill_type_to_string[COMPANY_NAME] = L"COMPANY_NAME"; + autofill_type_to_string[EMAIL_ADDRESS] = L"EMAIL_ADDRESS"; + autofill_type_to_string[ADDRESS_HOME_LINE1] = L"ADDRESS_HOME_LINE1"; + autofill_type_to_string[ADDRESS_HOME_LINE2] = L"ADDRESS_HOME_LINE2"; + autofill_type_to_string[ADDRESS_HOME_CITY] = L"ADDRESS_HOME_CITY"; + autofill_type_to_string[ADDRESS_HOME_STATE] = L"ADDRESS_HOME_STATE"; + autofill_type_to_string[ADDRESS_HOME_ZIP] = L"ADDRESS_HOME_ZIP"; + autofill_type_to_string[ADDRESS_HOME_COUNTRY] = L"ADDRESS_HOME_COUNTRY"; + autofill_type_to_string[PHONE_HOME_NUMBER] = L"PHONE_HOME_NUMBER"; + autofill_type_to_string[PHONE_FAX_NUMBER] = L"PHONE_FAX_NUMBER"; + autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST"; + return autofill_type_to_string; +} + +/* static */ +std::map<AutoFillFieldType, std::wstring> + AutomationProvider::GetCreditCardFieldToStringMap() { + std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string; + credit_card_type_to_string[CREDIT_CARD_NAME] = L"CREDIT_CARD_NAME"; + credit_card_type_to_string[CREDIT_CARD_NUMBER] = L"CREDIT_CARD_NUMBER"; + credit_card_type_to_string[CREDIT_CARD_TYPE] = L"CREDIT_CARD_TYPE"; + credit_card_type_to_string[CREDIT_CARD_EXP_MONTH] = L"CREDIT_CARD_EXP_MONTH"; + credit_card_type_to_string[CREDIT_CARD_EXP_4_DIGIT_YEAR] = + L"CREDIT_CARD_EXP_4_DIGIT_YEAR"; + return credit_card_type_to_string; +} + /* static */ std::string AutomationProvider::JSONErrorString(const std::string& err) { std::string prefix = "{\"error\": \""; @@ -2399,7 +2654,6 @@ void AutomationProvider::SendJSONRequest(int handle, handler_map["GetPluginsInfo"] = &AutomationProvider::GetPluginsInfo; handler_map["GetBrowserInfo"] = &AutomationProvider::GetBrowserInfo; - handler_map["GetHistoryInfo"] = &AutomationProvider::GetHistoryInfo; handler_map["AddHistoryItem"] = &AutomationProvider::AddHistoryItem; @@ -2422,6 +2676,9 @@ void AutomationProvider::SendJSONRequest(int handle, handler_map["SaveTabContents"] = &AutomationProvider::SaveTabContents; + handler_map["GetAutoFillProfile"] = &AutomationProvider::GetAutoFillProfile; + handler_map["FillAutoFillProfile"] = &AutomationProvider::FillAutoFillProfile; + if (error_string.empty()) { if (handler_map.find(std::string(command)) != handler_map.end()) { (this->*handler_map[command])(browser, dict_value, reply_message); diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 3727bd8..5361695 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -18,6 +18,7 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" #include "base/values.h" +#include "chrome/browser/autofill/field_types.h" #include "chrome/browser/automation/automation_autocomplete_edit_tracker.h" #include "chrome/browser/automation/automation_browser_tracker.h" #include "chrome/browser/automation/automation_resource_message_filter.h" @@ -43,7 +44,9 @@ struct Reposition_Params; struct ExternalTabSettings; } +class AutoFillProfile; class AutomationExtensionTracker; +class CreditCard; class Extension; class ExtensionPortContainer; class ExtensionTestResultNotificationObserver; @@ -438,6 +441,43 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, DictionaryValue* args, IPC::Message* reply_message); + // Get the profiles that are currently saved to the DB. + // Uses the JSON interface for input/output. + void GetAutoFillProfile(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Fill in an AutoFillProfile with the given profile information. + // Uses the JSON interface for input/output. + void FillAutoFillProfile(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + + // Translate DictionaryValues of autofill profiles and credit cards to the + // data structure used in the browser. + // Args: + // profiles/cards: the ListValue of profiles/credit cards to translate. + // json_return: a pointer to the return string in case of error. + static std::vector<AutoFillProfile> GetAutoFillProfilesFromList( + const ListValue& profiles, std::string* json_return); + static std::vector<CreditCard> GetCreditCardsFromList( + const ListValue& cards, std::string* json_return); + + // The opposite of the above: translates from the internal data structure + // for profiles and credit cards to a ListValue of DictionaryValues. The + // caller owns the returned object. + static ListValue* GetListFromAutoFillProfiles( + std::vector<AutoFillProfile*> autofill_profiles); + static ListValue* GetListFromCreditCards( + std::vector<CreditCard*> credit_cards); + + // Return the map from the internal data representation to the string value + // of auto fill fields and credit card fields. + static std::map<AutoFillFieldType, std::wstring> + GetAutoFillFieldToStringMap(); + static std::map<AutoFillFieldType, std::wstring> + GetCreditCardFieldToStringMap(); + // Util for creating a JSON error return string (dict with key // 'error' and error string value). No need to quote input. static std::string JSONErrorString(const std::string& err); diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS index 87c70c3..85772d5 100644 --- a/chrome/test/functional/PYAUTO_TESTS +++ b/chrome/test/functional/PYAUTO_TESTS @@ -21,6 +21,7 @@ { 'all': [ + 'autofill', 'bookmark_bar', 'bookmarks', 'browser', diff --git a/chrome/test/functional/autofill.py b/chrome/test/functional/autofill.py new file mode 100644 index 0000000..0c68c22 --- /dev/null +++ b/chrome/test/functional/autofill.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import pyauto_functional # Must be imported before pyauto +import pyauto + + +class AutoFillTest(pyauto.PyUITest): + """Tests that autofill works correctly""" + + def testFillProfile(self): + """Test filling profiles and overwriting with new profiles""" + profiles = [{'label': 'Profile 1', 'NAME_FIRST': 'Bob', + 'NAME_LAST': 'Smith', 'ADDRESS_HOME_ZIP': '94043',}, + {'label': 'Profile 2', 'EMAIL_ADDRESS': 'sue@example.com', + 'COMPANY_NAME': 'Company X',}] + credit_cards = [{'label': 'Credit Card 1', + 'CREDIT_CARD_NUMBER': '6011111111111117', + 'CREDIT_CARD_EXP_MONTH': '12', + 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2011'}, + {'label': 'Credit Card 2', + 'CREDIT_CARD_NAME': 'Bob C. Smith', + 'CREDIT_CARD_TYPE': 'Visa'}] + + self.FillAutoFillProfile(profiles=profiles, credit_cards=credit_cards) + profile = self.GetAutoFillProfile() + self.assertEqual(profiles, profile['profiles']) + self.assertEqual(credit_cards, profile['credit_cards']) + + profiles = [ {'label': 'Profile3', 'NAME_FIRST': 'Larry'}] + self.FillAutoFillProfile(profiles=profiles) + profile = self.GetAutoFillProfile() + self.assertEqual(profiles, profile['profiles']) + self.assertEqual(credit_cards, profile['credit_cards']) + + def testFillProfileUnicode(self): + """Test filling profiles with unicode strings""" + profiles = [{'label': u'unic\u00F3de', 'NAME_FIRST': u'J\u00E4n', + 'ADDRESS_HOME_LINE1': u'123 R\u00F6d'}] + self.FillAutoFillProfile(profiles) + self.assertEqual(profiles, self.GetAutoFillProfile()['profiles']) + + def testGetProfilesEmpty(self): + """Test getting profiles when none have been filled""" + profile = self.GetAutoFillProfile() + self.assertEqual([], profile['profiles']) + self.assertEqual([], profile['credit_cards']) + + +if __name__ == '__main__': + pyauto_functional.Main() diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 661b7c3..e2c64c5 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -551,6 +551,112 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): return history_info.HistoryInfo( self._SendJSONRequest(0, json.dumps(cmd_dict))) + def FillAutoFillProfile(self, profiles=None, credit_cards=None, + tab_index=0, window_index=0): + """Set the autofill profile to contain the given profiles and credit cards. + + If profiles or credit_cards are specified, they will overwrite existing + profiles and credit cards. To update profiles and credit cards, get the + existing ones with the GetAutoFillProfile function and then append new + profiles to the list and call this function. + + Args: + profiles: (optional) a list of dictionaries representing each profile to + add. Example: + [{ + 'label': 'Profile 1', # Note: 'label' must be present + 'NAME_FIRST': 'Bob', + 'NAME_LAST': 'Smith', + 'ADDRESS_HOME_ZIP': '94043', + }, + { + 'label': 'Profile 2', + 'EMAIL_ADDRESS': 'sue@example.com', + 'COMPANY_NAME': 'Company X', + }] + + Each dictionary must have a key 'label'. Other possible keys are: + 'NAME_FIRST', 'NAME_MIDDLE', 'NAME_LAST', 'EMAIL_ADDRESS', + 'COMPANY_NAME', 'ADDRESS_HOME_LINE1', 'ADDRESS_HOME_LINE2', + 'ADDRESS_HOME_CITY', 'ADDRESS_HOME_STATE', 'ADDRESS_HOME_ZIP', + 'ADDRESS_HOME_COUNTRY', 'PHONE_HOME_NUMBER', 'PHONE_FAX_NUMBER' + + All values must be strings. + + credit_cards: (optional) a list of dictionaries representing each credit + card to add. Example: + [{ + 'label': 'Personal Credit Card', # Note: 'label' must be present + 'CREDIT_CARD_NAME': 'Bob C. Smith', + 'CREDIT_CARD_NUMBER': '5555555555554444', + 'CREDIT_CARD_EXP_MONTH': '12', + 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2011' + }, + { + 'label': 'Work Credit Card', + 'CREDIT_CARD_NAME': 'Bob C. Smith', + 'CREDIT_CARD_NUMBER': '4111111111111111', + 'CREDIT_CARD_TYPE': 'Visa' + } + + Each dictionary must have a key 'label'. Other possible keys are: + 'CREDIT_CARD_NAME', 'CREDIT_CARD_NUMBER', 'CREDIT_CARD_TYPE', + 'CREDIT_CARD_EXP_MONTH', 'CREDIT_CARD_EXP_4_DIGIT_YEAR' + + All values must be strings. + + tab_index: tab index, defaults to 0. + + window_index: window index, defaults to 0. + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { # Prepare command for the json interface + 'command': 'FillAutoFillProfile', + 'tab_index': tab_index, + 'profiles': profiles, + 'credit_cards': credit_cards + } + if profiles: + for profile in profiles: + if not 'label' in profile: + raise JSONInterfaceError('must specify label for all profiles') + if credit_cards: + for credit_card in credit_cards: + if not 'label' in credit_card: + raise JSONInterfaceError('must specify label for all credit cards') + ret_dict = json.loads(self._SendJSONRequest(window_index, + json.dumps(cmd_dict))) + if ret_dict.has_key('error'): + raise JSONInterfaceError(ret_dict['error']) + + def GetAutoFillProfile(self, tab_index=0, window_index=0): + """Return the profile including all profiles and credit cards currently + saved as a list of dictionaries. + + The format of the returned dictionary is described above in + FillAutoFillProfile. The general format is: + {'profiles': [list of profile dictionaries as described above], + 'credit_cards': [list of credit card dictionaries as described above]} + + Args: + tab_index: tab index, defaults to 0. + window_index: window index, defaults to 0. + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { # Prepare command for the json interface + 'command': 'GetAutoFillProfile', + 'tab_index': tab_index + } + ret_dict = json.loads(self._SendJSONRequest(window_index, + json.dumps(cmd_dict))) + if ret_dict.has_key('error'): + raise JSONInterfaceError(ret_dict['error']) + return ret_dict + def AddHistoryItem(self, item): """Forge a history item for Chrome. |