autofill_interactive_uitest.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1// Copyright (c) 2013 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 "base/memory/ref_counted.h"
6#include "base/memory/scoped_ptr.h"
7#include "chrome/browser/autofill/personal_data_manager_factory.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/infobars/confirm_infobar_delegate.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/browser/ui/tabs/tab_strip_model.h"
13#include "chrome/test/base/in_process_browser_test.h"
14#include "chrome/test/base/ui_test_utils.h"
15#include "components/autofill/content/browser/autofill_driver_impl.h"
16#include "components/autofill/core/browser/autofill_common_test.h"
17#include "components/autofill/core/browser/autofill_external_delegate.h"
18#include "components/autofill/core/browser/autofill_manager.h"
19#include "components/autofill/core/browser/autofill_manager_test_delegate.h"
20#include "components/autofill/core/browser/autofill_profile.h"
21#include "components/autofill/core/browser/personal_data_manager.h"
22#include "components/autofill/core/browser/personal_data_manager_observer.h"
23#include "content/public/browser/notification_observer.h"
24#include "content/public/browser/notification_registrar.h"
25#include "content/public/browser/notification_service.h"
26#include "content/public/browser/render_view_host.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/test/browser_test_utils.h"
29#include "content/public/test/test_utils.h"
30#include "ui/base/keycodes/keyboard_codes.h"
31
32using content::RenderViewHost;
33
34// TODO(csharp): Most of this file was just copied from autofill_browsertests.cc
35// The repeated code should be moved into a helper file, instead of being
36// repeated.
37
38namespace autofill {
39
40static const char* kDataURIPrefix = "data:text/html;charset=utf-8,";
41static const char* kTestFormString =
42    "<form action=\"http://www.example.com/\" method=\"POST\">"
43    "<label for=\"firstname\">First name:</label>"
44    " <input type=\"text\" id=\"firstname\""
45    "        onFocus=\"domAutomationController.send(true)\"><br>"
46    "<label for=\"lastname\">Last name:</label>"
47    " <input type=\"text\" id=\"lastname\"><br>"
48    "<label for=\"address1\">Address line 1:</label>"
49    " <input type=\"text\" id=\"address1\"><br>"
50    "<label for=\"address2\">Address line 2:</label>"
51    " <input type=\"text\" id=\"address2\"><br>"
52    "<label for=\"city\">City:</label>"
53    " <input type=\"text\" id=\"city\"><br>"
54    "<label for=\"state\">State:</label>"
55    " <select id=\"state\">"
56    " <option value=\"\" selected=\"yes\">--</option>"
57    " <option value=\"CA\">California</option>"
58    " <option value=\"TX\">Texas</option>"
59    " </select><br>"
60    "<label for=\"zip\">ZIP code:</label>"
61    " <input type=\"text\" id=\"zip\"><br>"
62    "<label for=\"country\">Country:</label>"
63    " <select id=\"country\">"
64    " <option value=\"\" selected=\"yes\">--</option>"
65    " <option value=\"CA\">Canada</option>"
66    " <option value=\"US\">United States</option>"
67    " </select><br>"
68    "<label for=\"phone\">Phone number:</label>"
69    " <input type=\"text\" id=\"phone\"><br>"
70    "</form>";
71
72class AutofillManagerTestDelegateImpl
73    : public autofill::AutofillManagerTestDelegate {
74 public:
75  AutofillManagerTestDelegateImpl() {}
76
77  virtual void DidPreviewFormData() OVERRIDE {
78    loop_runner_->Quit();
79  }
80
81  virtual void DidFillFormData() OVERRIDE {
82    loop_runner_->Quit();
83  }
84
85  virtual void DidShowSuggestions() OVERRIDE {
86    loop_runner_->Quit();
87  }
88
89  void Reset() {
90    loop_runner_ = new content::MessageLoopRunner();
91  }
92
93  void Wait() {
94    loop_runner_->Run();
95  }
96
97 private:
98  scoped_refptr<content::MessageLoopRunner> loop_runner_;
99
100  DISALLOW_COPY_AND_ASSIGN(AutofillManagerTestDelegateImpl);
101};
102
103class WindowedPersonalDataManagerObserver
104    : public PersonalDataManagerObserver,
105      public content::NotificationObserver {
106 public:
107  explicit WindowedPersonalDataManagerObserver(Browser* browser)
108      : alerted_(false),
109        has_run_message_loop_(false),
110        browser_(browser),
111        infobar_service_(NULL) {
112    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
113        AddObserver(this);
114    registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
115                   content::NotificationService::AllSources());
116  }
117
118  virtual ~WindowedPersonalDataManagerObserver() {
119    if (infobar_service_ && infobar_service_->infobar_count() > 0)
120      infobar_service_->RemoveInfoBar(infobar_service_->infobar_at(0));
121  }
122
123  void Wait() {
124    if (!alerted_) {
125      has_run_message_loop_ = true;
126      content::RunMessageLoop();
127    }
128    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
129        RemoveObserver(this);
130  }
131
132  // PersonalDataManagerObserver:
133  virtual void OnPersonalDataChanged() OVERRIDE {
134    if (has_run_message_loop_) {
135      base::MessageLoopForUI::current()->Quit();
136      has_run_message_loop_ = false;
137    }
138    alerted_ = true;
139  }
140
141  virtual void OnInsufficientFormData() OVERRIDE {
142    OnPersonalDataChanged();
143  }
144
145  // content::NotificationObserver:
146  virtual void Observe(int type,
147                       const content::NotificationSource& source,
148                       const content::NotificationDetails& details) OVERRIDE {
149    // Accept in the infobar.
150    infobar_service_ = InfoBarService::FromWebContents(
151        browser_->tab_strip_model()->GetActiveWebContents());
152    InfoBarDelegate* infobar = infobar_service_->infobar_at(0);
153
154    ConfirmInfoBarDelegate* confirm_infobar =
155        infobar->AsConfirmInfoBarDelegate();
156    confirm_infobar->Accept();
157  }
158
159 private:
160  bool alerted_;
161  bool has_run_message_loop_;
162  Browser* browser_;
163  content::NotificationRegistrar registrar_;
164  InfoBarService* infobar_service_;
165};
166
167class TestAutofillExternalDelegate : public AutofillExternalDelegate {
168 public:
169  TestAutofillExternalDelegate(content::WebContents* web_contents,
170                               AutofillManager* autofill_manager,
171                               AutofillDriver* autofill_driver)
172      : AutofillExternalDelegate(web_contents, autofill_manager,
173                                 autofill_driver),
174        keyboard_listener_(NULL) {
175  }
176  virtual ~TestAutofillExternalDelegate() {}
177
178  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
179    AutofillExternalDelegate::OnPopupShown(listener);
180    keyboard_listener_ = listener;
181  }
182
183  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE {
184    keyboard_listener_ = NULL;
185    AutofillExternalDelegate::OnPopupHidden(listener);
186  }
187
188  content::KeyboardListener* keyboard_listener() {
189    return keyboard_listener_;
190  }
191
192 private:
193  // The popup that is currently registered as a keyboard listener, or NULL if
194  // there is none.
195  content::KeyboardListener* keyboard_listener_;
196
197  DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
198};
199
200class AutofillInteractiveTest : public InProcessBrowserTest {
201 protected:
202  AutofillInteractiveTest() {}
203
204  virtual void SetUpOnMainThread() OVERRIDE {
205    // Don't want Keychain coming up on Mac.
206    test::DisableSystemServices(browser()->profile());
207
208    // When testing the native UI, hook up a test external delegate, which
209    // allows us to forward keyboard events to the popup directly.
210    content::WebContents* web_contents =
211        browser()->tab_strip_model()->GetActiveWebContents();
212    AutofillDriverImpl* autofill_driver =
213        AutofillDriverImpl::FromWebContents(web_contents);
214    AutofillManager* autofill_manager = autofill_driver->autofill_manager();
215    scoped_ptr<AutofillExternalDelegate> external_delegate(
216        new TestAutofillExternalDelegate(web_contents, autofill_manager,
217                                         autofill_driver));
218    autofill_driver->SetAutofillExternalDelegate(external_delegate.Pass());
219    autofill_manager->SetTestDelegate(&test_delegate_);
220  }
221
222  virtual void CleanUpOnMainThread() OVERRIDE {
223    // Make sure to close any showing popups prior to tearing down the UI.
224    content::WebContents* web_contents =
225        browser()->tab_strip_model()->GetActiveWebContents();
226    AutofillManager* autofill_manager =
227        AutofillDriverImpl::FromWebContents(web_contents)->autofill_manager();
228    autofill_manager->delegate()->HideAutofillPopup();
229  }
230
231  PersonalDataManager* personal_data_manager() {
232    return PersonalDataManagerFactory::GetForProfile(browser()->profile());
233  }
234
235  void CreateTestProfile() {
236    AutofillProfile profile;
237    test::SetProfileInfo(
238        &profile, "Milton", "C.", "Waddams",
239        "red.swingline@initech.com", "Initech", "4120 Freidrich Lane",
240        "Basement", "Austin", "Texas", "78744", "US", "5125551234");
241
242    WindowedPersonalDataManagerObserver observer(browser());
243    personal_data_manager()->AddProfile(profile);
244
245    // AddProfile is asynchronous. Wait for it to finish before continuing the
246    // tests.
247    observer.Wait();
248  }
249
250  void ExpectFieldValue(const std::string& field_name,
251                        const std::string& expected_value) {
252    std::string value;
253    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
254        browser()->tab_strip_model()->GetActiveWebContents(),
255        "window.domAutomationController.send("
256        "    document.getElementById('" + field_name + "').value);",
257        &value));
258    EXPECT_EQ(expected_value, value);
259  }
260
261  RenderViewHost* render_view_host() {
262    return browser()->tab_strip_model()->GetActiveWebContents()->
263        GetRenderViewHost();
264  }
265
266  void FocusFirstNameField() {
267    LOG(WARNING) << "Clicking on the tab.";
268    content::SimulateMouseClick(
269        browser()->tab_strip_model()->GetActiveWebContents(),
270        0,
271        WebKit::WebMouseEvent::ButtonLeft);
272
273    LOG(WARNING) << "Focusing the first name field.";
274    bool result = false;
275    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
276        render_view_host(),
277        "if (document.readyState === 'complete')"
278        "  document.getElementById('firstname').focus();"
279        "else"
280        "  domAutomationController.send(false);",
281        &result));
282    ASSERT_TRUE(result);
283  }
284
285  void ExpectFilledTestForm() {
286    ExpectFieldValue("firstname", "Milton");
287    ExpectFieldValue("lastname", "Waddams");
288    ExpectFieldValue("address1", "4120 Freidrich Lane");
289    ExpectFieldValue("address2", "Basement");
290    ExpectFieldValue("city", "Austin");
291    ExpectFieldValue("state", "TX");
292    ExpectFieldValue("zip", "78744");
293    ExpectFieldValue("country", "US");
294    ExpectFieldValue("phone", "5125551234");
295  }
296
297  void SendKeyToPageAndWait(ui::KeyboardCode key) {
298    test_delegate_.Reset();
299    content::SimulateKeyPress(
300        browser()->tab_strip_model()->GetActiveWebContents(),
301        key, false, false, false, false);
302    test_delegate_.Wait();
303  }
304
305  void SendKeyToPopupAndWait(ui::KeyboardCode key) {
306    // TODO(isherman): Remove this condition once the WebKit popup UI code is
307    // removed.
308    if (!external_delegate()) {
309      // When testing the WebKit-based UI, route all keys to the page.
310      SendKeyToPageAndWait(key);
311      return;
312    }
313
314    // When testing the native UI, route popup-targeted key presses via the
315    // external delegate.
316    content::NativeWebKeyboardEvent event;
317    event.windowsKeyCode = key;
318    test_delegate_.Reset();
319    external_delegate()->keyboard_listener()->HandleKeyPressEvent(event);
320    test_delegate_.Wait();
321  }
322
323  TestAutofillExternalDelegate* external_delegate() {
324    content::WebContents* web_contents =
325        browser()->tab_strip_model()->GetActiveWebContents();
326    AutofillDriverImpl* autofill_driver =
327        AutofillDriverImpl::FromWebContents(web_contents);
328    return static_cast<TestAutofillExternalDelegate*>(
329        autofill_driver->autofill_external_delegate());
330  }
331
332  AutofillManagerTestDelegateImpl test_delegate_;
333};
334
335IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, DISABLED_AutofillSelectViaTab) {
336  CreateTestProfile();
337
338  // Load the test page.
339  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(browser(),
340      GURL(std::string(kDataURIPrefix) + kTestFormString)));
341
342  // Focus a fillable field.
343  FocusFirstNameField();
344
345  // Press the down arrow to initiate Autofill and wait for the popup to be
346  // shown.
347  SendKeyToPageAndWait(ui::VKEY_DOWN);
348
349  // Press the down arrow to select the suggestion and preview the autofilled
350  // form.
351  SendKeyToPopupAndWait(ui::VKEY_DOWN);
352
353  // Press tab to accept the autofill suggestions.
354  SendKeyToPopupAndWait(ui::VKEY_TAB);
355
356  // The form should be filled.
357  ExpectFilledTestForm();
358}
359
360}  // namespace autofill
361