blob: 76428deaaca3ca1abda27dd26dad60af81b6e2ab [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/browser/autofill_driver_impl.h"
#include "components/autofill/core/browser/autofill_common_test.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_manager_test_delegate.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "ui/base/keycodes/keyboard_codes.h"
using content::RenderViewHost;
// TODO(csharp): Most of this file was just copied from autofill_browsertests.cc
// The repeated code should be moved into a helper file, instead of being
// repeated.
namespace autofill {
static const char* kDataURIPrefix = "data:text/html;charset=utf-8,";
static const char* kTestFormString =
"<form action=\"http://www.example.com/\" method=\"POST\">"
"<label for=\"firstname\">First name:</label>"
" <input type=\"text\" id=\"firstname\""
" onFocus=\"domAutomationController.send(true)\"><br>"
"<label for=\"lastname\">Last name:</label>"
" <input type=\"text\" id=\"lastname\"><br>"
"<label for=\"address1\">Address line 1:</label>"
" <input type=\"text\" id=\"address1\"><br>"
"<label for=\"address2\">Address line 2:</label>"
" <input type=\"text\" id=\"address2\"><br>"
"<label for=\"city\">City:</label>"
" <input type=\"text\" id=\"city\"><br>"
"<label for=\"state\">State:</label>"
" <select id=\"state\">"
" <option value=\"\" selected=\"yes\">--</option>"
" <option value=\"CA\">California</option>"
" <option value=\"TX\">Texas</option>"
" </select><br>"
"<label for=\"zip\">ZIP code:</label>"
" <input type=\"text\" id=\"zip\"><br>"
"<label for=\"country\">Country:</label>"
" <select id=\"country\">"
" <option value=\"\" selected=\"yes\">--</option>"
" <option value=\"CA\">Canada</option>"
" <option value=\"US\">United States</option>"
" </select><br>"
"<label for=\"phone\">Phone number:</label>"
" <input type=\"text\" id=\"phone\"><br>"
"</form>";
class AutofillManagerTestDelegateImpl
: public autofill::AutofillManagerTestDelegate {
public:
AutofillManagerTestDelegateImpl() {}
virtual void DidPreviewFormData() OVERRIDE {
LOG(INFO) << "DidPreviewFormData";
loop_runner_->Quit();
}
virtual void DidFillFormData() OVERRIDE {
LOG(INFO) << "DidFillFormData";
loop_runner_->Quit();
}
virtual void DidShowSuggestions() OVERRIDE {
LOG(INFO) << "DidShowSuggestions";
loop_runner_->Quit();
}
void Reset() {
loop_runner_ = new content::MessageLoopRunner();
}
void Wait() {
LOG(INFO) << "Wait";
loop_runner_->Run();
}
private:
scoped_refptr<content::MessageLoopRunner> loop_runner_;
DISALLOW_COPY_AND_ASSIGN(AutofillManagerTestDelegateImpl);
};
class WindowedPersonalDataManagerObserver
: public PersonalDataManagerObserver,
public content::NotificationObserver {
public:
explicit WindowedPersonalDataManagerObserver(Browser* browser)
: alerted_(false),
has_run_message_loop_(false),
browser_(browser),
infobar_service_(NULL) {
PersonalDataManagerFactory::GetForProfile(browser_->profile())->
AddObserver(this);
registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
content::NotificationService::AllSources());
}
virtual ~WindowedPersonalDataManagerObserver() {
if (infobar_service_ && infobar_service_->infobar_count() > 0)
infobar_service_->RemoveInfoBar(infobar_service_->infobar_at(0));
}
void Wait() {
if (!alerted_) {
has_run_message_loop_ = true;
content::RunMessageLoop();
}
PersonalDataManagerFactory::GetForProfile(browser_->profile())->
RemoveObserver(this);
}
// PersonalDataManagerObserver:
virtual void OnPersonalDataChanged() OVERRIDE {
if (has_run_message_loop_) {
base::MessageLoopForUI::current()->Quit();
has_run_message_loop_ = false;
}
alerted_ = true;
}
virtual void OnInsufficientFormData() OVERRIDE {
OnPersonalDataChanged();
}
// content::NotificationObserver:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE {
// Accept in the infobar.
infobar_service_ = InfoBarService::FromWebContents(
browser_->tab_strip_model()->GetActiveWebContents());
InfoBarDelegate* infobar = infobar_service_->infobar_at(0);
ConfirmInfoBarDelegate* confirm_infobar =
infobar->AsConfirmInfoBarDelegate();
confirm_infobar->Accept();
}
private:
bool alerted_;
bool has_run_message_loop_;
Browser* browser_;
content::NotificationRegistrar registrar_;
InfoBarService* infobar_service_;
};
class TestAutofillExternalDelegate : public AutofillExternalDelegate {
public:
TestAutofillExternalDelegate(content::WebContents* web_contents,
AutofillManager* autofill_manager,
AutofillDriver* autofill_driver)
: AutofillExternalDelegate(web_contents, autofill_manager,
autofill_driver),
keyboard_listener_(NULL) {
}
virtual ~TestAutofillExternalDelegate() {}
virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
AutofillExternalDelegate::OnPopupShown(listener);
keyboard_listener_ = listener;
}
virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE {
keyboard_listener_ = NULL;
AutofillExternalDelegate::OnPopupHidden(listener);
}
content::KeyboardListener* keyboard_listener() {
return keyboard_listener_;
}
private:
// The popup that is currently registered as a keyboard listener, or NULL if
// there is none.
content::KeyboardListener* keyboard_listener_;
DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
};
class AutofillInteractiveTest : public InProcessBrowserTest {
protected:
AutofillInteractiveTest() {}
virtual void SetUpOnMainThread() OVERRIDE {
// Don't want Keychain coming up on Mac.
test::DisableSystemServices(browser()->profile());
// When testing the native UI, hook up a test external delegate, which
// allows us to forward keyboard events to the popup directly.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
AutofillDriverImpl* autofill_driver =
AutofillDriverImpl::FromWebContents(web_contents);
AutofillManager* autofill_manager = autofill_driver->autofill_manager();
scoped_ptr<AutofillExternalDelegate> external_delegate(
new TestAutofillExternalDelegate(web_contents, autofill_manager,
autofill_driver));
autofill_driver->SetAutofillExternalDelegate(external_delegate.Pass());
autofill_manager->SetTestDelegate(&test_delegate_);
}
virtual void CleanUpOnMainThread() OVERRIDE {
// Make sure to close any showing popups prior to tearing down the UI.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
AutofillManager* autofill_manager =
AutofillDriverImpl::FromWebContents(web_contents)->autofill_manager();
autofill_manager->delegate()->HideAutofillPopup();
}
PersonalDataManager* personal_data_manager() {
return PersonalDataManagerFactory::GetForProfile(browser()->profile());
}
void CreateTestProfile() {
AutofillProfile profile;
test::SetProfileInfo(
&profile, "Milton", "C.", "Waddams",
"red.swingline@initech.com", "Initech", "4120 Freidrich Lane",
"Basement", "Austin", "Texas", "78744", "US", "5125551234");
WindowedPersonalDataManagerObserver observer(browser());
personal_data_manager()->AddProfile(profile);
// AddProfile is asynchronous. Wait for it to finish before continuing the
// tests.
observer.Wait();
}
void ExpectFieldValue(const std::string& field_name,
const std::string& expected_value) {
std::string value;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send("
" document.getElementById('" + field_name + "').value);",
&value));
EXPECT_EQ(expected_value, value);
}
RenderViewHost* render_view_host() {
return browser()->tab_strip_model()->GetActiveWebContents()->
GetRenderViewHost();
}
void FocusFirstNameField() {
LOG(WARNING) << "Clicking on the tab.";
content::SimulateMouseClick(
browser()->tab_strip_model()->GetActiveWebContents(),
0,
WebKit::WebMouseEvent::ButtonLeft);
LOG(WARNING) << "Focusing the first name field.";
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
render_view_host(),
"if (document.readyState === 'complete')"
" document.getElementById('firstname').focus();"
"else"
" domAutomationController.send(false);",
&result));
ASSERT_TRUE(result);
}
void ExpectFilledTestForm() {
ExpectFieldValue("firstname", "Milton");
ExpectFieldValue("lastname", "Waddams");
ExpectFieldValue("address1", "4120 Freidrich Lane");
ExpectFieldValue("address2", "Basement");
ExpectFieldValue("city", "Austin");
ExpectFieldValue("state", "TX");
ExpectFieldValue("zip", "78744");
ExpectFieldValue("country", "US");
ExpectFieldValue("phone", "5125551234");
}
void SendKeyToPageAndWait(ui::KeyboardCode key) {
LOG(INFO) << "SendKeyToPageAndWait";
test_delegate_.Reset();
content::SimulateKeyPress(
browser()->tab_strip_model()->GetActiveWebContents(),
key, false, false, false, false);
test_delegate_.Wait();
}
void SendKeyToPopupAndWait(ui::KeyboardCode key) {
// When testing the native UI, route popup-targeted key presses via the
// external delegate.
content::NativeWebKeyboardEvent event;
event.windowsKeyCode = key;
test_delegate_.Reset();
external_delegate()->keyboard_listener()->HandleKeyPressEvent(event);
test_delegate_.Wait();
}
TestAutofillExternalDelegate* external_delegate() {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
AutofillDriverImpl* autofill_driver =
AutofillDriverImpl::FromWebContents(web_contents);
return static_cast<TestAutofillExternalDelegate*>(
autofill_driver->autofill_external_delegate());
}
AutofillManagerTestDelegateImpl test_delegate_;
};
// Temporarily renabled to help track down flakiness. crbug.com/150084
IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, AutofillSelectViaTab) {
CreateTestProfile();
// Load the test page.
ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
GURL(std::string(kDataURIPrefix) + kTestFormString)));
// Focus a fillable field.
FocusFirstNameField();
// Enable all logging to help track down the flakiness.
// TODO(csharp): Remove once this flakiness is fixed.
logging::SetMinLogLevel(0);
// Press the down arrow to initiate Autofill and wait for the popup to be
// shown.
SendKeyToPageAndWait(ui::VKEY_DOWN);
// Press the down arrow to select the suggestion and preview the autofilled
// form.
SendKeyToPopupAndWait(ui::VKEY_DOWN);
// Press tab to accept the autofill suggestions.
SendKeyToPopupAndWait(ui::VKEY_TAB);
// The form should be filled.
ExpectFilledTestForm();
}
} // namespace autofill