summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/renderer/form_manager.cc111
-rw-r--r--chrome/renderer/form_manager.h27
-rw-r--r--chrome/renderer/form_manager_unittest.cc135
-rwxr-xr-xchrome/renderer/render_view.cc57
-rw-r--r--chrome/renderer/render_view.h23
5 files changed, 312 insertions, 41 deletions
diff --git a/chrome/renderer/form_manager.cc b/chrome/renderer/form_manager.cc
index 9f2b785..6cc4e17 100644
--- a/chrome/renderer/form_manager.cc
+++ b/chrome/renderer/form_manager.cc
@@ -316,7 +316,7 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element,
continue;
}
- if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element.isEnabled())
+ if (requirements & REQUIRE_ENABLED && !control_element.isEnabled())
continue;
// Create a new FormField, fill it out and map it to the field's name.
@@ -519,14 +519,56 @@ bool FormManager::FillForm(const FormData& form) {
if (!FindCachedFormElement(form, &form_element))
return false;
- ForEachMatchingFormField(
- form_element, form, NewCallback(this, &FormManager::FillFormField));
+ RequirementsMask requirements = static_cast<RequirementsMask>(
+ REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY);
+ ForEachMatchingFormField(form_element,
+ requirements,
+ form,
+ NewCallback(this, &FormManager::FillFormField));
return true;
}
-void FormManager::FillForms(const std::vector<webkit_glue::FormData>& forms) {
- for (std::vector<webkit_glue::FormData>::const_iterator iter = forms.begin();
+bool FormManager::PreviewForm(const FormData& form) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElement(form, &form_element))
+ return false;
+
+ RequirementsMask requirements = static_cast<RequirementsMask>(
+ REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY);
+ ForEachMatchingFormField(form_element,
+ requirements,
+ form,
+ NewCallback(this, &FormManager::PreviewFormField));
+
+ return true;
+}
+
+void FormManager::ClearPreviewedForm(const FormData& form) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElement(form, &form_element))
+ return;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ WebFormControlElement* element = &form_element->control_elements[i];
+
+ // Only input elements can be previewed.
+ if (element->formControlType() != WebString::fromUTF8("text"))
+ continue;
+
+ // If the input element has not been auto-filled, FormManager has not
+ // previewed this field, so we have nothing to reset.
+ WebInputElement input_element = element->to<WebInputElement>();
+ if (!input_element.isAutofilled())
+ continue;
+
+ input_element.setPlaceholder(string16());
+ input_element.setAutofilled(false);
+ }
+}
+
+void FormManager::FillForms(const std::vector<FormData>& forms) {
+ for (std::vector<FormData>::const_iterator iter = forms.begin();
iter != forms.end(); ++iter) {
FillForm(*iter);
}
@@ -582,7 +624,7 @@ bool FormManager::FormElementToFormData(const WebFrame* frame,
continue;
}
- if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element.isEnabled())
+ if (requirements & REQUIRE_ENABLED && !control_element.isEnabled())
continue;
FormField field;
@@ -639,8 +681,10 @@ bool FormManager::FindCachedFormElement(const FormData& form,
return false;
}
-void FormManager::ForEachMatchingFormField(
- FormElement* form, const FormData& data, Callback* callback) {
+void FormManager::ForEachMatchingFormField(FormElement* form,
+ RequirementsMask requirements,
+ const FormData& data,
+ Callback* callback) {
// 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
@@ -666,6 +710,26 @@ void FormManager::ForEachMatchingFormField(
continue;
DCHECK_EQ(data.fields[k].name(), element_name);
+
+ // More than likely |requirements| will contain REQUIRE_AUTOCOMPLETE and/or
+ // REQUIRE_EMPTY, which both require text form control elements, so special-
+ // case this type of element.
+ if (element->formControlType() == WebString::fromUTF8("text")) {
+ const WebInputElement& input_element =
+ element->toConst<WebInputElement>();
+
+ // TODO(jhawkins): WebKit currently doesn't handle the autocomplete
+ // attribute for select control elements, but it probably should.
+ if (requirements & REQUIRE_AUTOCOMPLETE && !input_element.autoComplete())
+ continue;
+
+ if (requirements & REQUIRE_EMPTY && !input_element.value().isEmpty())
+ continue;
+ }
+
+ if (requirements & REQUIRE_ENABLED && !element->isEnabled())
+ continue;
+
callback->Run(element, &data.fields[k]);
// We found a matching form field so move on to the next.
@@ -684,18 +748,6 @@ void FormManager::FillFormField(WebKit::WebFormControlElement* field,
if (field->formControlType() == WebString::fromUTF8("text")) {
WebInputElement input_element = field->to<WebInputElement>();
- // Don't auto-fill a disabled field.
- if (!input_element.isEnabledFormControl())
- return;
-
- // Don't auto-fill a field with autocomplete=off.
- if (!input_element.autoComplete())
- return;
-
- // Don't overwrite a pre-existing value in the field.
- if (!input_element.value().isEmpty())
- return;
-
// If the maxlength attribute contains a negative value, maxLength()
// returns the default maxlength value.
input_element.setValue(data->value().substr(0, input_element.maxLength()));
@@ -705,3 +757,22 @@ void FormManager::FillFormField(WebKit::WebFormControlElement* field,
select_element.setValue(data->value());
}
}
+
+void FormManager::PreviewFormField(WebKit::WebFormControlElement* field,
+ const FormField* data) {
+ // Nothing to preview.
+ if (data->value().empty())
+ return;
+
+ // Only preview input fields.
+ if (field->formControlType() != WebString::fromUTF8("text"))
+ return;
+
+ WebInputElement input_element = field->to<WebInputElement>();
+
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ input_element.setPlaceholder(
+ data->value().substr(0, input_element.maxLength()));
+ input_element.setAutofilled(true);
+}
diff --git a/chrome/renderer/form_manager.h b/chrome/renderer/form_manager.h
index 57ed5af..ad4dee7 100644
--- a/chrome/renderer/form_manager.h
+++ b/chrome/renderer/form_manager.h
@@ -29,7 +29,8 @@ class FormManager {
enum RequirementsMask {
REQUIRE_NONE = 0x0, // No requirements.
REQUIRE_AUTOCOMPLETE = 0x1, // Require that autocomplete != off.
- REQUIRE_ELEMENTS_ENABLED = 0x2 // Require that disabled attribute is off.
+ REQUIRE_ENABLED = 0x2, // Require that disabled attribute is off.
+ REQUIRE_EMPTY = 0x4, // Require that the fields are empty.
};
FormManager();
@@ -91,6 +92,13 @@ class FormManager {
// store multiple forms with the same names from different frames.
bool FillForm(const webkit_glue::FormData& form);
+ // Previews the form represented by |form|. Same conditions as FillForm.
+ bool PreviewForm(const webkit_glue::FormData& form);
+
+ // 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);
+
// Fills all of the forms in the cache with form data from |forms|.
void FillForms(const std::vector<webkit_glue::FormData>& forms);
@@ -134,18 +142,27 @@ class FormManager {
bool FindCachedFormElement(const webkit_glue::FormData& form,
FormElement** form_element);
- // For each field in |form| that matches the corresponding field in the cached
- // FormElement, |callback| is called with the actual WebFormControlElement and
- // the FormField data from |form|. This method owns |callback|.
+ // For each field in |data| that matches the corresponding field in |form|
+ // and meets the |requirements|, |callback| is called with the actual
+ // WebFormControlElement and the FormField data from |form|. This method owns
+ // |callback|.
void ForEachMatchingFormField(FormElement* form,
+ RequirementsMask requirements,
const webkit_glue::FormData& data,
Callback* callback);
// A ForEachMatchingFormField() callback that sets |field|'s value using the
- // value in |data|.
+ // value in |data|. This method also sets the autofill attribute, causing the
+ // background to be yellow.
void FillFormField(WebKit::WebFormControlElement* field,
const webkit_glue::FormField* data);
+ // A ForEachMatchingFormField() callback that sets |field|'s placeholder value
+ // using the value in |data|, causing the test to be greyed-out. This method
+ // also sets the autofill attribute, causing the background to be yellow.
+ void PreviewFormField(WebKit::WebFormControlElement* field,
+ const webkit_glue::FormField* data);
+
// The map of form elements.
WebFrameFormElementMap form_elements_map_;
diff --git a/chrome/renderer/form_manager_unittest.cc b/chrome/renderer/form_manager_unittest.cc
index e267aff..4344499 100644
--- a/chrome/renderer/form_manager_unittest.cc
+++ b/chrome/renderer/form_manager_unittest.cc
@@ -258,7 +258,7 @@ TEST_F(FormManagerTest, GetFormsElementsEnabled) {
form_manager.ExtractForms(web_frame);
std::vector<FormData> forms;
- form_manager.GetForms(FormManager::REQUIRE_ELEMENTS_ENABLED, &forms);
+ form_manager.GetForms(FormManager::REQUIRE_ENABLED, &forms);
ASSERT_EQ(1U, forms.size());
const FormData& form = forms[0];
@@ -426,35 +426,164 @@ TEST_F(FormManagerTest, FillForm) {
WebDocument document = web_frame->document();
WebInputElement firstname =
document.getElementById("firstname").to<WebInputElement>();
- // TODO(jhawkins): Check firstname.isAutofilled() once support has been added
- // in WebKit.
+ EXPECT_TRUE(firstname.isAutofilled());
EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.value());
WebInputElement lastname =
document.getElementById("lastname").to<WebInputElement>();
+ EXPECT_TRUE(lastname.isAutofilled());
EXPECT_EQ(ASCIIToUTF16("Earp"), lastname.value());
// Hidden fields are not filled.
WebInputElement imhidden =
document.getElementById("imhidden").to<WebInputElement>();
+ EXPECT_FALSE(imhidden.isAutofilled());
EXPECT_TRUE(imhidden.value().isEmpty());
// Non-empty fields are not filled.
WebInputElement notempty =
document.getElementById("notempty").to<WebInputElement>();
+ EXPECT_FALSE(notempty.isAutofilled());
EXPECT_EQ(ASCIIToUTF16("Hi"), notempty.value());
// autocomplete=off fields are not filled.
WebInputElement noautocomplete =
document.getElementById("noautocomplete").to<WebInputElement>();
+ EXPECT_FALSE(noautocomplete.isAutofilled());
EXPECT_TRUE(noautocomplete.value().isEmpty());
// Disabled fields are not filled.
WebInputElement notenabled =
document.getElementById("notenabled").to<WebInputElement>();
+ EXPECT_FALSE(notenabled.isAutofilled());
EXPECT_TRUE(notenabled.value().isEmpty());
}
+TEST_F(FormManagerTest, PreviewForm) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+ " <INPUT type=\"text\" id=\"firstname\"/>"
+ " <INPUT type=\"text\" id=\"lastname\"/>"
+ " <INPUT type=\"hidden\" id=\"imhidden\"/>"
+ " <INPUT type=\"text\" id=\"notempty\" value=\"Hi\"/>"
+ " <INPUT type=\"text\" autocomplete=\"off\" id=\"noautocomplete\"/>"
+ " <INPUT type=\"text\" disabled=\"disabled\" id=\"notenabled\"/>"
+ " <INPUT type=\"submit\" name=\"reply-send\" 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());
+
+ // Get the input element we want to find.
+ WebElement element = web_frame->document().getElementById("firstname");
+ WebInputElement input_element = element.to<WebInputElement>();
+
+ // Find the form that contains the input element.
+ FormData form;
+ EXPECT_TRUE(form_manager.FindFormWithFormControlElement(
+ input_element, FormManager::REQUIRE_NONE, &form));
+ EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name);
+ EXPECT_EQ(GURL(web_frame->url()), form.origin);
+ EXPECT_EQ(GURL("http://buh.com"), form.action);
+
+ const std::vector<FormField>& fields = form.fields;
+ ASSERT_EQ(7U, fields.size());
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[0]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("lastname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[1]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("imhidden"),
+ string16(),
+ ASCIIToUTF16("hidden"),
+ 0),
+ fields[2]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("notempty"),
+ ASCIIToUTF16("Hi"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[3]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("noautocomplete"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[4]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("notenabled"),
+ string16(),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[5]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("reply-send"),
+ ASCIIToUTF16("Send"),
+ ASCIIToUTF16("submit"),
+ 0),
+ fields[6]);
+
+ // Preview the form.
+ form.fields[0].set_value(ASCIIToUTF16("Wyatt"));
+ form.fields[1].set_value(ASCIIToUTF16("Earp"));
+ form.fields[2].set_value(ASCIIToUTF16("Alpha"));
+ form.fields[3].set_value(ASCIIToUTF16("Beta"));
+ form.fields[4].set_value(ASCIIToUTF16("Gamma"));
+ form.fields[5].set_value(ASCIIToUTF16("Delta"));
+ EXPECT_TRUE(form_manager.PreviewForm(form));
+
+ // Verify the previewed elements.
+ WebDocument document = web_frame->document();
+ WebInputElement firstname =
+ document.getElementById("firstname").to<WebInputElement>();
+ EXPECT_TRUE(firstname.isAutofilled());
+ EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.placeholder());
+
+ WebInputElement lastname =
+ document.getElementById("lastname").to<WebInputElement>();
+ EXPECT_TRUE(lastname.isAutofilled());
+ EXPECT_EQ(ASCIIToUTF16("Earp"), lastname.placeholder());
+
+ // Hidden fields are not previewed.
+ WebInputElement imhidden =
+ document.getElementById("imhidden").to<WebInputElement>();
+ EXPECT_FALSE(imhidden.isAutofilled());
+ EXPECT_TRUE(imhidden.placeholder().isEmpty());
+
+ // Non-empty fields are not previewed.
+ WebInputElement notempty =
+ document.getElementById("notempty").to<WebInputElement>();
+ EXPECT_FALSE(notempty.isAutofilled());
+ EXPECT_TRUE(notempty.placeholder().isEmpty());
+
+ // autocomplete=off fields are not previewed.
+ WebInputElement noautocomplete =
+ document.getElementById("noautocomplete").to<WebInputElement>();
+ EXPECT_FALSE(noautocomplete.isAutofilled());
+ EXPECT_TRUE(noautocomplete.placeholder().isEmpty());
+
+ // Disabled fields are not previewed.
+ WebInputElement notenabled =
+ document.getElementById("notenabled").to<WebInputElement>();
+ EXPECT_FALSE(notenabled.isAutofilled());
+ EXPECT_TRUE(notenabled.placeholder().isEmpty());
+}
+
TEST_F(FormManagerTest, Reset) {
LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
" <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>"
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index d00d948..a37872e 100755
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -402,7 +402,8 @@ RenderView::RenderView(RenderThreadBase* render_thread,
ALLOW_THIS_IN_INITIALIZER_LIST(translate_helper_(this)),
cross_origin_access_count_(0),
same_origin_access_count_(0),
- ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)) {
+ ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)),
+ autofill_action_(AUTOFILL_NONE) {
ClearBlockedContentSettings();
}
@@ -1505,10 +1506,17 @@ void RenderView::OnAutocompleteSuggestionsReturned(
void RenderView::OnAutoFillFormDataFilled(int query_id,
const webkit_glue::FormData& form) {
- if (query_id != autofill_query_id_)
+ if (!webview() || query_id != autofill_query_id_)
return;
- form_manager_.FillForm(form);
+ DCHECK_NE(AUTOFILL_NONE, autofill_action_);
+
+ if (autofill_action_ == AUTOFILL_FILL)
+ form_manager_.FillForm(form);
+ else if (autofill_action_ == AUTOFILL_PREVIEW)
+ form_manager_.PreviewForm(form);
+
+ autofill_action_ = AUTOFILL_NONE;
}
void RenderView::OnAllowScriptToClose(bool script_can_close) {
@@ -2030,21 +2038,26 @@ void RenderView::removeAutofillSuggestions(const WebString& name,
Send(new ViewHostMsg_RemoveAutofillEntry(routing_id_, name, value));
}
-void RenderView::didAcceptAutoFillSuggestion(
- const WebKit::WebNode& node,
- const WebKit::WebString& value,
- const WebKit::WebString& label) {
- static int query_counter = 0;
- autofill_query_id_ = query_counter++;
+void RenderView::didAcceptAutoFillSuggestion(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label) {
+ QueryAutoFillFormData(node, value, label, AUTOFILL_FILL);
+}
+
+void RenderView::didSelectAutoFillSuggestion(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label) {
+ didClearAutoFillSelection(node);
+ QueryAutoFillFormData(node, value, label, AUTOFILL_PREVIEW);
+}
+void RenderView::didClearAutoFillSelection(const WebKit::WebNode& node) {
webkit_glue::FormData form;
- const WebInputElement element = node.toConst<WebInputElement>();
+ const WebFormControlElement element = node.toConst<WebFormControlElement>();
if (!form_manager_.FindFormWithFormControlElement(
element, FormManager::REQUIRE_NONE, &form))
return;
-
- Send(new ViewHostMsg_FillAutoFillFormData(
- routing_id_, autofill_query_id_, form, value, label));
+ form_manager_.ClearPreviewedForm(form);
}
// WebKit::WebWidgetClient ----------------------------------------------------
@@ -5044,3 +5057,21 @@ bool RenderView::IsNonLocalTopLevelNavigation(
}
return false;
}
+
+void RenderView::QueryAutoFillFormData(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label,
+ AutoFillAction action) {
+ static int query_counter = 0;
+ autofill_query_id_ = query_counter++;
+
+ webkit_glue::FormData form;
+ const WebInputElement element = node.toConst<WebInputElement>();
+ if (!form_manager_.FindFormWithFormControlElement(
+ element, FormManager::REQUIRE_NONE, &form))
+ return;
+
+ autofill_action_ = action;
+ Send(new ViewHostMsg_FillAutoFillFormData(
+ routing_id_, autofill_query_id_, form, value, label));
+}
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 5da8469..60eb2a7 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -282,6 +282,11 @@ class RenderView : public RenderWidget,
const WebKit::WebNode& node,
const WebKit::WebString& value,
const WebKit::WebString& label);
+ virtual void didSelectAutoFillSuggestion(
+ const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label);
+ virtual void didClearAutoFillSelection(const WebKit::WebNode& node);
virtual WebKit::WebNotificationPresenter* GetNotificationPresenter() {
return notification_provider_.get();
@@ -571,6 +576,12 @@ class RenderView : public RenderWidget,
typedef std::map<GURL, ContentSettings> HostContentSettings;
typedef std::map<GURL, int> HostZoomLevels;
+ enum AutoFillAction {
+ AUTOFILL_NONE, // No state set.
+ AUTOFILL_FILL, // Fill the AutoFill form data.
+ AUTOFILL_PREVIEW, // Preview the AutoFill form data.
+ };
+
explicit RenderView(RenderThreadBase* render_thread,
const WebPreferences& webkit_preferences,
int64 session_storage_namespace_id);
@@ -929,6 +940,15 @@ class RenderView : public RenderWidget,
WebKit::WebFrame* frame,
WebKit::WebNavigationType type);
+ // Queries the AutoFillManager for form data for the form containing |node|.
+ // |value| is the current text in the field, and |label| is the selected
+ // profile label. |action| specifies whether to Fill or Preview the values
+ // returned from the AutoFillManager.
+ void QueryAutoFillFormData(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label,
+ AutoFillAction action);
+
// Bitwise-ORed set of extra bindings that have been enabled. See
// BindingsPolicy for details.
int enabled_bindings_;
@@ -1221,6 +1241,9 @@ class RenderView : public RenderWidget,
PepperPluginDelegateImpl pepper_delegate_;
+ // The action to take when receiving AutoFill data from the AutoFillManager.
+ AutoFillAction autofill_action_;
+
DISALLOW_COPY_AND_ASSIGN(RenderView);
};