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