1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/autofill/content/renderer/form_cache.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "components/autofill/content/renderer/focus_test_utils.h"
9 #include "components/autofill/content/renderer/form_autofill_util.h"
10 #include "components/autofill/core/common/form_field_data.h"
11 #include "content/public/test/render_view_test.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/blink/public/web/web_document.h"
14 #include "third_party/blink/public/web/web_input_element.h"
15 #include "third_party/blink/public/web/web_local_frame.h"
16 #include "third_party/blink/public/web/web_select_element.h"
17
18 using base::ASCIIToUTF16;
19 using blink::WebDocument;
20 using blink::WebElement;
21 using blink::WebInputElement;
22 using blink::WebSelectElement;
23 using blink::WebString;
24
25 namespace autofill {
26
GetFormByName(const std::vector<FormData> & forms,base::StringPiece name)27 const FormData* GetFormByName(const std::vector<FormData>& forms,
28 base::StringPiece name) {
29 for (const FormData& form : forms) {
30 if (form.name == ASCIIToUTF16(name))
31 return &form;
32 }
33 return nullptr;
34 }
35
36 class FormCacheBrowserTest : public content::RenderViewTest {
37 public:
FormCacheBrowserTest()38 FormCacheBrowserTest() {
39 focus_test_utils_ = std::make_unique<test::FocusTestUtils>(
40 base::BindRepeating(&FormCacheBrowserTest::ExecuteJavaScriptForTests,
41 base::Unretained(this)));
42 }
43 ~FormCacheBrowserTest() override = default;
44 FormCacheBrowserTest(const FormCacheBrowserTest&) = delete;
45 FormCacheBrowserTest& operator=(const FormCacheBrowserTest&) = delete;
46
47 protected:
GetFocusLog()48 std::string GetFocusLog() {
49 return focus_test_utils_->GetFocusLog(GetMainFrame()->GetDocument());
50 }
51
52 std::unique_ptr<test::FocusTestUtils> focus_test_utils_;
53 };
54
TEST_F(FormCacheBrowserTest,ExtractForms)55 TEST_F(FormCacheBrowserTest, ExtractForms) {
56 LoadHTML(R"(
57 <form id="form1">
58 <input type="text" name="foo1">
59 <input type="text" name="foo2">
60 <input type="text" name="foo3">
61 </form>
62 <input type="text" name="unowned_element">
63 )");
64
65 FormCache form_cache(GetMainFrame());
66 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
67
68 const FormData* form1 = GetFormByName(forms, "form1");
69 ASSERT_TRUE(form1);
70 EXPECT_EQ(3u, form1->fields.size());
71
72 const FormData* unowned_form = GetFormByName(forms, "");
73 ASSERT_TRUE(unowned_form);
74 EXPECT_EQ(1u, unowned_form->fields.size());
75 }
76
TEST_F(FormCacheBrowserTest,ExtractFormsTwice)77 TEST_F(FormCacheBrowserTest, ExtractFormsTwice) {
78 LoadHTML(R"(
79 <form id="form1">
80 <input type="text" name="foo1">
81 <input type="text" name="foo2">
82 <input type="text" name="foo3">
83 </form>
84 <input type="text" name="unowned_element">
85 )");
86
87 FormCache form_cache(GetMainFrame());
88 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
89
90 forms = form_cache.ExtractNewForms(nullptr);
91 // As nothing has changed, there are no new forms and |forms| should be empty.
92 EXPECT_TRUE(forms.empty());
93 }
94
TEST_F(FormCacheBrowserTest,ExtractFormsAfterModification)95 TEST_F(FormCacheBrowserTest, ExtractFormsAfterModification) {
96 LoadHTML(R"(
97 <form id="form1">
98 <input type="text" name="foo1">
99 <input type="text" name="foo2">
100 <input type="text" name="foo3">
101 </form>
102 <input type="text" name="unowned_element">
103 )");
104
105 FormCache form_cache(GetMainFrame());
106 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
107
108 // Append an input element to the form and to the list of unowned inputs.
109 ExecuteJavaScriptForTests(R"(
110 var new_input_1 = document.createElement("input");
111 new_input_1.setAttribute("type", "text");
112 new_input_1.setAttribute("name", "foo4");
113
114 var form1 = document.getElementById("form1");
115 form1.appendChild(new_input_1);
116
117 var new_input_2 = document.createElement("input");
118 new_input_2.setAttribute("type", "text");
119 new_input_2.setAttribute("name", "unowned_element_2");
120 document.body.appendChild(new_input_2);
121 )");
122
123 forms = form_cache.ExtractNewForms(nullptr);
124
125 const FormData* form1 = GetFormByName(forms, "form1");
126 ASSERT_TRUE(form1);
127 EXPECT_EQ(4u, form1->fields.size());
128
129 const FormData* unowned_form = GetFormByName(forms, "");
130 ASSERT_TRUE(unowned_form);
131 EXPECT_EQ(2u, unowned_form->fields.size());
132 }
133
TEST_F(FormCacheBrowserTest,FillAndClear)134 TEST_F(FormCacheBrowserTest, FillAndClear) {
135 LoadHTML(R"(
136 <input type="text" name="text" id="text">
137 <input type="checkbox" checked name="checkbox" id="checkbox">
138 <select name="select" id="select">
139 <option value="first">first</option>
140 <option value="second" selected>second</option>
141 </select>
142 )");
143
144 FormCache form_cache(GetMainFrame());
145 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
146
147 ASSERT_EQ(1u, forms.size());
148 FormData values_to_fill = forms[0];
149 values_to_fill.fields[0].value = ASCIIToUTF16("test");
150 values_to_fill.fields[0].is_autofilled = true;
151 values_to_fill.fields[1].check_status =
152 FormFieldData::CheckStatus::kCheckableButUnchecked;
153 values_to_fill.fields[1].is_autofilled = true;
154 values_to_fill.fields[2].value = ASCIIToUTF16("first");
155 values_to_fill.fields[2].is_autofilled = true;
156
157 WebDocument doc = GetMainFrame()->GetDocument();
158 auto text = doc.GetElementById("text").To<WebInputElement>();
159 auto checkbox = doc.GetElementById("checkbox").To<WebInputElement>();
160 auto select_element = doc.GetElementById("select").To<WebSelectElement>();
161
162 form_util::FillForm(values_to_fill, text);
163
164 EXPECT_EQ("test", text.Value().Ascii());
165 EXPECT_FALSE(checkbox.IsChecked());
166 EXPECT_EQ("first", select_element.Value().Ascii());
167
168 // Validate that clearing works, in particular that the previous values
169 // were saved correctly.
170 form_cache.ClearSectionWithElement(text);
171
172 EXPECT_EQ("", text.Value().Ascii());
173 EXPECT_TRUE(checkbox.IsChecked());
174 EXPECT_EQ("second", select_element.Value().Ascii());
175 }
176
177 // Tests that correct focus, change and blur events are emitted during the
178 // autofilling and clearing of the form with an initially focused element.
TEST_F(FormCacheBrowserTest,VerifyFocusAndBlurEventsAfterAutofillAndClearingWithFocusElement)179 TEST_F(FormCacheBrowserTest,
180 VerifyFocusAndBlurEventsAfterAutofillAndClearingWithFocusElement) {
181 // Load a form.
182 LoadHTML(
183 "<html><form id='myForm'>"
184 "<label>First Name:</label><input id='fname' name='0'/><br/>"
185 "<label>Last Name:</label> <input id='lname' name='1'/><br/>"
186 "</form></html>");
187
188 focus_test_utils_->SetUpFocusLogging();
189 focus_test_utils_->FocusElement("fname");
190
191 FormCache form_cache(GetMainFrame());
192 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
193
194 ASSERT_EQ(2u, forms.size());
195 FormData values_to_fill = forms[0];
196 values_to_fill.fields[0].value = ASCIIToUTF16("John");
197 values_to_fill.fields[0].is_autofilled = true;
198 values_to_fill.fields[1].value = ASCIIToUTF16("Smith");
199 values_to_fill.fields[1].is_autofilled = true;
200
201 auto fname = GetMainFrame()
202 ->GetDocument()
203 .GetElementById("fname")
204 .To<WebInputElement>();
205
206 // Simulate filling the form using Autofill.
207 form_util::FillForm(values_to_fill, fname);
208
209 // Simulate clearing the form.
210 form_cache.ClearSectionWithElement(fname);
211
212 // Expected Result in order:
213 // - from filling
214 // * Change fname
215 // * Blur fname
216 // * Focus lname
217 // * Change lname
218 // * Blur lname
219 // * Focus fname
220 // - from clearing
221 // * Change fname
222 // * Blur fname
223 // * Focus lname
224 // * Change lname
225 // * Blur lname
226 // * Focus fname
227 EXPECT_EQ(GetFocusLog(), "c0b0f1c1b1f0c0b0f1c1b1f0");
228 }
229
TEST_F(FormCacheBrowserTest,FreeDataOnElementRemoval)230 TEST_F(FormCacheBrowserTest, FreeDataOnElementRemoval) {
231 LoadHTML(R"(
232 <div id="container">
233 <input type="text" name="text" id="text">
234 <input type="checkbox" checked name="checkbox" id="checkbox">
235 <select name="select" id="select">
236 <option value="first">first</option>
237 <option value="second" selected>second</option>
238 </select>
239 </div>
240 )");
241
242 FormCache form_cache(GetMainFrame());
243 form_cache.ExtractNewForms(nullptr);
244
245 EXPECT_EQ(1u, form_cache.initial_select_values_.size());
246 EXPECT_EQ(1u, form_cache.initial_checked_state_.size());
247
248 ExecuteJavaScriptForTests(R"(
249 const container = document.getElementById('container');
250 while (container.childElementCount > 0) {
251 container.removeChild(container.children.item(0));
252 }
253 )");
254
255 std::vector<FormData> forms = form_cache.ExtractNewForms(nullptr);
256 EXPECT_EQ(0u, forms.size());
257 EXPECT_EQ(0u, form_cache.initial_select_values_.size());
258 EXPECT_EQ(0u, form_cache.initial_checked_state_.size());
259 }
260
261 } // namespace autofill
262