910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/base_paths.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/content/renderer/autofill_renderer_test.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "third_party/blink/public/web/web_view.h"

using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::Property;
using ::testing::ResultOf;
using ::testing::SizeIs;
using ::testing::Truly;

namespace autofill {

namespace {

auto FormField(
    const std::string& name,
    const std::string& value = "",
    FormControlType form_control_type = FormControlType::kInputText) {
  return AllOf(
      Property("name", &FormFieldData::name, base::UTF8ToUTF16(name)),
      Property("value", &FormFieldData::value, base::UTF8ToUTF16(value)),
      Property("form_control_type", &FormFieldData::form_control_type,
               form_control_type));
}

auto FormWithFields(auto matcher) {
  return ElementsAre(Property(&FormData::fields, matcher));
}

auto Nth(int index, auto matcher) {
  return ResultOf(
      base::StringPrintf("array[%d]", index),
      [index](auto& container) { return container[index]; }, matcher);
}

using AutofillRendererTest = test::AutofillRendererTest;

TEST_F(AutofillRendererTest, SendForms) {
  EXPECT_CALL(
      autofill_driver(),
      FormsSeen(FormWithFields(ElementsAre(
                    FormField("firstname"), FormField("middlename"),
                    FormField("lastname"),
                    FormField("state", "?", FormControlType::kSelectOne))),
                IsEmpty()));
  LoadHTML(R"(<form method='POST'>
                <input type='text' id='firstname'/>
                <input type='text' id='middlename'/>
                <input type='text' id='lastname' autocomplete='off'/>
                <input type='hidden' id='email'/>
                <select id='state'/>
                  <option>?</option>
                  <option>California</option>
                  <option>Texas</option>
                </select>
              </form>)");
  WaitForFormsSeen();

  // Dynamically create a new form. A new message should be sent for it, but
  // not for the previous form.
  EXPECT_CALL(autofill_driver(),
              FormsSeen(FormWithFields(ElementsAre(
                            FormField("second_firstname", "Bob"),
                            FormField("second_lastname", "Hope"),
                            FormField("second_email", "bobhope@example.com"))),
                        IsEmpty()));
  ExecuteJavaScriptForTests(
      R"(var newForm=document.createElement('form');
         newForm.id='new_testform';
         newForm.action='http://google.com';
         newForm.method='post';
         var newFirstname=document.createElement('input');
         newFirstname.setAttribute('type', 'text');
         newFirstname.setAttribute('id', 'second_firstname');
         newFirstname.value = 'Bob';
         var newLastname=document.createElement('input');
         newLastname.setAttribute('type', 'text');
         newLastname.setAttribute('id', 'second_lastname');
         newLastname.value = 'Hope';
         var newEmail=document.createElement('input');
         newEmail.setAttribute('type', 'text');
         newEmail.setAttribute('id', 'second_email');
         newEmail.value = 'bobhope@example.com';
         newForm.appendChild(newFirstname);
         newForm.appendChild(newLastname);
         newForm.appendChild(newEmail);
         document.body.appendChild(newForm);)");
  WaitForFormsSeen();
}

// Regression test for [ http://crbug.com/346010 ].
// Shouldn't crash.
TEST_F(AutofillRendererTest, DontCrashWhileAssociatingForms) {
  EXPECT_CALL(autofill_driver(), FormsSeen(_, _)).Times(0);
  LoadHTML(R"(<form id='form'>
              <foo id='foo'>
              <script id='script'>
              document.documentElement.appendChild(foo);
              newDoc = document.implementation.createDocument(
                  'http://www.w3.org/1999/xhtml', 'html');
              foo.insertBefore(form, script);
              newDoc.adoptNode(foo);
              </script>)");
  WaitForFormsSeen();
}

TEST_F(AutofillRendererTest, DynamicallyAddedUnownedFormElements) {
  base::FilePath test_data_dir;
  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir));
  base::FilePath test_path = test_data_dir.AppendASCII(
      "chrome/test/data/autofill/autofill_noform_dynamic.html");
  std::string html_data;
  ASSERT_TRUE(base::ReadFileToString(test_path, &html_data));

  EXPECT_CALL(autofill_driver(),
              FormsSeen(FormWithFields(SizeIs(7)), IsEmpty()));
  LoadHTML(html_data.c_str());
  WaitForFormsSeen();

  EXPECT_CALL(autofill_driver(),
              FormsSeen(FormWithFields(AllOf(
                            SizeIs(9), Nth(7, FormField("EMAIL_ADDRESS")),
                            Nth(8, FormField("PHONE_HOME_WHOLE_NUMBER")))),
                        IsEmpty()));
  ExecuteJavaScriptForTests("AddFields()");
  WaitForFormsSeen();
}

TEST_F(AutofillRendererTest, IgnoreNonUserGestureTextFieldChanges) {
  EXPECT_CALL(autofill_driver(), TextFieldValueChanged).Times(0);
  LoadHTML(R"(<form method='post'>
                <input type='text' id='full_name'/>
              </form>)");
  WaitForFormsSeen();

  blink::WebInputElement full_name = GetMainFrame()
                                         ->GetDocument()
                                         .GetElementById("full_name")
                                         .To<blink::WebInputElement>();
  while (!full_name.Focused())
    GetMainFrame()->View()->AdvanceFocus(false);

  full_name.SetValue("Alice", true);
  GetMainFrame()->AutofillClient()->TextFieldValueChanged(full_name);

  EXPECT_CALL(autofill_driver(), TextFieldValueChanged);
  SimulateUserInputChangeForElement(full_name, "Alice");
}

}  // namespace

}  // namespace autofill