diff options
-rw-r--r-- | chrome/browser/autofill/autofill_browsertest.cc | 89 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 331 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 52 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager_unittest.cc | 4 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.cc | 15 | ||||
-rw-r--r-- | chrome/renderer/autofill/autofill_agent.cc | 39 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager.cc | 579 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager.h | 118 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager_browsertest.cc | 553 |
9 files changed, 809 insertions, 971 deletions
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc index 22603c9..16bdb70 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc @@ -431,6 +431,95 @@ IN_PROC_BROWSER_TEST_F(AutofillTest, AutofillFormWithRepeatedField) { ExpectFieldValue(L"state_freeform", ""); } +// Test that we can Autofill dynamically generated forms. +IN_PROC_BROWSER_TEST_F(AutofillTest, DynamicFormFill) { + CreateTestProfile(); + + // Load the test page. + ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); + ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(), + GURL(std::string(kDataURIPrefix) + + "<form id=\"form\" action=\"http://www.example.com/\"" + " method=\"POST\"></form>" + "<script>" + "function AddElement(name, label) {" + " var form = document.getElementById('form');" + "" + " var label_text = document.createTextNode(label);" + " var label_element = document.createElement('label');" + " label_element.setAttribute('for', name);" + " label_element.appendChild(label_text);" + " form.appendChild(label_element);" + "" + " if (name === 'state' || name === 'country') {" + " var select_element = document.createElement('select');" + " select_element.setAttribute('id', name);" + " select_element.setAttribute('name', name);" + "" + " /* Add an empty selected option. */" + " var default_option = new Option('--', '', true);" + " select_element.appendChild(default_option);" + "" + " /* Add the other options. */" + " if (name == 'state') {" + " var option1 = new Option('California', 'CA');" + " select_element.appendChild(option1);" + " var option2 = new Option('Texas', 'TX');" + " select_element.appendChild(option2);" + " } else {" + " var option1 = new Option('Canada', 'CA');" + " select_element.appendChild(option1);" + " var option2 = new Option('United States', 'US');" + " select_element.appendChild(option2);" + " }" + "" + " form.appendChild(select_element);" + " } else {" + " var input_element = document.createElement('input');" + " input_element.setAttribute('id', name);" + " input_element.setAttribute('name', name);" + "" + " /* Add the onFocus listener to the 'firstname' field. */" + " if (name === 'firstname') {" + " input_element.setAttribute(" + " 'onFocus', 'domAutomationController.send(true)');" + " }" + "" + " form.appendChild(input_element);" + " }" + "" + " form.appendChild(document.createElement('br'));" + "};" + "" + "function BuildForm() {" + " var elements = [" + " ['firstname', 'First name:']," + " ['lastname', 'Last name:']," + " ['address1', 'Address line 1:']," + " ['address2', 'Address line 2:']," + " ['city', 'City:']," + " ['state', 'State:']," + " ['zip', 'ZIP code:']," + " ['country', 'Country:']," + " ['phone', 'Phone number:']," + " ];" + "" + " for (var i = 0; i < elements.length; i++) {" + " var name = elements[i][0];" + " var label = elements[i][1];" + " AddElement(name, label);" + " }" + "};" + "</script>"))); + + // Dynamically construct the form. + ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(render_view_host(), L"", + L"BuildForm();")); + + // Invoke Autofill. + TryBasicFormFill(); +} + // Test that form filling works after reloading the current page. // This test brought to you by http://crbug.com/69204 #if defined(OS_MACOSX) diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 97d68b1..1542d8b 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -67,6 +67,10 @@ const double kAutofillNegativeUploadRateDefaultValue = 0.20; const size_t kMaxRecentFormSignaturesToRemember = 3; +// Set a conservative upper bound on the number of forms we are willing to +// cache, simply to prevent unbounded memory consumption. +const size_t kMaxFormCacheSize = 100; + const string16::value_type kCreditCardPrefix[] = {'*', 0}; // Removes duplicate suggestions whilst preserving their original order. @@ -106,36 +110,12 @@ void RemoveDuplicateSuggestions(std::vector<string16>* values, bool SectionIsAutofilled(const FormStructure* form_structure, const webkit_glue::FormData& form, const string16& section) { - // TODO(isherman): It would be nice to share most of this code with the loop - // in |OnFillAutofillFormData()|, but I don't see a particularly clean way to - // do that. - - // The list of fields in |form_structure| and |form.fields| often match - // directly and we can fill these corresponding fields; however, when the - // |form_structure| and |form.fields| do not match directly we search - // ahead in the |form_structure| for the matching field. - for (size_t i = 0, j = 0; - i < form_structure->field_count() && j < form.fields.size(); - j++) { - size_t k = i; - - // Search forward in the |form_structure| for a corresponding field. - while (k < form_structure->field_count() && - (form_structure->field(k)->section() != section || - *form_structure->field(k) != form.fields[j])) { - k++; - } - - // If we didn't find a match, continue on to the next |form| field. - if (k >= form_structure->field_count()) - continue; - - if (form.fields[j].is_autofilled) + DCHECK_EQ(form_structure->field_count(), form.fields.size()); + for (size_t i = 0; i < form_structure->field_count(); ++i) { + if (form_structure->field(i)->section() == section && + form.fields[i].is_autofilled) { return true; - - // We found a matching field in the |form_structure|, so on the next - // iteration we should proceed to the next |form_structure| field. - ++i; + } } return false; @@ -368,7 +348,7 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form, const TimeTicks& timestamp) { FormStructure* form_structure = NULL; AutofillField* autofill_field = NULL; - if (!FindCachedFormAndField(form, field, &form_structure, &autofill_field)) + if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field)) return; if (!user_did_type_) { @@ -405,7 +385,7 @@ void AutofillManager::OnQueryFormFieldAutofill( AutofillField* autofill_field = NULL; if (GetHost( personal_data_->profiles(), personal_data_->credit_cards(), &host) && - FindCachedFormAndField(form, field, &form_structure, &autofill_field) && + GetCachedFormAndField(form, field, &form_structure, &autofill_field) && // Don't send suggestions for forms that aren't auto-fillable. form_structure->IsAutofillable(false)) { AutofillFieldType type = autofill_field->type(); @@ -481,52 +461,22 @@ void AutofillManager::OnFillAutofillFormData(int query_id, int unique_id) { const std::vector<AutofillProfile*>& profiles = personal_data_->profiles(); const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards(); + const AutofillProfile* profile = NULL; + const CreditCard* credit_card = NULL; + size_t variant = 0; RenderViewHost* host = NULL; FormStructure* form_structure = NULL; AutofillField* autofill_field = NULL; - if (!GetHost(profiles, credit_cards, &host) || - !FindCachedFormAndField(form, field, &form_structure, &autofill_field)) + if (!GetProfileOrCreditCard(unique_id, profiles, credit_cards, &profile, + &credit_card, &variant) || + !GetHost(profiles, credit_cards, &host) || + !GetCachedFormAndField(form, field, &form_structure, &autofill_field)) return; DCHECK(host); DCHECK(form_structure); DCHECK(autofill_field); - - // Unpack the |unique_id| into component parts. - GUIDPair cc_guid; - GUIDPair profile_guid; - UnpackGUIDs(unique_id, &cc_guid, &profile_guid); - DCHECK(!guid::IsValidGUID(cc_guid.first) || - !guid::IsValidGUID(profile_guid.first)); - - // Find the profile that matches the |profile_id|, if one is specified. - const AutofillProfile* profile = NULL; - if (guid::IsValidGUID(profile_guid.first)) { - for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin(); - iter != profiles.end(); ++iter) { - if ((*iter)->guid() == profile_guid.first) { - profile = *iter; - break; - } - } - DCHECK(profile); - } - - // Find the credit card that matches the |cc_id|, if one is specified. - const CreditCard* credit_card = NULL; - if (guid::IsValidGUID(cc_guid.first)) { - for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin(); - iter != credit_cards.end(); ++iter) { - if ((*iter)->guid() == cc_guid.first) { - credit_card = *iter; - break; - } - } - DCHECK(credit_card); - } - - if (!profile && !credit_card) - return; + DCHECK(profile || credit_card); FormData result = form; @@ -540,8 +490,7 @@ void AutofillManager::OnFillAutofillFormData(int query_id, if (profile) { DCHECK_NE(AutofillType::CREDIT_CARD, AutofillType(field_type).group()); - FillFormField(*profile, *autofill_field, profile_guid.second, - &(*iter)); + FillFormField(*profile, *autofill_field, variant, &(*iter)); } else { DCHECK_EQ(AutofillType::CREDIT_CARD, AutofillType(field_type).group()); @@ -560,30 +509,14 @@ void AutofillManager::OnFillAutofillFormData(int query_id, return; } - // The list of fields in |form_structure| and |result.fields| often match - // directly and we can fill these corresponding fields; however, when the - // |form_structure| and |result.fields| do not match directly we search - // ahead in the |form_structure| for the matching field. - // See unit tests: AutofillManagerTest.FormChangesRemoveField and - // AutofillManagerTest.FormChangesAddField for usage. - for (size_t i = 0, j = 0; - i < form_structure->field_count() && j < result.fields.size(); - j++) { - size_t k = i; - - // Search forward in the |form_structure| for a corresponding field. - while (k < form_structure->field_count() && - (form_structure->field(k)->section() != autofill_field->section() || - *form_structure->field(k) != result.fields[j])) { - k++; - } - - // If we've found a match then fill the |result| field with the found - // field in the |form_structure|. - if (k >= form_structure->field_count()) + DCHECK_EQ(form_structure->field_count(), form.fields.size()); + for (size_t i = 0; i < form_structure->field_count(); ++i) { + if (form_structure->field(i)->section() != autofill_field->section()) continue; - const AutofillField* cached_field = form_structure->field(k); + DCHECK_EQ(*form_structure->field(i), result.fields[i]); + + const AutofillField* cached_field = form_structure->field(i); AutofillFieldType field_type = cached_field->type(); FieldTypeGroup field_group_type = AutofillType(field_type).group(); if (field_group_type != AutofillType::NO_GROUP) { @@ -592,25 +525,20 @@ void AutofillManager::OnFillAutofillFormData(int query_id, // If the field being filled is the field that the user initiated the // fill from, then take the multi-profile "variant" into account. // Otherwise fill with the default (zeroth) variant. - if (result.fields[j] == field) { - FillFormField(*profile, *cached_field, profile_guid.second, - &result.fields[j]); + if (result.fields[i] == field) { + FillFormField(*profile, *cached_field, variant, &result.fields[i]); } else { - FillFormField(*profile, *cached_field, 0, &result.fields[j]); + FillFormField(*profile, *cached_field, 0, &result.fields[i]); } } else { DCHECK_EQ(AutofillType::CREDIT_CARD, field_group_type); - FillCreditCardFormField(*credit_card, field_type, &result.fields[j]); + FillCreditCardFormField(*credit_card, field_type, &result.fields[i]); } // Mark the cached field as autofilled, so that we can detect when a user // edits an autofilled field (for metrics). - form_structure->field(k)->is_autofilled = true; + form_structure->field(i)->is_autofilled = true; } - - // We found a matching field in the |form_structure|, so on the next - // iteration we should proceed to the next |form_structure| field. - ++i; } autofilled_form_signatures_.push_front(form_structure->FormSignature()); @@ -679,16 +607,7 @@ void AutofillManager::OnLoadedServerPredictions( *metric_logger_); // If the corresponding flag is set, annotate forms with the predicted types. - RenderViewHost* host = tab_contents()->render_view_host(); - if (!CommandLine::ForCurrentProcess()->HasSwitch( - switches::kShowAutofillTypePredictions) || !host) { - return; - } - - std::vector<FormDataPredictions> forms; - FormStructure::GetFieldTypePredictions(form_structures_.get(), &forms); - host->Send(new AutofillMsg_FieldTypePredictionsAvailable(host->routing_id(), - forms)); + SendAutofillTypePredictions(form_structures_.get()); } void AutofillManager::OnUploadedPossibleFieldTypes() { @@ -735,6 +654,22 @@ void AutofillManager::DeterminePossibleFieldTypesForUpload( } } +void AutofillManager::SendAutofillTypePredictions( + const std::vector<FormStructure*>& forms) const { + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kShowAutofillTypePredictions)) + return; + + RenderViewHost* host = tab_contents()->render_view_host(); + if (!host) + return; + + std::vector<FormDataPredictions> type_predictions; + FormStructure::GetFieldTypePredictions(forms, &type_predictions); + host->Send(new AutofillMsg_FieldTypePredictionsAvailable(host->routing_id(), + type_predictions)); +} + void AutofillManager::ImportFormData(const FormStructure& submitted_form) { const CreditCard* imported_credit_card; if (!personal_data_->ImportFormData(submitted_form, &imported_credit_card)) @@ -824,13 +759,63 @@ bool AutofillManager::GetHost(const std::vector<AutofillProfile*>& profiles, return true; } +bool AutofillManager::GetProfileOrCreditCard( + int unique_id, + const std::vector<AutofillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards, + const AutofillProfile** profile, + const CreditCard** credit_card, + size_t* variant) const { + // Unpack the |unique_id| into component parts. + GUIDPair credit_card_guid; + GUIDPair profile_guid; + UnpackGUIDs(unique_id, &credit_card_guid, &profile_guid); + DCHECK(!guid::IsValidGUID(credit_card_guid.first) || + !guid::IsValidGUID(profile_guid.first)); + + // Find the profile that matches the |profile_guid|, if one is specified. + if (guid::IsValidGUID(profile_guid.first)) { + for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin(); + iter != profiles.end(); ++iter) { + if ((*iter)->guid() == profile_guid.first) { + *profile = *iter; + break; + } + } + DCHECK(*profile); + + *variant = profile_guid.second; + } + + // Find the credit card that matches the |credit_card_guid|, if specified. + if (guid::IsValidGUID(credit_card_guid.first)) { + for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin(); + iter != credit_cards.end(); ++iter) { + if ((*iter)->guid() == credit_card_guid.first) { + *credit_card = *iter; + break; + } + } + DCHECK(*credit_card); + + *variant = credit_card_guid.second; + } + + return (*profile) || (*credit_card); +} + bool AutofillManager::FindCachedForm(const FormData& form, FormStructure** form_structure) const { // Find the FormStructure that corresponds to |form|. + // Scan backward through the cached |form_structures_|, as updated versions of + // forms are added to the back of the list, whereas original versions of these + // forms might appear toward the beginning of the list. The communication + // protocol with the crowdsourcing server does not permit us to discard the + // original versions of the forms. *form_structure = NULL; - for (std::vector<FormStructure*>::const_iterator iter = - form_structures_.begin(); - iter != form_structures_.end(); ++iter) { + for (std::vector<FormStructure*>::const_reverse_iterator iter = + form_structures_.rbegin(); + iter != form_structures_.rend(); ++iter) { if (**iter == form) { *form_structure = *iter; break; @@ -843,13 +828,22 @@ bool AutofillManager::FindCachedForm(const FormData& form, return true; } -bool AutofillManager::FindCachedFormAndField(const FormData& form, - const FormField& field, - FormStructure** form_structure, - AutofillField** autofill_field) { +bool AutofillManager::GetCachedFormAndField(const FormData& form, + const FormField& field, + FormStructure** form_structure, + AutofillField** autofill_field) { // Find the FormStructure that corresponds to |form|. - if (!FindCachedForm(form, form_structure)) + // If we do not have this form in our cache but it is parseable, we'll add it + // in the call to |UpdateCachedForm()|. + if (!FindCachedForm(form, form_structure) && + (form_structures_.size() >= kMaxFormCacheSize || + !FormStructure(form).ShouldBeParsed(false))) { return false; + } + + // Update the cached form to reflect any dynamic changes to the form data, if + // necessary. + UpdateCachedForm(form, *form_structure, form_structure); // No data to return if there are no auto-fillable fields. if (!(*form_structure)->autofill_count()) @@ -866,19 +860,66 @@ bool AutofillManager::FindCachedFormAndField(const FormData& form, } } - if (!(*autofill_field)) - return false; - + // We always update the cache, so we should be guaranteed to find the field. + DCHECK(*autofill_field); return true; } -void AutofillManager::GetProfileSuggestions(FormStructure* form, - const FormField& field, - AutofillFieldType type, - std::vector<string16>* values, - std::vector<string16>* labels, - std::vector<string16>* icons, - std::vector<int>* unique_ids) { +void AutofillManager::UpdateCachedForm(const FormData& live_form, + const FormStructure* cached_form, + FormStructure** updated_form) { + bool needs_update = + (!cached_form || + live_form.fields.size() != cached_form->field_count()); + for (size_t i = 0; !needs_update && i < cached_form->field_count(); ++i) { + needs_update = *cached_form->field(i) != live_form.fields[i]; + } + + if (!needs_update) + return; + + // Add the new or updated form to our cache. + DCHECK(form_structures_.size() < kMaxFormCacheSize); + form_structures_.push_back(new FormStructure(live_form)); + *updated_form = *form_structures_.rbegin(); + (*updated_form)->DetermineHeuristicTypes(); + + // If we have cached data, propagate it to the updated form. + if (cached_form) { + std::map<string16, const AutofillField*> cached_fields; + for (size_t i = 0; i < cached_form->field_count(); ++i) { + const AutofillField* field = cached_form->field(i); + cached_fields[field->unique_name()] = field; + } + + for (size_t i = 0; i < (*updated_form)->field_count(); ++i) { + AutofillField* field = (*updated_form)->field(i); + std::map<string16, const AutofillField*>::iterator cached_field = + cached_fields.find(field->unique_name()); + if (cached_field != cached_fields.end()) { + field->set_server_type(cached_field->second->server_type()); + field->is_autofilled = cached_field->second->is_autofilled; + } + } + + // Note: We _must not_ remove the original version of the cached form from + // the list of |form_structures_|. Otherwise, we break parsing of the + // crowdsourcing server's response to our query. + } + + // Annotate the updated form with its predicted types. + std::vector<FormStructure*> forms(1, *updated_form); + SendAutofillTypePredictions(forms); +} + +void AutofillManager::GetProfileSuggestions( + FormStructure* form, + const FormField& field, + AutofillFieldType type, + std::vector<string16>* values, + std::vector<string16>* labels, + std::vector<string16>* icons, + std::vector<int>* unique_ids) const { const std::vector<AutofillProfile*>& profiles = personal_data_->profiles(); if (!field.is_autofilled) { std::vector<AutofillProfile*> matched_profiles; @@ -959,13 +1000,14 @@ void AutofillManager::GetProfileSuggestions(FormStructure* form, } } -void AutofillManager::GetCreditCardSuggestions(FormStructure* form, - const FormField& field, - AutofillFieldType type, - std::vector<string16>* values, - std::vector<string16>* labels, - std::vector<string16>* icons, - std::vector<int>* unique_ids) { +void AutofillManager::GetCreditCardSuggestions( + FormStructure* form, + const FormField& field, + AutofillFieldType type, + std::vector<string16>* values, + std::vector<string16>* labels, + std::vector<string16>* icons, + std::vector<int>* unique_ids) const { for (std::vector<CreditCard*>::const_iterator iter = personal_data_->credit_cards().begin(); iter != personal_data_->credit_cards().end(); ++iter) { @@ -1099,9 +1141,14 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { if (!form_structures_.empty()) metric_logger_->LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED); + + // For the |non_queryable_forms|, we have all the field type info we're ever + // going to get about them. For the other forms, we'll wait until we get a + // response from the server. + SendAutofillTypePredictions(non_queryable_forms); } -int AutofillManager::GUIDToID(const GUIDPair& guid) { +int AutofillManager::GUIDToID(const GUIDPair& guid) const { static int last_id = 1; if (!guid::IsValidGUID(guid.first)) @@ -1117,7 +1164,7 @@ int AutofillManager::GUIDToID(const GUIDPair& guid) { } } -const AutofillManager::GUIDPair AutofillManager::IDToGUID(int id) { +const AutofillManager::GUIDPair AutofillManager::IDToGUID(int id) const { if (id == 0) return GUIDPair(std::string(), 0); @@ -1134,7 +1181,7 @@ const AutofillManager::GUIDPair AutofillManager::IDToGUID(int id) { // profile IDs into a single integer. Credit card IDs are sent in the high // word and profile IDs are sent in the low word. int AutofillManager::PackGUIDs(const GUIDPair& cc_guid, - const GUIDPair& profile_guid) { + const GUIDPair& profile_guid) const { int cc_id = GUIDToID(cc_guid); int profile_id = GUIDToID(profile_guid); @@ -1149,7 +1196,7 @@ int AutofillManager::PackGUIDs(const GUIDPair& cc_guid, // high word and profile IDs are stored in the low word. void AutofillManager::UnpackGUIDs(int id, GUIDPair* cc_guid, - GUIDPair* profile_guid) { + GUIDPair* profile_guid) const { int cc_id = id >> std::numeric_limits<unsigned short>::digits & std::numeric_limits<unsigned short>::max(); int profile_id = id & std::numeric_limits<unsigned short>::max(); diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 986c801..21ded1c 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -102,13 +102,13 @@ class AutofillManager : public TabContentsObserver, // Maps GUIDs to and from IDs that are used to identify profiles and credit // cards sent to and from the renderer process. - virtual int GUIDToID(const GUIDPair& guid); - virtual const GUIDPair IDToGUID(int id); + virtual int GUIDToID(const GUIDPair& guid) const; + virtual const GUIDPair IDToGUID(int id) const; // Methods for packing and unpacking credit card and profile IDs for sending // and receiving to and from the renderer process. - int PackGUIDs(const GUIDPair& cc_guid, const GUIDPair& profile_guid); - void UnpackGUIDs(int id, GUIDPair* cc_guid, GUIDPair* profile_guid); + int PackGUIDs(const GUIDPair& cc_guid, const GUIDPair& profile_guid) const; + void UnpackGUIDs(int id, GUIDPair* cc_guid, GUIDPair* profile_guid) const; private: void OnFormSubmitted(const webkit_glue::FormData& form, @@ -136,19 +136,36 @@ class AutofillManager : public TabContentsObserver, const std::vector<CreditCard*>& credit_cards, RenderViewHost** host) const WARN_UNUSED_RESULT; + // Unpacks |unique_id| and fills |profile| or |credit_card| with the + // appropriate data source. Returns false if the unpacked id cannot be found. + bool GetProfileOrCreditCard(int unique_id, + const std::vector<AutofillProfile*>& profiles, + const std::vector<CreditCard*>& credit_cards, + const AutofillProfile** profile, + const CreditCard** credit_card, + size_t* variant) const WARN_UNUSED_RESULT; + // Fills |form_structure| cached element corresponding to |form|. // Returns false if the cached element was not found. bool FindCachedForm(const webkit_glue::FormData& form, FormStructure** form_structure) const WARN_UNUSED_RESULT; // Fills |form_structure| and |autofill_field| with the cached elements - // corresponding to |form| and |field|. Returns false if the cached elements - // were not found. - bool FindCachedFormAndField( - const webkit_glue::FormData& form, - const webkit_glue::FormField& field, - FormStructure** form_structure, - AutofillField** autofill_field) WARN_UNUSED_RESULT; + // corresponding to |form| and |field|. This might have the side-effect of + // updating the cache. Returns false if the |form| is not autofillable, or if + // it is not already present in the cache and the cache is full. + bool GetCachedFormAndField(const webkit_glue::FormData& form, + const webkit_glue::FormField& field, + FormStructure** form_structure, + AutofillField** autofill_field) WARN_UNUSED_RESULT; + + // Re-parses |live_form| and adds the result to |form_structures_|. + // |cached_form| should be a pointer to the existing version of the form, or + // NULL if no cached version exists. The updated form is then written into + // |updated_form|. + void UpdateCachedForm(const webkit_glue::FormData& live_form, + const FormStructure* cached_form, + FormStructure** updated_form); // Returns a list of values from the stored profiles that match |type| and the // value of |field| and returns the labels of the matching profiles. |labels| @@ -159,7 +176,7 @@ class AutofillManager : public TabContentsObserver, std::vector<string16>* values, std::vector<string16>* labels, std::vector<string16>* icons, - std::vector<int>* unique_ids); + std::vector<int>* unique_ids) const; // Returns a list of values from the stored credit cards that match |type| and // the value of |field| and returns the labels of the matching credit cards. @@ -169,7 +186,7 @@ class AutofillManager : public TabContentsObserver, std::vector<string16>* values, std::vector<string16>* labels, std::vector<string16>* icons, - std::vector<int>* unique_ids); + std::vector<int>* unique_ids) const; // Set |field|'s value based on |type| and contents of the |credit_card|. void FillCreditCardFormField(const CreditCard& credit_card, @@ -206,6 +223,11 @@ class AutofillManager : public TabContentsObserver, void UpdateInitialInteractionTimestamp( const base::TimeTicks& interaction_timestamp); + // Send our current field type predictions to the renderer. + // This is a no-op if the appropriate command-line flag is not set. + void SendAutofillTypePredictions( + const std::vector<FormStructure*>& forms) const; + // The owning TabContentsWrapper. TabContentsWrapper* tab_contents_wrapper_; @@ -250,8 +272,8 @@ class AutofillManager : public TabContentsObserver, ScopedVector<FormStructure> form_structures_; // GUID to ID mapping. We keep two maps to convert back and forth. - std::map<GUIDPair, int> guid_id_map_; - std::map<int, GUIDPair> id_guid_map_; + mutable std::map<GUIDPair, int> guid_id_map_; + mutable std::map<int, GUIDPair> id_guid_map_; friend class AutofillManagerTest; friend class FormStructureBrowserTest; diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index 8804aa7..03a77df 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -442,7 +442,7 @@ class TestAutofillManager : public AutofillManager { return PackGUIDs(IDToGUID(credit_card_id), GUIDPair(std::string(), 0)); } - virtual int GUIDToID(const GUIDPair& guid) OVERRIDE { + virtual int GUIDToID(const GUIDPair& guid) const OVERRIDE { if (guid.first.empty()) return 0; @@ -452,7 +452,7 @@ class TestAutofillManager : public AutofillManager { return id; } - virtual const GUIDPair IDToGUID(int id) OVERRIDE { + virtual const GUIDPair IDToGUID(int id) const OVERRIDE { EXPECT_TRUE(id >= 0); if (id <= 0) return GUIDPair(std::string(), 0); diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index 90250bf..0bbe1db 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -279,8 +279,9 @@ FormStructure::FormStructure(const FormData& form) server_experiment_id_("no server response"), has_author_specified_types_(false) { // Copy the form fields. - std::vector<webkit_glue::FormField>::const_iterator field; - for (field = form.fields.begin(); + std::map<string16, size_t> unique_names; + for (std::vector<webkit_glue::FormField>::const_iterator field = + form.fields.begin(); field != form.fields.end(); field++) { // Add all supported form fields (including with empty names) to the // signature. This is a requirement for Autofill servers. @@ -288,8 +289,14 @@ FormStructure::FormStructure(const FormData& form) form_signature_field_names_.append(UTF16ToUTF8(field->name)); // Generate a unique name for this field by appending a counter to the name. - string16 unique_name = field->name + - base::IntToString16(fields_.size() + 1); + // Make sure to prepend the counter with a non-numeric digit so that we are + // guaranteed to avoid collisions. + if (!unique_names.count(field->name)) + unique_names[field->name] = 1; + else + ++unique_names[field->name]; + string16 unique_name = field->name + ASCIIToUTF16("_") + + base::IntToString16(unique_names[field->name]); fields_.push_back(new AutofillField(*field, unique_name)); } diff --git a/chrome/renderer/autofill/autofill_agent.cc b/chrome/renderer/autofill/autofill_agent.cc index 98334d7..d18d9b4 100644 --- a/chrome/renderer/autofill/autofill_agent.cc +++ b/chrome/renderer/autofill/autofill_agent.cc @@ -78,7 +78,7 @@ void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) { // The document has now been fully loaded. Scan for forms to be sent up to // the browser. std::vector<webkit_glue::FormData> forms; - form_manager_.ExtractForms(frame, &forms); + form_manager_.ExtractForms(*frame, &forms); if (!forms.empty()) { Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, @@ -87,11 +87,11 @@ void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) { } void AutofillAgent::FrameDetached(WebFrame* frame) { - form_manager_.ResetFrame(frame); + form_manager_.ResetFrame(*frame); } void AutofillAgent::FrameWillClose(WebFrame* frame) { - form_manager_.ResetFrame(frame); + form_manager_.ResetFrame(*frame); } void AutofillAgent::WillSubmitForm(WebFrame* frame, @@ -99,10 +99,12 @@ void AutofillAgent::WillSubmitForm(WebFrame* frame, FormData form_data; if (FormManager::WebFormElementToFormData( form, + WebFormControlElement(), FormManager::REQUIRE_AUTOCOMPLETE, static_cast<FormManager::ExtractMask>( FormManager::EXTRACT_VALUE | FormManager::EXTRACT_OPTION_TEXT), - &form_data)) { + &form_data, + NULL)) { Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data, base::TimeTicks::Now())); } @@ -137,7 +139,8 @@ void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, } else if (suggestions_clear_index_ != -1 && index == static_cast<unsigned>(suggestions_clear_index_)) { // User selected 'Clear form'. - form_manager_.ClearFormWithNode(node); + DCHECK(node == autofill_query_element_); + form_manager_.ClearFormWithElement(autofill_query_element_); } else if (!unique_id) { // User selected an Autocomplete entry, so we fill directly. WebInputElement element = node.toConst<WebInputElement>(); @@ -167,7 +170,9 @@ void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node, } void AutofillAgent::didClearAutofillSelection(const WebNode& node) { - form_manager_.ClearPreviewedFormWithNode(node, was_query_node_autofilled_); + DCHECK(node == autofill_query_element_); + FormManager::ClearPreviewedFormWithElement(autofill_query_element_, + was_query_node_autofilled_); } void AutofillAgent::removeAutocompleteSuggestion(const WebString& name, @@ -276,7 +281,7 @@ void AutofillAgent::OnSuggestionsReturned(int query_id, // The form has been auto-filled, so give the user the chance to clear the // form. Append the 'Clear form' menu item. if (has_autofill_item && - form_manager_.FormWithNodeIsAutofilled(autofill_query_element_)) { + FormManager::FormWithElementIsAutofilled(autofill_query_element_)) { v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM)); l.push_back(string16()); i.push_back(string16()); @@ -316,12 +321,12 @@ void AutofillAgent::OnFormDataFilled(int query_id, switch (autofill_action_) { case AUTOFILL_FILL: - form_manager_.FillForm(form, autofill_query_element_); + FormManager::FillForm(form, autofill_query_element_); Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), base::TimeTicks::Now())); break; case AUTOFILL_PREVIEW: - form_manager_.PreviewForm(form, autofill_query_element_); + FormManager::PreviewForm(form, autofill_query_element_); Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); break; default: @@ -415,20 +420,8 @@ bool AutofillAgent::FindFormAndFieldForNode(const WebNode& node, webkit_glue::FormData* form, webkit_glue::FormField* field) { const WebInputElement& element = node.toConst<WebInputElement>(); - if (!form_manager_.FindFormWithFormControlElement(element, form)) - return false; - - FormManager::WebFormControlElementToFormField(element, - FormManager::EXTRACT_VALUE, - field); - - // WebFormControlElementToFormField does not scrape the DOM for the field - // label, so find the label here. - // TODO(isherman): Add form and field identities so we can use the cached form - // data in FormManager. - field->label = FormManager::LabelForElement(element); - - return true; + return + FormManager::FindFormAndFieldForFormControlElement(element, form, field); } } // namespace autofill diff --git a/chrome/renderer/autofill/form_manager.cc b/chrome/renderer/autofill/form_manager.cc index 3465f93..99e2f8e 100644 --- a/chrome/renderer/autofill/form_manager.cc +++ b/chrome/renderer/autofill/form_manager.cc @@ -457,35 +457,62 @@ typedef void (*Callback)(WebKit::WebFormControlElement*, const webkit_glue::FormField*, bool); +// Fills |autofillable_elements| with all the auto-fillable form control +// elements in |form_element|. +void ExtractAutofillableElements( + const WebFormElement& form_element, + std::vector<WebFormControlElement>* autofillable_elements) { + WebVector<WebFormControlElement> control_elements; + form_element.getFormControlElements(control_elements); + + autofillable_elements->clear(); + for (size_t i = 0; i < control_elements.size(); ++i) { + WebFormControlElement element = control_elements[i]; + if (!IsAutofillableElement(element)) + continue; + + autofillable_elements->push_back(element); + } +} + // For each autofillable field in |data| that matches a field in the |form|, // the |callback| is invoked with the corresponding |form| field data. -void ForEachMatchingFormField( - std::vector<WebKit::WebFormControlElement>* control_elements, - const WebNode& node, - const FormData& data, - Callback callback) { +void ForEachMatchingFormField(const WebFormElement& form_element, + const WebElement& initiating_element, + const FormData& data, + Callback callback) { + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); + + if (control_elements.size() != data.fields.size()) { + // This case should be reachable only for pathological websites, which add + // or remove form fields while the user is interacting with the Autofill + // popup. I (isherman) am not aware of any such websites, and so am + // optimistically including a NOTREACHED(). If you ever trip this check, + // please file a bug against me. + NOTREACHED(); + return; + } + // It's possible that the site has injected fields into the form after the // page has loaded, so we can't assert that the size of the cached control // elements is equal to the size of the fields in |form|. Fortunately, the // one case in the wild where this happens, paypal.com signup form, the fields // are appended to the end of the form and are not visible. - for (size_t i = 0, j = 0; - i < control_elements->size() && j < data.fields.size(); - ++i) { - WebFormControlElement* element = &(*control_elements)[i]; - string16 element_name(element->nameForAutofill()); - - // Search forward in the |form| for a corresponding field. - size_t k = j; - while (k < data.fields.size() && element_name != data.fields[k].name) - k++; - - if (k >= data.fields.size()) + for (size_t i = 0; i < control_elements.size(); ++i) { + WebFormControlElement* element = &control_elements[i]; + + if (string16(element->nameForAutofill()) != data.fields[i].name) { + // This case should be reachable only for pathological websites, which + // rename form fields while the user is interacting with the Autofill + // popup. I (isherman) am not aware of any such websites, and so am + // optimistically including a NOTREACHED(). If you ever trip this check, + // please file a bug against me. + NOTREACHED(); continue; + } - DCHECK_EQ(data.fields[k].name, element_name); - - bool is_initiating_node = false; + bool is_initiating_element = (*element == initiating_element); const WebInputElement* input_element = toWebInputElement(element); if (IsTextInput(input_element)) { @@ -494,11 +521,9 @@ void ForEachMatchingFormField( if (!input_element->autoComplete()) continue; - is_initiating_node = (*input_element == node); - // Only autofill empty fields and the field that initiated the filling, // i.e. the field the user is currently editing and interacting with. - if (!is_initiating_node && !input_element->value().isEmpty()) + if (!is_initiating_element && !input_element->value().isEmpty()) continue; } @@ -506,10 +531,7 @@ void ForEachMatchingFormField( !element->isFocusable()) continue; - callback(element, &data.fields[k], is_initiating_node); - - // We found a matching form field so move on to the next. - ++j; + callback(element, &data.fields[i], is_initiating_element); } } @@ -573,12 +595,6 @@ void PreviewFormField(WebKit::WebFormControlElement* field, namespace autofill { -struct FormManager::FormElement { - WebKit::WebFormElement form_element; - std::vector<WebKit::WebFormControlElement> control_elements; - std::vector<string16> control_values; -}; - FormManager::FormManager() { } @@ -659,50 +675,30 @@ void FormManager::WebFormControlElementToFormField( } // static -string16 FormManager::LabelForElement(const WebFormControlElement& element) { - // Don't scrape labels for elements we can't possibly autofill anyway. - if (!IsAutofillableElement(element)) - return string16(); - - WebNodeList labels = element.document().getElementsByTagName("label"); - for (unsigned i = 0; i < labels.length(); ++i) { - WebLabelElement label = labels.item(i).to<WebLabelElement>(); - WebElement corresponding_control = label.correspondingControl(); - if (corresponding_control == element || - (corresponding_control.isNull() && - label.getAttribute("for") == element.nameForAutofill())) { - return FindChildText(label); - } - } - - // Infer the label from context if not found in label element. - return InferLabelForElement(element); -} - -// static -bool FormManager::WebFormElementToFormData(const WebFormElement& element, - RequirementsMask requirements, - ExtractMask extract_mask, - FormData* form) { - DCHECK(form); - - const WebFrame* frame = element.document().frame(); +bool FormManager::WebFormElementToFormData( + const WebKit::WebFormElement& form_element, + const WebKit::WebFormControlElement& form_control_element, + RequirementsMask requirements, + ExtractMask extract_mask, + webkit_glue::FormData* form, + webkit_glue::FormField* field) { + const WebFrame* frame = form_element.document().frame(); if (!frame) return false; - if (requirements & REQUIRE_AUTOCOMPLETE && !element.autoComplete()) + if (requirements & REQUIRE_AUTOCOMPLETE && !form_element.autoComplete()) return false; - form->name = GetFormIdentifier(element); - form->method = element.method(); + form->name = GetFormIdentifier(form_element); + form->method = form_element.method(); form->origin = frame->document().url(); - form->action = frame->document().completeURL(element.action()); - form->user_submitted = element.wasUserSubmitted(); + form->action = frame->document().completeURL(form_element.action()); + form->user_submitted = form_element.wasUserSubmitted(); // If the completed URL is not valid, just use the action we get from // WebKit. if (!form->action.is_valid()) - form->action = GURL(element.action()); + form->action = GURL(form_element.action()); // A map from a FormField's name to the FormField itself. std::map<string16, FormField*> name_map; @@ -712,7 +708,7 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, ScopedVector<FormField> form_fields; WebVector<WebFormControlElement> control_elements; - element.getFormControlElements(control_elements); + form_element.getFormControlElements(control_elements); // A vector of bools that indicate whether each field in the form meets the // requirements and thus will be in the resulting |form|. @@ -733,13 +729,13 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, continue; // Create a new FormField, fill it out and map it to the field's name. - FormField* field = new FormField; - WebFormControlElementToFormField(control_element, extract_mask, field); - form_fields.push_back(field); + FormField* form_field = new FormField; + WebFormControlElementToFormField(control_element, extract_mask, form_field); + form_fields.push_back(form_field); // TODO(jhawkins): A label element is mapped to a form control element's id. // field->name() will contain the id only if the name does not exist. Add // an id() method to WebFormControlElement and use that here. - name_map[field->name] = field; + name_map[form_field->name] = form_field; fields_extracted[i] = true; } @@ -752,7 +748,7 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, // element's name as a key into the <name, FormField> map to find the // previously created FormField and set the FormField's label to the // label.firstChild().nodeValue() of the label element. - WebNodeList labels = element.getElementsByTagName("label"); + WebNodeList labels = form_element.getElementsByTagName("label"); for (unsigned i = 0; i < labels.length(); ++i) { WebLabelElement label = labels.item(i).to<WebLabelElement>(); WebFormControlElement field_element = @@ -798,6 +794,9 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, if (form_fields[field_idx]->label.empty()) form_fields[field_idx]->label = InferLabelForElement(control_element); + if (field && form_control_element == control_element) + *field = *form_fields[field_idx]; + ++field_idx; } @@ -810,50 +809,156 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, return true; } -void FormManager::ExtractForms(const WebFrame* frame, +// static +bool FormManager::FindFormAndFieldForFormControlElement( + const WebFormControlElement& element, + FormData* form, + webkit_glue::FormField* field) { + const WebFormElement form_element = element.form(); + if (form_element.isNull()) + return false; + + ExtractMask extract_mask = + static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS); + return WebFormElementToFormData(form_element, + element, + REQUIRE_NONE, + extract_mask, + form, + field); +} + +// static +void FormManager::FillForm(const FormData& form, + const WebInputElement& element) { + WebFormElement form_element = element.form(); + if (form_element.isNull()) + return; + + ForEachMatchingFormField(form_element, + element, + form, + &FillFormField); +} + +// static +void FormManager::PreviewForm(const FormData& form, + const WebInputElement& element) { + WebFormElement form_element = element.form(); + if (form_element.isNull()) + return; + + ForEachMatchingFormField(form_element, + element, + form, + &PreviewFormField); +} + +// static +bool FormManager::ClearPreviewedFormWithElement(const WebInputElement& element, + bool was_autofilled) { + WebFormElement form_element = element.form(); + if (form_element.isNull()) + return false; + + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); + for (size_t i = 0; i < control_elements.size(); ++i) { + // Only text input elements can be previewed. + WebInputElement* input_element = toWebInputElement(&control_elements[i]); + if (!IsTextInput(input_element)) + continue; + + // If the input element is not auto-filled, we did not preview it, so there + // is nothing to reset. + if (!input_element->isAutofilled()) + continue; + + // There might be unrelated elements in this form which have already been + // auto-filled. For example, the user might have already filled the address + // part of a form and now be dealing with the credit card section. We only + // want to reset the auto-filled status for fields that were previewed. + if (input_element->suggestedValue().isEmpty()) + continue; + + // Clear the suggested value. For the initiating node, also restore the + // original value. + input_element->setSuggestedValue(WebString()); + bool is_initiating_node = (element == *input_element); + if (is_initiating_node) + input_element->setAutofilled(was_autofilled); + else + input_element->setAutofilled(false); + + // Clearing the suggested value in the focused node (above) can cause + // selection to be lost. We force selection range to restore the text + // cursor. + if (is_initiating_node) { + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); + } + } + + return true; +} + +// static +bool FormManager::FormWithElementIsAutofilled(const WebInputElement& element) { + WebFormElement form_element = element.form(); + if (form_element.isNull()) + return false; + + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); + for (size_t i = 0; i < control_elements.size(); ++i) { + WebInputElement* input_element = toWebInputElement(&control_elements[i]); + if (!IsTextInput(input_element)) + continue; + + if (input_element->isAutofilled()) + return true; + } + + return false; +} + +void FormManager::ExtractForms(const WebFrame& frame, std::vector<FormData>* forms) { // Reset the vector of FormElements for this frame. ResetFrame(frame); + web_frames_.insert(&frame); WebVector<WebFormElement> web_forms; - frame->document().forms(web_forms); + frame.document().forms(web_forms); size_t num_fields_seen = 0; for (size_t i = 0; i < web_forms.size(); ++i) { - // Owned by |form_elements_|. - FormElement* form_element = new FormElement; - form_element->form_element = web_forms[i]; + WebFormElement form_element = web_forms[i]; - WebVector<WebFormControlElement> control_elements; - form_element->form_element.getFormControlElements(control_elements); + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); for (size_t j = 0; j < control_elements.size(); ++j) { WebFormControlElement element = control_elements[j]; - if (!IsAutofillableElement(element)) - continue; - - form_element->control_elements.push_back(element); // Save original values of <select> elements so we can restore them // when |ClearFormWithNode()| is invoked. if (IsSelectElement(element)) { - form_element->control_values.push_back( - element.toConst<WebSelectElement>().value()); - } else { - form_element->control_values.push_back(string16()); + const WebSelectElement select_element = + element.toConst<WebSelectElement>(); + initial_select_values_.insert(std::make_pair(select_element, + select_element.value())); } } - form_elements_.push_back(form_element); - // To avoid overly expensive computation, we impose both a minimum and a // maximum number of allowable fields. - if (form_element->control_elements.size() < kRequiredAutofillFields || - form_element->control_elements.size() > kMaxParseableFields) + if (control_elements.size() < kRequiredAutofillFields || + control_elements.size() > kMaxParseableFields) continue; FormData form; - WebFormElementToFormData( - form_element->form_element, REQUIRE_NONE, EXTRACT_VALUE, &form); + WebFormElementToFormData(form_element, WebFormControlElement(), + REQUIRE_NONE, EXTRACT_VALUE, &form, NULL); num_fields_seen += form.fields.size(); if (num_fields_seen > kMaxParseableFields) @@ -864,112 +969,36 @@ void FormManager::ExtractForms(const WebFrame* frame, } } -bool FormManager::FindFormWithFormControlElement( - const WebFormControlElement& element, - FormData* form) { - DCHECK(form); - - const WebFrame* frame = element.document().frame(); - if (!frame) - return false; - - for (FormElementList::const_iterator iter = form_elements_.begin(); - iter != form_elements_.end(); ++iter) { - const FormElement* form_element = *iter; +void FormManager::ResetFrame(const WebFrame& frame) { + web_frames_.erase(&frame); - if (form_element->form_element.document().frame() != frame) - continue; - - for (std::vector<WebFormControlElement>::const_iterator iter = - form_element->control_elements.begin(); - iter != form_element->control_elements.end(); ++iter) { - if (iter->nameForAutofill() == element.nameForAutofill()) { - ExtractMask extract_mask = - static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS); - return WebFormElementToFormData(form_element->form_element, - REQUIRE_NONE, - extract_mask, - form); - } - } + std::vector<WebSelectElement> to_delete; + for (std::map<const WebSelectElement, string16>::const_iterator it = + initial_select_values_.begin(); + it != initial_select_values_.end(); ++it) { + WebFormElement form_element = it->first.form(); + if (form_element.isNull() || form_element.document().frame() == &frame) + to_delete.push_back(it->first); } - return false; -} - -void FormManager::FillForm(const FormData& form, const WebNode& node) { - FormElement* form_element = NULL; - if (!FindCachedFormElement(form, &form_element)) - return; - - ForEachMatchingFormField(&form_element->control_elements, - node, - form, - &FillFormField); -} - -void FormManager::PreviewForm(const FormData& form, const WebNode& node) { - FormElement* form_element = NULL; - if (!FindCachedFormElement(form, &form_element)) - return; - - ForEachMatchingFormField(&form_element->control_elements, - node, - form, - &PreviewFormField); -} - -bool FormManager::ShowPredictions(const FormDataPredictions& form) { - FormElement* form_element = NULL; - if (!FindCachedFormElement(form.data, &form_element)) - return false; - - DCHECK_EQ(form.data.fields.size(), form.fields.size()); - for (size_t i = 0, j = 0; - i < form_element->control_elements.size() && j < form.fields.size(); - ++i) { - WebFormControlElement* element = &form_element->control_elements[i]; - string16 element_name(element->nameForAutofill()); - - // Search forward in the |form| for a corresponding field. - size_t k = j; - while (k < form.fields.size() && element_name != form.data.fields[k].name) - k++; - - if (k >= form.fields.size()) - continue; - - DCHECK_EQ(form.data.fields[k].name, element_name); - - std::string placeholder = form.fields[k].overall_type; - string16 title = l10n_util::GetStringFUTF16( - IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE, - UTF8ToUTF16(form.fields[k].heuristic_type), - UTF8ToUTF16(form.fields[k].server_type), - UTF8ToUTF16(form.fields[k].signature), - UTF8ToUTF16(form.signature), - UTF8ToUTF16(form.experiment_id)); - if (!element->hasAttribute("placeholder")) - element->setAttribute("placeholder", WebString(UTF8ToUTF16(placeholder))); - element->setAttribute("title", WebString(title)); - - // We found a matching form field so move on to the next. - ++j; + for (std::vector<WebSelectElement>::const_iterator it = + to_delete.begin(); + it != to_delete.end(); ++it) { + initial_select_values_.erase(*it); } - - return true; } -bool FormManager::ClearFormWithNode(const WebNode& node) { - FormElement* form_element = NULL; - if (!FindCachedFormElementWithNode(node, &form_element)) +bool FormManager::ClearFormWithElement(const WebInputElement& element) { + WebFormElement form_element = element.form(); + if (form_element.isNull()) return false; - for (size_t i = 0; i < form_element->control_elements.size(); ++i) { - WebFormControlElement element = form_element->control_elements[i]; - WebInputElement* input_element = toWebInputElement(&element); + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); + for (size_t i = 0; i < control_elements.size(); ++i) { + WebFormControlElement control_element = control_elements[i]; + WebInputElement* input_element = toWebInputElement(&control_element); if (IsTextInput(input_element)) { - // We don't modify the value of disabled fields. if (!input_element->isEnabled()) continue; @@ -979,15 +1008,19 @@ bool FormManager::ClearFormWithNode(const WebNode& node) { // Clearing the value in the focused node (above) can cause selection // to be lost. We force selection range to restore the text cursor. - if (node == *input_element) { + if (element == *input_element) { int length = input_element->value().length(); input_element->setSelectionRange(length, length); } } else { - DCHECK(IsSelectElement(element)); - WebSelectElement select_element = element.to<WebSelectElement>(); - if (select_element.value() != form_element->control_values[i]) { - select_element.setValue(form_element->control_values[i]); + DCHECK(IsSelectElement(control_element)); + WebSelectElement select_element = control_element.to<WebSelectElement>(); + + std::map<const WebSelectElement, string16>::const_iterator + initial_value_iter = initial_select_values_.find(select_element); + if (initial_value_iter != initial_select_values_.end() && + select_element.value() != initial_value_iter->second) { + select_element.setValue(initial_value_iter->second); select_element.dispatchFormControlChangeEvent(); } } @@ -996,121 +1029,71 @@ bool FormManager::ClearFormWithNode(const WebNode& node) { return true; } -bool FormManager::ClearPreviewedFormWithNode(const WebNode& node, - bool was_autofilled) { - FormElement* form_element = NULL; - if (!FindCachedFormElementWithNode(node, &form_element)) - return false; - - for (size_t i = 0; i < form_element->control_elements.size(); ++i) { - WebInputElement* input_element = - toWebInputElement(&form_element->control_elements[i]); - // Only text input elements can be previewed. - if (!IsTextInput(input_element)) - continue; - - // If the input element has not been auto-filled, FormManager has not - // previewed this field, so we have nothing to reset. - if (!input_element->isAutofilled()) - continue; - - // There might be unrelated elements in this form which have already been - // auto-filled. For example, the user might have already filled the address - // part of a form and now be dealing with the credit card section. We only - // want to reset the auto-filled status for fields that were previewed. - if (input_element->suggestedValue().isEmpty()) - continue; - - // Clear the suggested value. For the initiating node, also restore the - // original value. - input_element->setSuggestedValue(WebString()); - bool is_initiating_node = (node == *input_element); - if (is_initiating_node) - input_element->setAutofilled(was_autofilled); - else - input_element->setAutofilled(false); +bool FormManager::ShowPredictions(const FormDataPredictions& form) { + DCHECK_EQ(form.data.fields.size(), form.fields.size()); - // Clearing the suggested value in the focused node (above) can cause - // selection to be lost. We force selection range to restore the text - // cursor. - if (is_initiating_node) { - int length = input_element->value().length(); - input_element->setSelectionRange(length, length); + // Find the form. + bool found_form = false; + WebFormElement form_element; + for (std::set<const WebFrame*>::const_iterator it = web_frames_.begin(); + it != web_frames_.end() && !found_form; ++it) { + WebVector<WebFormElement> web_forms; + (*it)->document().forms(web_forms); + + for (size_t i = 0; i < web_forms.size(); ++i) { + form_element = web_forms[i]; + + // Note: matching on the form name here which is not guaranteed to be + // unique for the page, nor is it guaranteed to be non-empty. Ideally, we + // would have a way to uniquely identify the form cross-process. For now, + // we'll check form name and form action for identity. + // Also note that WebString() == WebString(string16()) does not evaluate + // to |true| -- WebKit distinguishes between a "null" string (lhs) and an + // "empty" string (rhs). We don't want that distinction, so forcing to + // string16. + string16 element_name = GetFormIdentifier(form_element); + GURL action(form_element.document().completeURL(form_element.action())); + if (element_name == form.data.name && action == form.data.action) { + found_form = true; + break; + } } } - return true; -} - -void FormManager::ResetFrame(const WebFrame* frame) { - FormElementList::iterator iter = form_elements_.begin(); - while (iter != form_elements_.end()) { - if ((*iter)->form_element.document().frame() == frame) - iter = form_elements_.erase(iter); - else - ++iter; - } -} - -bool FormManager::FormWithNodeIsAutofilled(const WebNode& node) { - FormElement* form_element = NULL; - if (!FindCachedFormElementWithNode(node, &form_element)) + if (!found_form) return false; - for (size_t i = 0; i < form_element->control_elements.size(); ++i) { - WebInputElement* input_element = - toWebInputElement(&form_element->control_elements[i]); - if (!IsTextInput(input_element)) - continue; - - if (input_element->isAutofilled()) - return true; + std::vector<WebFormControlElement> control_elements; + ExtractAutofillableElements(form_element, &control_elements); + if (control_elements.size() != form.fields.size()) { + // Keep things simple. Don't show predictions for forms that were modified + // between page load and the server's response to our query. + return false; } - return false; -} + for (size_t i = 0; i < control_elements.size(); ++i) { + WebFormControlElement* element = &control_elements[i]; -bool FormManager::FindCachedFormElementWithNode(const WebNode& node, - FormElement** form_element) { - for (FormElementList::const_iterator form_iter = form_elements_.begin(); - form_iter != form_elements_.end(); ++form_iter) { - for (std::vector<WebFormControlElement>::const_iterator iter = - (*form_iter)->control_elements.begin(); - iter != (*form_iter)->control_elements.end(); ++iter) { - if (*iter == node) { - *form_element = *form_iter; - return true; - } + if (string16(element->nameForAutofill()) != form.data.fields[i].name) { + // Keep things simple. Don't show predictions for elements whose names + // were modified between page load and the server's response to our query. + continue; } - } - - return false; -} -bool FormManager::FindCachedFormElement(const FormData& form, - FormElement** form_element) { - for (FormElementList::iterator form_iter = form_elements_.begin(); - form_iter != form_elements_.end(); ++form_iter) { - // TODO(dhollowa): matching on form name here which is not guaranteed to - // be unique for the page, nor is it guaranteed to be non-empty. Need to - // find a way to uniquely identify the form cross-process. For now we'll - // check form name and form action for identity. - // http://crbug.com/37990 test file sample8.html. - // Also note that WebString() == WebString(string16()) does not evaluate to - // |true| -- WebKit distinguishes between a "null" string (lhs) and an - // "empty" string (rhs). We don't want that distinction, so forcing to - // string16. - string16 element_name = GetFormIdentifier((*form_iter)->form_element); - GURL action( - (*form_iter)->form_element.document().completeURL( - (*form_iter)->form_element.action())); - if (element_name == form.name && action == form.action) { - *form_element = *form_iter; - return true; - } + std::string placeholder = form.fields[i].overall_type; + string16 title = l10n_util::GetStringFUTF16( + IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE, + UTF8ToUTF16(form.fields[i].heuristic_type), + UTF8ToUTF16(form.fields[i].server_type), + UTF8ToUTF16(form.fields[i].signature), + UTF8ToUTF16(form.signature), + UTF8ToUTF16(form.experiment_id)); + if (!element->hasAttribute("placeholder")) + element->setAttribute("placeholder", WebString(UTF8ToUTF16(placeholder))); + element->setAttribute("title", WebString(title)); } - return false; + return true; } } // namespace autofill diff --git a/chrome/renderer/autofill/form_manager.h b/chrome/renderer/autofill/form_manager.h index b32dcb5..cc8f570 100644 --- a/chrome/renderer/autofill/form_manager.h +++ b/chrome/renderer/autofill/form_manager.h @@ -7,6 +7,7 @@ #pragma once #include <map> +#include <set> #include <vector> #include "base/callback_old.h" @@ -23,6 +24,8 @@ struct FormField; namespace WebKit { class WebFormControlElement; class WebFrame; +class WebInputElement; +class WebSelectElement; } // namespace WebKit namespace autofill { @@ -61,86 +64,75 @@ class FormManager { ExtractMask extract_mask, webkit_glue::FormField* field); - // Returns the corresponding label for |element|. WARNING: This method can - // potentially be very slow. Do not use during any code paths where the page - // is loading. - // TODO(isherman): Refactor autofill_agent.cc not to require this method to be - // exposed. - static string16 LabelForElement(const WebKit::WebFormControlElement& element); - - // Fills out a FormData object from a given WebFormElement. If |get_values| - // is true, the fields in |form| will have the values filled out. If - // |get_options| is true, the fields in |form will have select options filled - // out. Returns true if |form| is filled out; it's possible that |element| - // won't meet the requirements in |requirements|. This also returns false if - // there are no fields in |form|. - static bool WebFormElementToFormData(const WebKit::WebFormElement& element, - RequirementsMask requirements, - ExtractMask extract_mask, - webkit_glue::FormData* form); - - // Scans the DOM in |frame| extracting and storing forms. - // Returns a vector of the extracted forms. - void ExtractForms(const WebKit::WebFrame* frame, - std::vector<webkit_glue::FormData>* forms); + // Fills |form| with the FormData object corresponding to the |form_element|. + // If |field| is non-NULL, also fills |field| with the FormField object + // corresponding to the |form_control_element|. + // |extract_mask| controls what data is extracted. + // Returns true if |form| is filled out; it's possible that the |form_element| + // won't meet the |requirements|. Also returns false if there are no fields + // in the |form|. + static bool WebFormElementToFormData( + const WebKit::WebFormElement& form_element, + const WebKit::WebFormControlElement& form_control_element, + RequirementsMask requirements, + ExtractMask extract_mask, + webkit_glue::FormData* form, + webkit_glue::FormField* field); - // Finds the form that contains |element| and returns it in |form|. Returns - // false if the form is not found. - bool FindFormWithFormControlElement( + // Finds the form that contains |element| and returns it in |form|. Fills + // |field| with the |FormField| representation for element. + // Returns false if the form is not found. + static bool FindFormAndFieldForFormControlElement( const WebKit::WebFormControlElement& element, - webkit_glue::FormData* form); + webkit_glue::FormData* form, + webkit_glue::FormField* field); - // Fills the form represented by |form|. |node| is the input element that + // Fills the form represented by |form|. |element| is the input element that // initiated the auto-fill process. - void FillForm(const webkit_glue::FormData& form, const WebKit::WebNode& node); - - // Previews the form represented by |form|. |node| is the input element that - // initiated the preview process. - void PreviewForm(const webkit_glue::FormData& form, - const WebKit::WebNode &node); - - // For each field in the |form|, sets the field's placeholder text to the - // field's overall predicted type. Also sets the title to include the field's - // heuristic type, server type, and signature; as well as the form's signature - // and the experiment id for the server predictions. - bool ShowPredictions(const webkit_glue::FormDataPredictions& form); + static void FillForm(const webkit_glue::FormData& form, + const WebKit::WebInputElement& element); - // Clears the values of all input elements in the form that contains |node|. - // Returns false if the form is not found. - bool ClearFormWithNode(const WebKit::WebNode& node); + // Previews the form represented by |form|. |element| is the input element + // that initiated the preview process. + static void PreviewForm(const webkit_glue::FormData& form, + const WebKit::WebInputElement& element); // Clears the placeholder values and the auto-filled background for any fields // in the form containing |node| that have been previewed. Resets the // autofilled state of |node| to |was_autofilled|. Returns false if the form // is not found. - bool ClearPreviewedFormWithNode(const WebKit::WebNode& node, - bool was_autofilled); - - // Resets the forms for the specified |frame|. - void ResetFrame(const WebKit::WebFrame* frame); + static bool ClearPreviewedFormWithElement( + const WebKit::WebInputElement& element, + bool was_autofilled); // Returns true if |form| has any auto-filled fields. - bool FormWithNodeIsAutofilled(const WebKit::WebNode& node); + static bool FormWithElementIsAutofilled( + const WebKit::WebInputElement& element); - private: - // Stores the WebFormElement and the form control elements for a form. - // Original form values are stored so when we clear a form we can reset - // <select> elements to their original value. - struct FormElement; + // Scans the DOM in |frame| extracting and storing forms. + // Returns a vector of the extracted forms. + void ExtractForms(const WebKit::WebFrame& frame, + std::vector<webkit_glue::FormData>* forms); - // Type for cache of FormElement objects. - typedef ScopedVector<FormElement> FormElementList; + // Resets the forms for the specified |frame|. + void ResetFrame(const WebKit::WebFrame& frame); - // Finds the cached FormElement that contains |node|. - bool FindCachedFormElementWithNode(const WebKit::WebNode& node, - FormElement** form_element); + // Clears the values of all input elements in the form that contains + // |element|. Returns false if the form is not found. + bool ClearFormWithElement(const WebKit::WebInputElement& element); - // Uses the data in |form| to find the cached FormElement. - bool FindCachedFormElement(const webkit_glue::FormData& form, - FormElement** form_element); + // For each field in the |form|, sets the field's placeholder text to the + // field's overall predicted type. Also sets the title to include the field's + // heuristic type, server type, and signature; as well as the form's signature + // and the experiment id for the server predictions. + bool ShowPredictions(const webkit_glue::FormDataPredictions& form); + + private: + // The cached web frames. + std::set<const WebKit::WebFrame*> web_frames_; - // The cached FormElement objects. - FormElementList form_elements_; + // The cached initial values for <select> elements. + std::map<const WebKit::WebSelectElement, string16> initial_select_values_; DISALLOW_COPY_AND_ASSIGN(FormManager); }; diff --git a/chrome/renderer/autofill/form_manager_browsertest.cc b/chrome/renderer/autofill/form_manager_browsertest.cc index c28d548..fefcef0 100644 --- a/chrome/renderer/autofill/form_manager_browsertest.cc +++ b/chrome/renderer/autofill/form_manager_browsertest.cc @@ -69,7 +69,7 @@ class FormManagerTest : public RenderViewTest { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); const FormData& form = forms[0]; @@ -463,14 +463,19 @@ TEST_F(FormManagerTest, WebFormControlElementToFormFieldAutocompletetype) { TEST_F(FormManagerTest, WebFormElementToFormData) { LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" + " <LABEL for=\"firstname\">First name:</LABEL>" " <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>" + " <LABEL for=\"lastname\">Last name:</LABEL>" " <INPUT type=\"text\" id=\"lastname\" value=\"Smith\"/>" + " <LABEL for=\"state\">State:</LABEL>" " <SELECT id=\"state\"/>" " <OPTION value=\"CA\">California</OPTION>" " <OPTION value=\"TX\">Texas</OPTION>" " </SELECT>" // The below inputs should be ignored + " <LABEL for=\"notvisible\">Hidden:</LABEL>" " <INPUT type=\"hidden\" id=\"notvisible\" value=\"apple\"/>" + " <LABEL for=\"password\">Password:</LABEL>" " <INPUT type=\"password\" id=\"password\" value=\"secret\"/>" " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" "</FORM>"); @@ -482,11 +487,17 @@ TEST_F(FormManagerTest, WebFormElementToFormData) { frame->document().forms(forms); ASSERT_EQ(1U, forms.size()); + WebElement element = frame->document().getElementById("firstname"); + WebInputElement input_element = element.to<WebInputElement>(); + FormData form; + FormField field; EXPECT_TRUE(FormManager::WebFormElementToFormData(forms[0], + input_element, FormManager::REQUIRE_NONE, FormManager::EXTRACT_VALUE, - &form)); + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(frame->document().url()), form.origin); EXPECT_EQ(GURL("http://cnn.com"), form.action); @@ -497,18 +508,21 @@ TEST_F(FormManagerTest, WebFormElementToFormData) { FormField expected; expected.name = ASCIIToUTF16("firstname"); expected.value = ASCIIToUTF16("John"); + expected.label = ASCIIToUTF16("First name:"); expected.form_control_type = ASCIIToUTF16("text"); expected.max_length = WebInputElement::defaultMaxLength(); EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); expected.name = ASCIIToUTF16("lastname"); expected.value = ASCIIToUTF16("Smith"); + expected.label = ASCIIToUTF16("Last name:"); expected.form_control_type = ASCIIToUTF16("text"); expected.max_length = WebInputElement::defaultMaxLength(); EXPECT_FORM_FIELD_EQUALS(expected, fields[1]); expected.name = ASCIIToUTF16("state"); expected.value = ASCIIToUTF16("CA"); + expected.label = ASCIIToUTF16("State:"); expected.form_control_type = ASCIIToUTF16("select-one"); expected.max_length = 0; EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); @@ -543,7 +557,7 @@ TEST_F(FormManagerTest, ExtractMultipleForms) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(2U, forms.size()); // First form. @@ -606,7 +620,7 @@ TEST_F(FormManagerTest, ExtractFormsTooFewFields) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); EXPECT_EQ(0U, forms.size()); } @@ -631,10 +645,11 @@ TEST_F(FormManagerTest, WebFormElementToFormDataAutocomplete) { FormData form; EXPECT_TRUE(FormManager::WebFormElementToFormData( - web_form, FormManager::REQUIRE_NONE, FormManager::EXTRACT_NONE, &form)); + web_form, WebFormControlElement(), FormManager::REQUIRE_NONE, + FormManager::EXTRACT_NONE, &form, NULL)); EXPECT_FALSE(FormManager::WebFormElementToFormData( - web_form, FormManager::REQUIRE_AUTOCOMPLETE, FormManager::EXTRACT_NONE, - &form)); + web_form, WebFormControlElement(), FormManager::REQUIRE_AUTOCOMPLETE, + FormManager::EXTRACT_NONE, &form, NULL)); } { @@ -659,8 +674,8 @@ TEST_F(FormManagerTest, WebFormElementToFormDataAutocomplete) { FormData form; EXPECT_TRUE(FormManager::WebFormElementToFormData( - web_form, FormManager::REQUIRE_AUTOCOMPLETE, FormManager::EXTRACT_VALUE, - &form)); + web_form, WebFormControlElement(), FormManager::REQUIRE_AUTOCOMPLETE, + FormManager::EXTRACT_VALUE, &form, NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); @@ -707,8 +722,8 @@ TEST_F(FormManagerTest, WebFormElementToFormDataEnabled) { FormData form; EXPECT_TRUE(FormManager::WebFormElementToFormData( - web_form, FormManager::REQUIRE_ENABLED, FormManager::EXTRACT_VALUE, - &form)); + web_form, WebFormControlElement(), FormManager::REQUIRE_ENABLED, + FormManager::EXTRACT_VALUE, &form, NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); @@ -747,7 +762,7 @@ TEST_F(FormManagerTest, FindForm) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -756,8 +771,10 @@ TEST_F(FormManagerTest, FindForm) { // Find the form and verify it's the correct form. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -772,6 +789,7 @@ TEST_F(FormManagerTest, FindForm) { expected.name = ASCIIToUTF16("firstname"); expected.value = ASCIIToUTF16("John"); EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); + EXPECT_FORM_FIELD_EQUALS(expected, field); expected.name = ASCIIToUTF16("lastname"); expected.value = ASCIIToUTF16("Smith"); @@ -802,7 +820,7 @@ TEST_F(FormManagerTest, FillForm) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -811,8 +829,10 @@ TEST_F(FormManagerTest, FillForm) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -933,7 +953,7 @@ TEST_F(FormManagerTest, FAILS_PreviewForm) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -942,8 +962,10 @@ TEST_F(FormManagerTest, FAILS_PreviewForm) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -1888,7 +1910,7 @@ TEST_F(FormManagerTest, FillFormMaxLength) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -1897,8 +1919,10 @@ TEST_F(FormManagerTest, FillFormMaxLength) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -1928,12 +1952,15 @@ TEST_F(FormManagerTest, FillFormMaxLength) { form.fields[0].value = ASCIIToUTF16("Brother"); form.fields[1].value = ASCIIToUTF16("Jonathan"); form.fields[2].value = ASCIIToUTF16("brotherj@example.com"); - form_manager.FillForm(form, WebNode()); + form_manager.FillForm(form, input_element); // Find the newly-filled form that contains the input element. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form2, + &field2)); + EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -1978,7 +2005,7 @@ TEST_F(FormManagerTest, FillFormNegativeMaxLength) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -1987,8 +2014,10 @@ TEST_F(FormManagerTest, FillFormNegativeMaxLength) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -2013,12 +2042,15 @@ TEST_F(FormManagerTest, FillFormNegativeMaxLength) { form.fields[0].value = ASCIIToUTF16("Brother"); form.fields[1].value = ASCIIToUTF16("Jonathan"); form.fields[2].value = ASCIIToUTF16("brotherj@example.com"); - form_manager.FillForm(form, WebNode()); + form_manager.FillForm(form, input_element); // Find the newly-filled form that contains the input element. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form2, + &field2)); + EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -2039,340 +2071,6 @@ TEST_F(FormManagerTest, FillFormNegativeMaxLength) { EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); } -// This test sends a FormData object to FillForm with more fields than are in -// the cached WebFormElement. In this case, we only fill out the fields that -// match between the FormData object and the WebFormElement. -TEST_F(FormManagerTest, FillFormMoreFormDataFields) { - LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" - " <INPUT type=\"text\" id=\"firstname\"/>" - " <INPUT type=\"text\" id=\"middlename\"/>" - " <INPUT type=\"text\" id=\"lastname\"/>" - " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" - "</FORM>"); - - WebFrame* web_frame = GetMainFrame(); - ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame); - - FormManager form_manager; - std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); - ASSERT_EQ(1U, forms.size()); - - // After the field modification, the fields in |form| will look like: - // prefix - // firstname - // hidden - // middlename - // second - // lastname - // postfix - FormData* form = &forms[0]; - - FormField field1; - field1.name = ASCIIToUTF16("prefix"); - field1.form_control_type = ASCIIToUTF16("text"); - field1.max_length = WebInputElement::defaultMaxLength(); - form->fields.insert(form->fields.begin(), field1); - - FormField field2; - field2.name = ASCIIToUTF16("hidden"); - field2.form_control_type = ASCIIToUTF16("text"); - field2.max_length = WebInputElement::defaultMaxLength(); - form->fields.insert(form->fields.begin() + 2, field2); - - FormField field3; - field3.name = ASCIIToUTF16("second"); - field3.form_control_type = ASCIIToUTF16("text"); - field3.max_length = WebInputElement::defaultMaxLength(); - form->fields.insert(form->fields.begin() + 4, field3); - - FormField field4; - field4.name = ASCIIToUTF16("postfix"); - field4.form_control_type = ASCIIToUTF16("text"); - field4.max_length = WebInputElement::defaultMaxLength(); - form->fields.insert(form->fields.begin() + 6, field4); - - // Fill the form. - form->fields[0].value = ASCIIToUTF16("Alpha"); - form->fields[1].value = ASCIIToUTF16("Brother"); - form->fields[2].value = ASCIIToUTF16("Abracadabra"); - form->fields[3].value = ASCIIToUTF16("Joseph"); - form->fields[4].value = ASCIIToUTF16("Beta"); - form->fields[5].value = ASCIIToUTF16("Jonathan"); - form->fields[6].value = ASCIIToUTF16("Omega"); - form_manager.FillForm(*form, WebNode()); - - // Get the input element we want to find. - WebElement element = web_frame->document().getElementById("firstname"); - WebInputElement input_element = element.to<WebInputElement>(); - - // Find the newly-filled form that contains the input element. - FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); - EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); - EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); - EXPECT_EQ(GURL("http://buh.com"), form2.action); - - const std::vector<FormField>& fields = form2.fields; - ASSERT_EQ(3U, fields.size()); - - FormField expected; - expected.form_control_type = ASCIIToUTF16("text"); - expected.max_length = WebInputElement::defaultMaxLength(); - expected.is_autofilled = true; - - expected.name = ASCIIToUTF16("firstname"); - expected.value = ASCIIToUTF16("Brother"); - EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); - - expected.name = ASCIIToUTF16("middlename"); - expected.value = ASCIIToUTF16("Joseph"); - EXPECT_FORM_FIELD_EQUALS(expected, fields[1]); - - expected.name = ASCIIToUTF16("lastname"); - expected.value = ASCIIToUTF16("Jonathan"); - EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); -} - -// This test sends a FormData object to FillForm with fewer fields than are in -// the cached WebFormElement. In this case, we only fill out the fields that -// match between the FormData object and the WebFormElement. -TEST_F(FormManagerTest, FillFormFewerFormDataFields) { - LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" - " <INPUT type=\"text\" id=\"prefix\"/>" - " <INPUT type=\"text\" id=\"firstname\"/>" - " <INPUT type=\"text\" id=\"hidden\"/>" - " <INPUT type=\"text\" id=\"middlename\"/>" - " <INPUT type=\"text\" id=\"second\"/>" - " <INPUT type=\"text\" id=\"lastname\"/>" - " <INPUT type=\"text\" id=\"postfix\"/>" - " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" - "</FORM>"); - - WebFrame* web_frame = GetMainFrame(); - ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame); - - FormManager form_manager; - std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); - ASSERT_EQ(1U, forms.size()); - - // After the field modification, the fields in |form| will look like: - // firstname - // middlename - // lastname - FormData* form = &forms[0]; - form->fields.erase(form->fields.begin()); - form->fields.erase(form->fields.begin() + 1); - form->fields.erase(form->fields.begin() + 2); - form->fields.erase(form->fields.begin() + 3); - - // Fill the form. - form->fields[0].value = ASCIIToUTF16("Brother"); - form->fields[1].value = ASCIIToUTF16("Joseph"); - form->fields[2].value = ASCIIToUTF16("Jonathan"); - form_manager.FillForm(*form, WebNode()); - - // Get the input element we want to find. - WebElement element = web_frame->document().getElementById("firstname"); - WebInputElement input_element = element.to<WebInputElement>(); - - // Find the newly-filled form that contains the input element. - FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); - EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); - EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); - EXPECT_EQ(GURL("http://buh.com"), form2.action); - - const std::vector<FormField>& fields = form2.fields; - ASSERT_EQ(7U, fields.size()); - - FormField expected; - expected.form_control_type = ASCIIToUTF16("text"); - expected.max_length = WebInputElement::defaultMaxLength(); - - expected.name = ASCIIToUTF16("prefix"); - expected.value = string16(); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); - - expected.name = ASCIIToUTF16("firstname"); - expected.value = ASCIIToUTF16("Brother"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[1]); - - expected.name = ASCIIToUTF16("hidden"); - expected.value = string16(); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); - - expected.name = ASCIIToUTF16("middlename"); - expected.value = ASCIIToUTF16("Joseph"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[3]); - - expected.name = ASCIIToUTF16("second"); - expected.value = string16(); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[4]); - - expected.name = ASCIIToUTF16("lastname"); - expected.value = ASCIIToUTF16("Jonathan"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[5]); - - expected.name = ASCIIToUTF16("postfix"); - expected.value = string16(); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[6]); -} - -// This test sends a FormData object to FillForm with a field changed from -// those in the cached WebFormElement. In this case, we only fill out the -// fields that match between the FormData object and the WebFormElement. -TEST_F(FormManagerTest, FillFormChangedFormDataFields) { - LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" - " <INPUT type=\"text\" id=\"firstname\"/>" - " <INPUT type=\"text\" id=\"middlename\"/>" - " <INPUT type=\"text\" id=\"lastname\"/>" - " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" - "</FORM>"); - - WebFrame* web_frame = GetMainFrame(); - ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame); - - FormManager form_manager; - std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); - ASSERT_EQ(1U, forms.size()); - - // After the field modification, the fields in |form| will look like: - // firstname - // middlename - // lastname - FormData* form = &forms[0]; - - // Fill the form. - form->fields[0].value = ASCIIToUTF16("Brother"); - form->fields[1].value = ASCIIToUTF16("Joseph"); - form->fields[2].value = ASCIIToUTF16("Jonathan"); - - // Alter the label and name used for matching. - form->fields[1].label = ASCIIToUTF16("bogus"); - form->fields[1].name = ASCIIToUTF16("bogus"); - - form_manager.FillForm(*form, WebNode()); - - // Get the input element we want to find. - WebElement element = web_frame->document().getElementById("firstname"); - WebInputElement input_element = element.to<WebInputElement>(); - - // Find the newly-filled form that contains the input element. - FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); - EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); - EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); - EXPECT_EQ(GURL("http://buh.com"), form2.action); - - const std::vector<FormField>& fields = form2.fields; - ASSERT_EQ(3U, fields.size()); - - FormField expected; - expected.form_control_type = ASCIIToUTF16("text"); - expected.max_length = WebInputElement::defaultMaxLength(); - - expected.name = ASCIIToUTF16("firstname"); - expected.value = ASCIIToUTF16("Brother"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); - - expected.name = ASCIIToUTF16("middlename"); - expected.value = ASCIIToUTF16(""); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[1]); - - expected.name = ASCIIToUTF16("lastname"); - expected.value = ASCIIToUTF16("Jonathan"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); -} - -// This test sends a FormData object to FillForm with fewer fields than are in -// the cached WebFormElement. In this case, we only fill out the fields that -// match between the FormData object and the WebFormElement. -TEST_F(FormManagerTest, FillFormExtraFieldInCache) { - LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" - " <INPUT type=\"text\" id=\"firstname\"/>" - " <INPUT type=\"text\" id=\"middlename\"/>" - " <INPUT type=\"text\" id=\"lastname\"/>" - " <INPUT type=\"text\" id=\"postfix\"/>" - " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" - "</FORM>"); - - WebFrame* web_frame = GetMainFrame(); - ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame); - - FormManager form_manager; - std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); - ASSERT_EQ(1U, forms.size()); - - // After the field modification, the fields in |form| will look like: - // firstname - // middlename - // lastname - FormData* form = &forms[0]; - form->fields.pop_back(); - - // Fill the form. - form->fields[0].value = ASCIIToUTF16("Brother"); - form->fields[1].value = ASCIIToUTF16("Joseph"); - form->fields[2].value = ASCIIToUTF16("Jonathan"); - form_manager.FillForm(*form, WebNode()); - - // Get the input element we want to find. - WebElement element = web_frame->document().getElementById("firstname"); - WebInputElement input_element = element.to<WebInputElement>(); - - // Find the newly-filled form that contains the input element. - FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); - EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); - EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); - EXPECT_EQ(GURL("http://buh.com"), form2.action); - - const std::vector<FormField>& fields = form2.fields; - ASSERT_EQ(4U, fields.size()); - - FormField expected; - expected.form_control_type = ASCIIToUTF16("text"); - expected.max_length = WebInputElement::defaultMaxLength(); - - expected.name = ASCIIToUTF16("firstname"); - expected.value = ASCIIToUTF16("Brother"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[0]); - - expected.name = ASCIIToUTF16("middlename"); - expected.value = ASCIIToUTF16("Joseph"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[1]); - - expected.name = ASCIIToUTF16("lastname"); - expected.value = ASCIIToUTF16("Jonathan"); - expected.is_autofilled = true; - EXPECT_FORM_FIELD_EQUALS(expected, fields[2]); - - expected.name = ASCIIToUTF16("postfix"); - expected.value = string16(); - expected.is_autofilled = false; - EXPECT_FORM_FIELD_EQUALS(expected, fields[3]); -} - TEST_F(FormManagerTest, FillFormEmptyName) { LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" " <INPUT type=\"text\" id=\"firstname\"/>" @@ -2386,7 +2084,7 @@ TEST_F(FormManagerTest, FillFormEmptyName) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -2395,8 +2093,10 @@ TEST_F(FormManagerTest, FillFormEmptyName) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -2421,12 +2121,15 @@ TEST_F(FormManagerTest, FillFormEmptyName) { form.fields[0].value = ASCIIToUTF16("Wyatt"); form.fields[1].value = ASCIIToUTF16("Earp"); form.fields[2].value = ASCIIToUTF16("wyatt@example.com"); - form_manager.FillForm(form, WebNode()); + form_manager.FillForm(form, input_element); // Find the newly-filled form that contains the input element. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form2, + &field2)); + EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -2469,7 +2172,7 @@ TEST_F(FormManagerTest, FillFormEmptyFormNames) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(2U, forms.size()); // Get the input element we want to find. @@ -2478,8 +2181,10 @@ TEST_F(FormManagerTest, FillFormEmptyFormNames) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(string16(), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://abc.com"), form.action); @@ -2507,12 +2212,15 @@ TEST_F(FormManagerTest, FillFormEmptyFormNames) { form.fields[0].value = ASCIIToUTF16("Red"); form.fields[1].value = ASCIIToUTF16("Yellow"); form.fields[2].value = ASCIIToUTF16("Also Yellow"); - form_manager.FillForm(form, WebNode()); + form_manager.FillForm(form, input_element); // Find the newly-filled form that contains the input element. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form2, + &field2)); + EXPECT_EQ(string16(), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://abc.com"), form2.action); @@ -2559,9 +2267,11 @@ TEST_F(FormManagerTest, ThreePartPhone) { FormData form; EXPECT_TRUE(FormManager::WebFormElementToFormData(forms[0], + WebFormControlElement(), FormManager::REQUIRE_NONE, FormManager::EXTRACT_VALUE, - &form)); + &form, + NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(frame->document().url()), form.origin); EXPECT_EQ(GURL("http://cnn.com"), form.action); @@ -2616,9 +2326,11 @@ TEST_F(FormManagerTest, MaxLengthFields) { FormData form; EXPECT_TRUE(FormManager::WebFormElementToFormData(forms[0], + WebFormControlElement(), FormManager::REQUIRE_NONE, FormManager::EXTRACT_VALUE, - &form)); + &form, + NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(frame->document().url()), form.origin); EXPECT_EQ(GURL("http://cnn.com"), form.action); @@ -2678,7 +2390,7 @@ TEST_F(FormManagerTest, FAILS_FillFormNonEmptyField) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Get the input element we want to find. @@ -2690,8 +2402,10 @@ TEST_F(FormManagerTest, FAILS_FillFormNonEmptyField) { // Find the form that contains the input element. FormData form; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form)); + FormField field; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form, + &field)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(web_frame->document().url()), form.origin); EXPECT_EQ(GURL("http://buh.com"), form.action); @@ -2731,8 +2445,11 @@ TEST_F(FormManagerTest, FAILS_FillFormNonEmptyField) { // Find the newly-filled form that contains the input element. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(input_element, - &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(input_element, + &form2, + &field2)); + EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -2775,7 +2492,7 @@ TEST_F(FormManagerTest, ClearFormWithNode) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Set the auto-filled attribute on the firstname element. @@ -2789,14 +2506,17 @@ TEST_F(FormManagerTest, ClearFormWithNode) { notenabled.setValue(WebString::fromUTF8("no clear")); // Clear the form. - EXPECT_TRUE(form_manager.ClearFormWithNode(firstname)); + EXPECT_TRUE(form_manager.ClearFormWithElement(firstname)); // Verify that the auto-filled attribute has been turned off. EXPECT_FALSE(firstname.isAutofilled()); // Verify the form is cleared. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(firstname, &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(firstname, + &form2, + &field2)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -2848,7 +2568,7 @@ TEST_F(FormManagerTest, ClearFormWithNodeContainingSelectOne) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Set the auto-filled attribute on the firstname element. @@ -2862,14 +2582,17 @@ TEST_F(FormManagerTest, ClearFormWithNodeContainingSelectOne) { select_element.setValue(WebString::fromUTF8("AK")); // Clear the form. - EXPECT_TRUE(form_manager.ClearFormWithNode(firstname)); + EXPECT_TRUE(form_manager.ClearFormWithElement(firstname)); // Verify that the auto-filled attribute has been turned off. EXPECT_FALSE(firstname.isAutofilled()); // Verify the form is cleared. FormData form2; - EXPECT_TRUE(form_manager.FindFormWithFormControlElement(firstname, &form2)); + FormField field2; + EXPECT_TRUE(FormManager::FindFormAndFieldForFormControlElement(firstname, + &form2, + &field2)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name); EXPECT_EQ(GURL(web_frame->document().url()), form2.origin); EXPECT_EQ(GURL("http://buh.com"), form2.action); @@ -2902,7 +2625,7 @@ TEST_F(FormManagerTest, ClearFormWithNodeContainingSelectOne) { EXPECT_EQ(0, firstname.selectionEnd()); } -TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { +TEST_F(FormManagerTest, ClearPreviewedFormWithElement) { LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">" " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>" " <INPUT type=\"text\" id=\"lastname\"/>" @@ -2917,7 +2640,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Set the auto-filled attribute. @@ -2944,7 +2667,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. - EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(lastname, false)); + EXPECT_TRUE(FormManager::ClearPreviewedFormWithElement(lastname, false)); // Fields with empty suggestions suggestions are not modified. EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.value()); @@ -2985,7 +2708,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Set the auto-filled attribute. @@ -3014,7 +2737,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. - EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(firstname, false)); + EXPECT_TRUE(FormManager::ClearPreviewedFormWithElement(firstname, false)); // Fields with non-empty values are restored. EXPECT_EQ(ASCIIToUTF16("W"), firstname.value()); @@ -3053,7 +2776,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); // Set the auto-filled attribute. @@ -3081,7 +2804,7 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. - EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(firstname, true)); + EXPECT_TRUE(FormManager::ClearPreviewedFormWithElement(firstname, true)); // Fields with non-empty values are restored. EXPECT_EQ(ASCIIToUTF16("W"), firstname.value()); @@ -3120,39 +2843,19 @@ TEST_F(FormManagerTest, FormWithNodeIsAutofilled) { FormManager form_manager; std::vector<FormData> forms; - form_manager.ExtractForms(web_frame, &forms); + form_manager.ExtractForms(*web_frame, &forms); ASSERT_EQ(1U, forms.size()); WebInputElement firstname = web_frame->document().getElementById("firstname").to<WebInputElement>(); // Auto-filled attribute not set yet. - EXPECT_FALSE(form_manager.FormWithNodeIsAutofilled(firstname)); + EXPECT_FALSE(FormManager::FormWithElementIsAutofilled(firstname)); // Set the auto-filled attribute. firstname.setAutofilled(true); - EXPECT_TRUE(form_manager.FormWithNodeIsAutofilled(firstname)); -} - -TEST_F(FormManagerTest, LabelForElementHidden) { - LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" - " <LABEL for=\"firstname\"> First name: </LABEL>" - " <INPUT type=\"hidden\" id=\"firstname\" value=\"John\"/>" - " <LABEL for=\"lastname\"> Last name: </LABEL>" - " <INPUT type=\"hidden\" id=\"lastname\" value=\"Smith\"/>" - " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>" - "</FORM>"); - - WebFrame* web_frame = GetMainFrame(); - ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame); - - WebElement e = web_frame->document().getElementById("firstname"); - WebFormControlElement firstname = e.to<WebFormControlElement>(); - - // Hidden form control element should not have a label set. - FormManager form_manager; - EXPECT_EQ(string16(), form_manager.LabelForElement(firstname)); + EXPECT_TRUE(FormManager::FormWithElementIsAutofilled(firstname)); } // If we have multiple labels per id, the labels concatenated into label string. @@ -3215,10 +2918,10 @@ TEST_F(FormManagerTest, SelectOneAsText) { // Extract the country select-one value as text. EXPECT_TRUE(FormManager::WebFormElementToFormData( - forms[0], FormManager::REQUIRE_NONE, + forms[0], WebFormControlElement(), FormManager::REQUIRE_NONE, static_cast<FormManager::ExtractMask>(FormManager::EXTRACT_VALUE | FormManager::EXTRACT_OPTION_TEXT), - &form)); + &form, NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(frame->document().url()), form.origin); EXPECT_EQ(GURL("http://cnn.com"), form.action); @@ -3249,9 +2952,11 @@ TEST_F(FormManagerTest, SelectOneAsText) { form.fields.clear(); // Extract the country select-one value as value. EXPECT_TRUE(FormManager::WebFormElementToFormData(forms[0], + WebFormControlElement(), FormManager::REQUIRE_NONE, FormManager::EXTRACT_VALUE, - &form)); + &form, + NULL)); EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name); EXPECT_EQ(GURL(frame->document().url()), form.origin); EXPECT_EQ(GURL("http://cnn.com"), form.action); |