summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-13 22:26:30 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-13 22:26:30 +0000
commit631ec0519a7a783dc3061076e828ecbd06401e66 (patch)
treeccff5fe6b2b21ee9a5d6c73c32025581b76e9455 /chrome
parent010bf046498b918d839fdd56fca9d47f3b9171e2 (diff)
downloadchromium_src-631ec0519a7a783dc3061076e828ecbd06401e66.zip
chromium_src-631ec0519a7a783dc3061076e828ecbd06401e66.tar.gz
chromium_src-631ec0519a7a783dc3061076e828ecbd06401e66.tar.bz2
AutoFill: Implement the 'Clear form' menu item in the suggestions popup.
BUG=44616 TEST=FormManagerTest.* Review URL: http://codereview.chromium.org/2752010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49658 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd3
-rw-r--r--chrome/renderer/form_manager.cc78
-rw-r--r--chrome/renderer/form_manager.h16
-rw-r--r--chrome/renderer/form_manager_unittest.cc214
-rwxr-xr-xchrome/renderer/render_view.cc25
5 files changed, 307 insertions, 29 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 8dc89fa..2e0827a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5605,6 +5605,9 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_AUTOFILL_OPTIONS_MENU_ITEM" desc="The entry in the suggestions dropdown that opens the AutoFill dialog.">
AutoFill Options...
</message>
+ <message name="IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM" desc="The entry in the suggestions dropdown that clears an auto-filled form.">
+ Clear form
+ </message>
<message name="IDS_AUTOFILL_DIALOG_TITLE" desc="The title of the AutoFill dialog.">
AutoFill Profiles
</message>
diff --git a/chrome/renderer/form_manager.cc b/chrome/renderer/form_manager.cc
index 3868582..7353b1a 100644
--- a/chrome/renderer/form_manager.cc
+++ b/chrome/renderer/form_manager.cc
@@ -237,7 +237,8 @@ void FormManager::WebFormControlElementToFormField(
// TODO(jhawkins): In WebKit, move value() and setValue() to
// WebFormControlElement.
string16 value;
- if (element.formControlType() == WebString::fromUTF8("text")) {
+ if (element.formControlType() == WebString::fromUTF8("text") ||
+ element.formControlType() == WebString::fromUTF8("hidden")) {
const WebInputElement& input_element =
element.toConst<WebInputElement>();
value = input_element.value();
@@ -546,10 +547,33 @@ bool FormManager::PreviewForm(const FormData& form) {
return true;
}
-void FormManager::ClearPreviewedForm(const FormData& form) {
+bool FormManager::ClearFormWithNode(const WebKit::WebNode& node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ WebFormControlElement element = form_element->control_elements[i];
+ if (element.formControlType() != WebString::fromUTF8("text"))
+ continue;
+
+ WebInputElement input_element = element.to<WebInputElement>();
+
+ // We don't modify the value of disabled fields.
+ if (!input_element.isEnabled())
+ continue;
+
+ input_element.setValue(string16());
+ input_element.setAutofilled(false);
+ }
+
+ return true;
+}
+
+bool FormManager::ClearPreviewedForm(const FormData& form) {
FormElement* form_element = NULL;
if (!FindCachedFormElement(form, &form_element))
- return;
+ return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
WebFormControlElement* element = &form_element->control_elements[i];
@@ -564,9 +588,16 @@ void FormManager::ClearPreviewedForm(const FormData& form) {
if (!input_element.isAutofilled())
continue;
+ // If the user has completed the auto-fill and the values are filled in, we
+ // don't want to reset the auto-filled status.
+ if (!input_element.value().isEmpty())
+ continue;
+
input_element.setPlaceholder(string16());
input_element.setAutofilled(false);
}
+
+ return true;
}
void FormManager::Reset() {
@@ -585,6 +616,24 @@ void FormManager::ResetFrame(const WebFrame* frame) {
}
}
+bool FormManager::FormWithNodeIsAutoFilled(const WebKit::WebNode& node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ WebFormControlElement element = form_element->control_elements[i];
+ if (element.formControlType() != WebString::fromUTF8("text"))
+ continue;
+
+ const WebInputElement& input_element = element.to<WebInputElement>();
+ if (input_element.isAutofilled())
+ return true;
+ }
+
+ return false;
+}
+
// static
bool FormManager::FormElementToFormData(const WebFrame* frame,
const FormElement* form_element,
@@ -605,7 +654,6 @@ bool FormManager::FormElementToFormData(const WebFrame* frame,
if (!form->action.is_valid())
form->action = GURL(form_element->form_element.action());
- // Form elements loop.
for (std::vector<WebFormControlElement>::const_iterator element_iter =
form_element->control_elements.begin();
element_iter != form_element->control_elements.end(); ++element_iter) {
@@ -648,6 +696,28 @@ string16 FormManager::InferLabelForElement(
return inferred_label;
}
+bool FormManager::FindCachedFormElementWithNode(const WebKit::WebNode& node,
+ FormElement** form_element) {
+ for (WebFrameFormElementMap::const_iterator frame_iter =
+ form_elements_map_.begin();
+ frame_iter != form_elements_map_.end(); ++frame_iter) {
+ for (std::vector<FormElement*>::const_iterator form_iter =
+ frame_iter->second.begin();
+ form_iter != frame_iter->second.end(); ++form_iter) {
+ for (std::vector<WebKit::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;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
bool FormManager::FindCachedFormElement(const FormData& form,
FormElement** form_element) {
for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin();
diff --git a/chrome/renderer/form_manager.h b/chrome/renderer/form_manager.h
index 4d6aa5c..bfc639f 100644
--- a/chrome/renderer/form_manager.h
+++ b/chrome/renderer/form_manager.h
@@ -96,9 +96,14 @@ class FormManager {
// Previews the form represented by |form|. Same conditions as FillForm.
bool PreviewForm(const webkit_glue::FormData& form);
+ // 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);
+
// Clears the placeholder values and the auto-filled background for any fields
- // in |form| that have been previewed.
- void ClearPreviewedForm(const webkit_glue::FormData& form);
+ // in |form| that have been previewed. Returns false if the form is not
+ // found.
+ bool ClearPreviewedForm(const webkit_glue::FormData& form);
// Resets the stored set of forms.
void Reset();
@@ -106,6 +111,9 @@ class FormManager {
// Resets the forms for the specified |frame|.
void ResetFrame(const WebKit::WebFrame* frame);
+ // Returns true if |form| has any auto-filled fields.
+ bool FormWithNodeIsAutoFilled(const WebKit::WebNode& node);
+
private:
// Stores the WebFormElement and the form control elements for a form.
struct FormElement {
@@ -136,6 +144,10 @@ class FormManager {
static string16 InferLabelForElement(
const WebKit::WebFormControlElement& element);
+ // Finds the cached FormElement that contains |node|.
+ bool FindCachedFormElementWithNode(const WebKit::WebNode& node,
+ FormElement** form_element);
+
// Uses the data in |form| to find the cached FormElement.
bool FindCachedFormElement(const webkit_glue::FormData& form,
FormElement** form_element);
diff --git a/chrome/renderer/form_manager_unittest.cc b/chrome/renderer/form_manager_unittest.cc
index 1731f47..e5b9ffd 100644
--- a/chrome/renderer/form_manager_unittest.cc
+++ b/chrome/renderer/form_manager_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/string_util.h"
#include "chrome/renderer/form_manager.h"
#include "chrome/test/render_view_test.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -34,6 +35,7 @@ TEST_F(FormManagerTest, WebFormElementToFormData) {
LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
" <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>"
" <INPUT type=\"text\" id=\"lastname\" value=\"Smith\"/>"
+ " <INPUT type=\"hidden\" id=\"notvisible\" value=\"apple\"/>"
" <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
"</FORM>");
@@ -54,25 +56,31 @@ TEST_F(FormManagerTest, WebFormElementToFormData) {
EXPECT_EQ(GURL("http://cnn.com"), form.action);
const std::vector<FormField>& fields = form.fields;
- ASSERT_EQ(3U, fields.size());
- EXPECT_EQ(FormField(string16(),
- ASCIIToUTF16("firstname"),
- ASCIIToUTF16("John"),
- ASCIIToUTF16("text"),
- 20),
- fields[0]);
- EXPECT_EQ(FormField(string16(),
- ASCIIToUTF16("lastname"),
- ASCIIToUTF16("Smith"),
- ASCIIToUTF16("text"),
- 20),
- fields[1]);
- EXPECT_EQ(FormField(string16(),
- ASCIIToUTF16("reply-send"),
- ASCIIToUTF16("Send"),
- ASCIIToUTF16("submit"),
- 0),
- fields[2]);
+ ASSERT_EQ(4U, fields.size());
+ EXPECT_TRUE(fields[0].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ ASCIIToUTF16("John"),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields[1].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("lastname"),
+ ASCIIToUTF16("Smith"),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields[2].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("notvisible"),
+ ASCIIToUTF16("apple"),
+ ASCIIToUTF16("hidden"),
+ 0)));
+ EXPECT_TRUE(fields[3].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("reply-send"),
+ string16(),
+ ASCIIToUTF16("submit"),
+ 0)));
}
TEST_F(FormManagerTest, ExtractForms) {
@@ -2164,4 +2172,172 @@ TEST_F(FormManagerTest, FillFormNonEmptyField) {
fields2[2]);
}
+TEST_F(FormManagerTest, ClearFormWithNode) {
+ LoadHTML(
+ "<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+ " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>"
+ " <INPUT type=\"text\" id=\"lastname\" value=\"Earp\"/>"
+ " <INPUT type=\"text\" autocomplete=\"off\" id=\"noAC\" value=\"one\"/>"
+ " <INPUT type=\"text\" id=\"notenabled\" disabled=\"disabled\">"
+ " <INPUT type=\"hidden\" id=\"notvisible\" value=\"apple\">"
+ " <INPUT type=\"submit\" value=\"Send\"/>"
+ "</FORM>");
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+ FormManager form_manager;
+ form_manager.ExtractForms(web_frame);
+
+ // Verify that we have the form.
+ std::vector<FormData> forms;
+ form_manager.GetForms(FormManager::REQUIRE_NONE, &forms);
+ ASSERT_EQ(1U, forms.size());
+
+ // Set the auto-filled attribute on the firstname element.
+ WebInputElement firstname =
+ web_frame->document().getElementById("firstname").to<WebInputElement>();
+ firstname.setAutofilled(true);
+
+ // Set the value of the disabled attribute.
+ WebInputElement notenabled =
+ web_frame->document().getElementById("notenabled").to<WebInputElement>();
+ notenabled.setValue(WebString::fromUTF8("no clear"));
+
+ // Clear the form.
+ EXPECT_TRUE(form_manager.ClearFormWithNode(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, FormManager::REQUIRE_NONE, &form2));
+ EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name);
+ EXPECT_EQ(GURL(web_frame->url()), form2.origin);
+ EXPECT_EQ(GURL("http://buh.com"), form2.action);
+
+ const std::vector<FormField>& fields2 = form2.fields;
+ ASSERT_EQ(6U, fields2.size());
+ EXPECT_TRUE(fields2[0].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields2[1].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("lastname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields2[2].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("noAC"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields2[3].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("notenabled"),
+ ASCIIToUTF16("no clear"),
+ ASCIIToUTF16("text"),
+ 20)));
+ EXPECT_TRUE(fields2[4].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("notvisible"),
+ ASCIIToUTF16("apple"),
+ ASCIIToUTF16("hidden"),
+ 0)));
+ EXPECT_TRUE(fields2[5].StrictlyEqualsHack(
+ FormField(string16(),
+ string16(),
+ string16(),
+ ASCIIToUTF16("submit"),
+ 0)));
+}
+
+TEST_F(FormManagerTest, ClearPreviewedForm) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+ " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>"
+ " <INPUT type=\"text\" id=\"lastname\"/>"
+ " <INPUT type=\"text\" id=\"email\"/>"
+ " <INPUT type=\"submit\" value=\"Send\"/>"
+ "</FORM>");
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+ FormManager form_manager;
+ form_manager.ExtractForms(web_frame);
+
+ // Verify that we have the form.
+ std::vector<FormData> forms;
+ form_manager.GetForms(FormManager::REQUIRE_NONE, &forms);
+ ASSERT_EQ(1U, forms.size());
+
+ // Set the auto-filled attribute.
+ WebInputElement firstname =
+ web_frame->document().getElementById("firstname").to<WebInputElement>();
+ firstname.setAutofilled(true);
+ WebInputElement lastname =
+ web_frame->document().getElementById("lastname").to<WebInputElement>();
+ lastname.setAutofilled(true);
+ WebInputElement email =
+ web_frame->document().getElementById("email").to<WebInputElement>();
+ email.setAutofilled(true);
+
+ // Set the placeholder values on two of the elements.
+ lastname.setPlaceholder(ASCIIToUTF16("Earp"));
+ email.setPlaceholder(ASCIIToUTF16("wyatt@earp.com"));
+
+ // Clear the previewed fields.
+ EXPECT_TRUE(form_manager.ClearPreviewedForm(forms[0]));
+
+ // Fields with non-empty values are not modified.
+ EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.value());
+ EXPECT_TRUE(firstname.placeholder().isEmpty());
+ EXPECT_TRUE(firstname.isAutofilled());
+
+ // Verify the previewed fields are cleared.
+ EXPECT_TRUE(lastname.value().isEmpty());
+ EXPECT_TRUE(lastname.placeholder().isEmpty());
+ EXPECT_FALSE(lastname.isAutofilled());
+ EXPECT_TRUE(email.value().isEmpty());
+ EXPECT_TRUE(email.placeholder().isEmpty());
+ EXPECT_FALSE(email.isAutofilled());
+}
+
+TEST_F(FormManagerTest, FormWithNodeIsAutoFilled) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+ " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>"
+ " <INPUT type=\"text\" id=\"lastname\"/>"
+ " <INPUT type=\"text\" id=\"email\"/>"
+ " <INPUT type=\"submit\" value=\"Send\"/>"
+ "</FORM>");
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+ FormManager form_manager;
+ form_manager.ExtractForms(web_frame);
+
+ // Verify that we have the form.
+ std::vector<FormData> forms;
+ form_manager.GetForms(FormManager::REQUIRE_NONE, &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));
+
+ // Set the auto-filled attribute.
+ firstname.setAutofilled(true);
+
+ EXPECT_TRUE(form_manager.FormWithNodeIsAutoFilled(firstname));
+}
+
} // namespace
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 78f1926..29f0f1a 100755
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -1518,11 +1518,22 @@ void RenderView::OnAutoFillSuggestionsReturned(
if (webview() && query_id == autofill_query_id_) {
std::vector<string16> v(values);
std::vector<string16> l(labels);
+ int separator_index = v.size();
+
+ // The form has been auto-filled, so give the user the chance to clear the
+ // form.
+ if (form_manager_.FormWithNodeIsAutoFilled(autofill_query_node_)) {
+ v.push_back(l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
+ l.push_back(string16());
+ }
+
+ // Append the 'AutoFill Options...' menu item.
v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_MENU_ITEM));
l.push_back(string16());
suggestions_count_ = v.size();
webview()->applyAutoFillSuggestions(
- autofill_query_node_, v, l, v.size() - 1);
+ autofill_query_node_, v, l, separator_index);
}
}
@@ -2112,11 +2123,17 @@ void RenderView::didAcceptAutoFillSuggestion(const WebKit::WebNode& node,
unsigned index) {
DCHECK_NE(0U, suggestions_count_);
- // User selected 'AutoFill Options...'.
- if (index == suggestions_count_ - 1)
+ if (index == suggestions_count_ - 1) {
+ // User selected 'AutoFill Options...'.
Send(new ViewHostMsg_ShowAutoFillDialog(routing_id_));
- else
+ } else if (form_manager_.FormWithNodeIsAutoFilled(node) &&
+ index == suggestions_count_ - 2) {
+ // The form has been auto-filled, so give the user the chance to clear the
+ // form.
+ form_manager_.ClearFormWithNode(node);
+ } else {
QueryAutoFillFormData(node, value, label, AUTOFILL_FILL);
+ }
suggestions_count_ = 0;
}